WinCC SQL Server Database Tag Source VBScript and OPC Integration

David Krause14 min read
SiemensTutorial / How-toWinCC
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Overview

WinCC 7.x does not expose SQL Server, MS Access, or any ODBC-compliant database as a native tag channel the way it exposes S7-MPI/TCP or OPC DA. The runtime tag manager only accepts channels of type SIMATIC S7 Protocol Suite, OPC, Allen-Bradley, Modbus TCP, and a handful of manufacturer-specific drivers. A row in dbo.MachineState or a record in EnergyMeters.accdb cannot be dragged into the tag database and treated as a process value.

Two engineering patterns close this gap without abandoning WinCC's alarm engine, trend subsystem, or limit-value reporting:

  1. Database-to-Internal Tag Bridge: a cyclic VBScript opens an ADO connection, executes a SELECT, and writes the resulting column values into pre-declared WinCC internal tags. The internal tags then carry the standard alarm, trend, and min/max configuration. This is the method the source thread converges on.
  2. Database-to-OPC DA Tunnel: a third-party OPC server (KEPServerEX, MatrikonOPC, Softing, etc.) periodically polls the database and re-publishes each row as an OPC DA item. WinCC consumes the items through its OPC channel. This trades a license cost for cleaner tag management.

This reference covers both patterns with the VBScript emphasis that the original engineering problem demands: eight electricity meters writing 60-second rows into Access, and two Allen-Bradley machines writing 60-second rows into SQL Server, all needing SCADA-grade alarming and trending in WinCC.

Architecture and Tag Source Models

Three data flow variants are common in mixed-vendor plants:

Model Source WinCC Interface Alarm/Trend Support
Native PLC driver S7-300 backplane SIMATIC S7 Protocol Suite channel Full
External log file SQL Server / Access (60 s history) VBScript → Internal tags Full (via internal tag config)
External log file SQL Server / Access OPC DA tunneler Full (via OPC tag config)

The shared constraint: WinCC can only evaluate limits, generate alarms, and feed the trend archive on objects living in its own tag manager. Therefore any external data source must be lifted into a WinCC tag before it can participate in those subsystems. There is no direct database alarm in WinCC; the database is always a producer, not a consumer, of process values.

Prerequisites

  • WinCC 7.0 SP3 or later (SP4 recommended for stable HMIRuntime behavior under Windows 10/Server 2016). Project compatibility is forward to WinCC 7.5 within the v7.x line.
  • SQL Server 2008 R2 or later on the Allen-Bradley machine PCs (SQL Server Express is acceptable). Mixed authentication (Windows + SQL) must be enabled if the WinCC station is on a separate domain account.
  • MS Access 2010 or later (32-bit, matching the WinCC bitness) on the electricity meter PC. WinCC 7.x is 32-bit, so the Access Database Engine 2010 32-bit redistributable is required even on 64-bit hosts. Install from the Microsoft Access Database Engine distribution.
  • ODBC Data Source (32-bit) configured on the WinCC station pointing at each remote SQL Server and the Access file. Use the 32-bit odbcad32.exe from %windir%\SysWOW64\, never the 64-bit version.
  • Network reachability: TCP/1433 (or the configured instance port) to the SQL Server hosts, and SMB/RPC reachability to the Access share if the .accdb is opened across the network. For multi-user Access writes, prefer a UNC path to a shared folder with read-only attributes on the .accdb for the WinCC user.
  • WinCC scripting permissions: the user running the WinCC Runtime must have write access to the internal tag database. The HMIRuntime object is only available to authenticated WinCC operators in Runtime, not in WinCC Explorer.

Method 1 — VBScript Bridge to Internal Tags

Declaring Internal Tags

Create one internal tag per database column. Naming convention used in the source example uses a suffix for multi-row datasets, but for a single-row Latest table (typical for 60-second logging with no historical interest on the SCADA side), one tag per column is sufficient.

Tag Name Type Source Column (SQL Server LatestState)
SQL_P_NR Text tag, 16 chars ProductionNumber (NVARCHAR)
SQL_P_DLC Date/Time DateLastChange (DATETIME)
SQL_P_Label Text tag, 32 chars ProductLabel (NVARCHAR)
SQL_P_Ln_Nr Signed 32-bit LineNumber (INT)
EM_KWh_Total Float, 64-bit Access MeterReadings!KWh_Total
EM_KWh_Phase_A Float, 64-bit Access MeterReadings!KWh_PhA

Use WinCC Explorer → Tag Management → right-click → Add new tag. Do not select a channel; the tag must be in the Internal Tags group.

Configuring the Polling Trigger

VBScripts in WinCC run on triggers. Two options:

  1. Cyclic trigger on a scheduler action — appropriate when the source polls every 60 s. Set the trigger interval to 5–10 s for jitter tolerance; the script itself must short-circuit when the database row is unchanged to avoid pointless alarm retriggers.
  2. Tag trigger on a heartbeat internal tag toggled by another script — used when synchronization with another event is required.

Configure under Global ScriptProject Module → right-click → New Action… → set Trigger tab to the desired cycle.

Connection Strings

Database Connection String (ADO)
SQL Server (Windows Auth) Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=MachineState;Data Source=AB_PC_01\SQLEXPRESS
SQL Server (SQL Auth) Provider=SQLOLEDB.1;Password=P@ssw0rd;Persist Security Info=True;User ID=scada;Initial Catalog=MachineState;Data Source=AB_PC_01\SQLEXPRESS
MS Access 2010+ (.accdb) Provider=Microsoft.ACE.OLEDB.12.0;Data Source=\\MeterPC\MeterShare\EnergyMeters.accdb;Persist Security Info=False
MS Access 2003 (.mdb) Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\MeterData\EnergyMeters.mdb;Persist Security Info=False

Microsoft SQLOLEDB is the legacy provider still preferred for 32-bit VBScript hosts because the newer MSOLEDBSQL driver is distributed only as a 64-bit package in most current releases and is not registered on a 32-bit process by default. The 32-bit Microsoft Access Database Engine 2010 redistributable registers Microsoft.ACE.OLEDB.12.0 in the 32-bit hive. See the Microsoft ADO Programmer's Guide and the Microsoft ADO documentation for provider registration details.

VBScript Implementation

The reference script is a direct adaptation of the source thread's snippet, hardened with explicit Option Explicit, a constant connection string, parameterized SQL, and deterministic field indexing.

Option Explicit

Const CONN_STR_SQL   = "Provider=SQLOLEDB.1;Integrated Security=SSPI;" _
                     & "Initial Catalog=MachineState;Data Source=AB_PC_01\SQLEXPRESS"
Const CONN_STR_ACC   = "Provider=Microsoft.ACE.OLEDB.12.0;" _
                     & "Data Source=\\MeterPC\MeterShare\EnergyMeters.accdb;Persist Security Info=False"
Const SQL_SELECT     = "SELECT TOP 1 ProductionNumber, DateLastChange, " _
                     & "ProductLabel, LineNumber FROM dbo.LatestState ORDER BY DateLastChange DESC"

Sub ReadSqlToInternal()
    Dim conn, rst, fldIdx, tempvar
    Set conn = CreateObject("ADODB.Connection")
    Set rst  = CreateObject("ADODB.Recordset")

    On Error Resume Next
    conn.ConnectionTimeout = 5
    conn.CommandTimeout    = 5
    conn.Open CONN_STR_SQL
    If Err.Number <> 0 Then
        ShowSystemAlarm "DB Conn Error #" & Err.Number & " " & Err.Description
        Err.Clear
        Exit Sub
    End If

    Set rst = conn.Execute(SQL_SELECT)
    If Err.Number <> 0 Then
        ShowSystemAlarm "DB Query Error #" & Err.Number & " " & Err.Description
        Err.Clear
        conn.Close
        Exit Sub
    End If

    If Not (rst.BOF And rst.EOF) Then
        rst.MoveFirst
        ' Column 0: ProductionNumber (text)
        HMIRuntime.Tags("SQL_P_NR").Write CStr(rst.Fields(0).Value)
        ' Column 1: DateLastChange (datetime)
        HMIRuntime.Tags("SQL_P_DLC").Write CDate(rst.Fields(1).Value)
        ' Column 2: ProductLabel (text)
        HMIRuntime.Tags("SQL_P_Label").Write CStr(rst.Fields(2).Value)
        ' Column 3: LineNumber (signed 32-bit)
        HMIRuntime.Tags("SQL_P_Ln_Nr").Write CLng(rst.Fields(3).Value)
        rst.Close
    Else
        ShowSystemAlarm "DB row not available"
    End If

    conn.Close
    Set rst  = Nothing
    Set conn = Nothing
End Sub

For the Access path the only difference is the connection string and the SELECT (typically a single row from a LatestReadings table written by the Delphi polling service).

Field-Type Compatibility

ADO returns a Variant; the WinCC tag write requires a compatible VBScript subtype. Common coercion rules:

SQL/Access Type ADO Variant Subtype WinCC Tag Type Coercion
INT, BIGINT VT_I4 / VT_I8 Signed 32/64-bit CLng() / direct
FLOAT, REAL VT_R4 / VT_R8 Float 64-bit CDbl()
NVARCHAR VT_BSTR Text tag, n chars CStr()
DATETIME VT_DATE Date/Time CDate()
BIT VT_BOOL Binary tag CBool()
NULL VT_NULL Any Guard with IsNull() before write
Tag-length mismatch: writing a 40-character string into a 16-character text tag raises E_ABORT (0x80004004) and the tag value is rejected silently in older WinCC SPs. Always size text tags to the worst-case column width plus 10%.

Multi-Row Datasets

Where the source thread mentions sqltag_A_1 suffixes: declare N internal tags, then assign rst.Fields(n).Value inside a For i = 0 To N-1 loop. Useful when the database carries an energy-meter array and each meter maps to a discrete tag. For more than ~30 tags per cycle, switch to Method 2 (OPC) — the VBScript bridge becomes a noticeable Runtime load beyond that point.

Method 2 — OPC DA Database Server

When the WinCC station has tens of database-backed tags, the cleaner architecture is a dedicated OPC DA server that performs the polling and exposes the results as standard OPC items. WinCC consumes them through its OPC channel with no VBScript at all.

WinCC OPC Channel Configuration

  1. Open the tag management and add a new driver of type OPCOPC DA.
  2. In the system parameters, browse the local OPC Enum to confirm the third-party server is registered (e.g., Kepware.KEPServerEX.V6 or Matrikon.OPC.Database). Registration is via regsvr32 or the vendor's installer.
  3. On the vendor server side, define a channel pointing at the SQL Server or Access file, with a polling rate (typically 1000 ms) and an SQL query (or stored procedure) producing a result set.
  4. For each row.column combination, the vendor exposes an OPC item name (commonly Channel.Device.Tag). Add these items to the WinCC OPC channel using OPC Tag Selection.

Trade-Offs

Concern VBScript Bridge OPC DA Tunnel
License cost None Vendor license per server
Tag count ceiling ~30 per cycle Thousands per server
Failure mode visibility Native WinCC alarm OPC server diagnostics only unless bridged
Historical backfill Manual SQL queries Vendor historian or custom
Polling precision WinCC scheduler jitter Server-side timer, ms accuracy

Configuring Alarms, Limits, and Trends on Internal Tags

Once an internal tag is fed by the VBScript bridge, the standard WinCC subsystems apply:

Limit Values and Message Configuration

  1. Open the internal tag's Properties dialog → Limits tab.
  2. Define LO_LO, LO, HI, HI_HI thresholds appropriate to the source column. For energy meters, typical HI is the contractual maximum demand; HI_HI is the breaker rating.
  3. Under Messages, assign the tag to an alarm class (e.g., Alarm_High) and configure the message text. The alarm fires automatically when the VBScript writes a value outside the configured range.

Trend Configuration

  1. Create a new Trend Window in the graphics designer.
  2. Add the internal tag to the trend's Source list.
  3. Set the archive to a 1-minute or 60-second cycle matching the source write interval. WinCC's tag logging will produce a continuous line; gaps in the trend reveal polling failures or DB outages.
Archive configuration: Tag LoggingArchives → define a process value archive with the cycle matching the database write rate. Sub-15-second cycles require WinCC/Performance tuning; 60-second cycles are the typical default and match the source project exactly.

Polling Optimization and Change Detection

A naive 5-second poll of an external database that updates every 60 seconds generates 11 identical tag writes per data point. Although WinCC's alarm engine deduplicates on value, the trend archive and HMIRuntime write traffic still scale linearly. To reduce this:

  • Use a WHERE clause keyed on DateLastChange > @lastSeen, where @lastSeen is held in a WinCC internal tag. The query returns zero rows once the database is caught up, and the VBScript skips the Write block entirely.
  • Use SQL Server's TOP 1 with ORDER BY DateLastChange DESC to guarantee a single row even under network retries.
  • For Access, wrap the SELECT in SELECT TOP 1 ... ORDER BY DateLastChange DESC; Access's TOP syntax requires no FETCH clause.
  • Place a one-second Sleep via WScript.Sleep only if the WScript host is available — the WinCC CScript/VBScript runtime does not expose it. Use a fixed conn.ConnectionTimeout = 5 and rely on WinCC's scheduler cadence instead.

Security and Data Classification

SQL Server exposes sensitive operational data once the bridge is in place. Apply the classification and auditing guidance from the official Microsoft SQL Server Data Discovery and Classification documentation:

  • Tag columns containing production numbers, customer identifiers, or operator names as Confidential - GDPR or per your enterprise classification schema.
  • Enable SQL Server Audit on the SELECT events against the SCADA user. Forward the audit log to your SIEM.
  • Restrict the scada SQL login to db_datareader on the MachineState database only. Deny write permission; the SCADA system must never modify the source log.
  • For Access, set the share to Read Only for the SCADA user account. The Delphi writer keeps Modify rights.

Verification

  1. Connectivity test: from the WinCC station, open odbcad32.exe (32-bit) → System DSNTest Connection on each configured DSN. A success here eliminates 80% of deployment failures.
  2. ADO smoke test: in a VBScript action, log the Err.Number from the conn.Open call. A persistent -2147467259 indicates provider not registered (reinstall the Access Database Engine 32-bit). A persistent 0x80004005 points to permission or path.
  3. Tag value test: open the WinCC tag management in Runtime, right-click the internal tag, select Properties and confirm the value updates within one polling cycle.
  4. Alarm path: temporarily set a tight HI limit on a known-good tag (e.g., SQL_P_Ln_Nr) and force the database to write a value above it. Verify the alarm appears in the message list and is acknowledged normally.
  5. Trend path: open the configured trend window, run Runtime for 10 minutes, and confirm continuous data points at the expected 60-second spacing.
  6. Failure injection: stop the SQL Server service on the AB machine PC. Within one polling cycle, the VBScript should raise ShowSystemAlarm with the ADO error number. Resume the service and confirm tags repopulate on the next cycle.

Troubleshooting Matrix

Symptom Likely Root Cause Resolution
Error 0x80004005 on conn.Open Wrong provider or path; permission denied Confirm 32-bit ACE/SQLOLEDB registration; verify UNC path; check share/NTFS permissions
Error 0x80004004 (E_ABORT) on Tags().Write Text tag too short for value Resize text tag length
Error -2147467259 on Access ACE.OLEDB.12.0 not registered Install 32-bit Microsoft Access Database Engine 2010
Tags show 0 after first write Type mismatch — NULL or Empty passed to numeric tag Guard with IsNull() and CLng/CDbl
Tags update on Explorer but not in Runtime Script runs in Project Module not Global Action; trigger not active Move to Global Script → Actions; verify trigger in C-Script editor
Trend has gaps every ~30 minutes Connection timeout too short; SQL Server named pipes fallback Force TCP/IP in connection string; increase ConnectionTimeout to 5–10 s
Access file locked exclusively Delphi writer holds .accdb lock Configure Delphi with Shared mode; open the Access file from WinCC with Mode=Read
Alarm fires on every poll, not on value change Hysteresis not configured; identical value written each cycle triggers limit check Enable limit-value hysteresis (deadband) on the internal tag
Date/Time tag shows 1899-12-30 NULL in source column, CDate(NULL) returns 0 Substitute default with Nz()-equivalent or guard

Field-Proven Caveats

  • Provider bitness: WinCC 7.x is 32-bit. Even on a 64-bit OS, only the 32-bit ACE and SQLOLEDB providers are usable from the WinCC process. Installing the 64-bit Access Database Engine does not register the 32-bit GUID.
  • DCOM hardening: when the OPC tunnel (Method 2) is used across two computers, modern Windows defaults block anonymous DCOM. Configure the OPC enum and server with explicit Launch & Activation permissions for the WinCC user.
  • Trend and Alarm Licensing: WinCC counts each internal tag toward the PowerTag license regardless of whether the script populates it from a database or a PLC. Confirm the RT license size before adding a large set of bridged tags.
  • Time zone drift: the AB machine PCs may be on local time while the WinCC station is UTC. Either normalize at the database level (store UTC) or set the WinCC station's Regional Settings accordingly, otherwise the DateLastChange trends will look offset.
  • SQL Server Express instance name: the default is SQLEXPRESS, which means the connection string is AB_PC_01\SQLEXPRESS, not AB_PC_01. Browser service must be running for dynamic port resolution.

Frequently Asked Questions

Can WinCC read a SQL Server table directly without a script?

No. WinCC's tag database only accepts channels of type SIMATIC S7, OPC, Allen-Bradley, Modbus TCP, and a few vendor drivers. A VBScript + internal tag bridge or an OPC DA database server is required to expose database rows as process tags.

Which OLE DB provider should I use from a 32-bit VBScript host?

Use SQLOLEDB.1 for SQL Server and Microsoft.ACE.OLEDB.12.0 for Access 2010+. Install the 32-bit Microsoft Access Database Engine redistributable and verify the provider in the 32-bit odbcad32.exe before commissioning.

Why do my internal tags not update in Runtime even though the script runs in the debugger?

The script is likely placed in Project Module instead of Global Script → Actions. Project Module functions are called explicitly; only global actions driven by a trigger execute in Runtime. Move the routine, set a cyclic trigger, and confirm the action is enabled.

How do I trigger a WinCC alarm on a database value?

Write the value into an internal tag, then configure Limits (LO_LO, LO, HI, HI_HI) and a message class on that tag. The alarm engine will fire automatically on the next write that exceeds a limit, exactly as it would for a PLC-driven tag.

Is there a free OPC DA server that polls SQL Server?

No production-grade free OPC DA server with database polling is widely available. KEPServerEX, MatrikonOPC Database Server, and Softing OPC Tunnel are the established commercial options. For non-production use, open-source projects exist but lack vendor support and stability guarantees.

How can I avoid alarm spam when the SQL row does not change?

Add a hysteresis (deadband) to the limit configuration on the internal tag, or use a WHERE DateLastChange > @lastSeen filter so the VBScript only writes when the database has a fresh row. This eliminates redundant write traffic and prevents limit-edge flapping.

Back to blog