Skip to main content
Call Eric:863-698-8266
CURRYCONTROLS.COMControls & Automation Knowledge Hub
ReferenceSCADADocumentationCybersecurityDesignProgramming

SQL Integration

Connecting SCADA and historian data to relational databases: why a utility does it, the methods, a schema that handles time series, the query patterns for totals and reports, the mistakes that flood a database or stall a server, and security.

10 min readUpdated Sep 5, 2026Published Sep 5, 2026By Eric Sullivan

The short answer

SQL Integration

SQL integration puts control system data where reports, regulatory submissions, maintenance systems, laboratory systems, and dashboards can reach it, and brings the occasional external value, such as a laboratory result or a work order, back. The methods are a historian query interface that answers time-series questions directly, SCADA logging that writes selected values and events to tables on a schedule or on change, a publish-subscribe path through a broker, or an export to a reporting database in the DMZ. The database schema stores tag, timestamp, value, and quality in a narrow table indexed by tag and time, with reference tables for tag metadata, and the writes are asynchronous and buffered so that an unavailable database never blocks the SCADA. The database that the office reads is a replica in the DMZ with read-only accounts, and no connection from the office reaches the control network.

Key points

  • The historian answers time-series questions; SQL answers reporting and integration questions. Use each for its job.
  • Write summaries and events to SQL, not every scan of every tag; the historian keeps the raw data.
  • A narrow table of tag, time, value, quality, indexed by tag and time, is the schema that survives growth.
  • Writes are asynchronous with store and forward; a database that is down must never stall the SCADA.
  • The office reads a replica in the DMZ through read-only accounts; nothing in the office connects inward.
  • Universal time in the database, local time in the report.

Why

  • Regulatory reports: monthly operating reports built from daily totals, maximums, minimums, and compliance measurements.
  • Maintenance: run hours and start counts to the maintenance management system, which schedules work from them, and work orders back to the SCADA for display.
  • Laboratory: sample results from the laboratory system alongside the online analyzers, for comparison and calibration.
  • Dashboards and management reporting: production, energy, chemical use, and cost, in tools the office already uses.
  • Billing and wholesale accounting: totalized flows at meters shared with other systems.
  • Asset and event records: alarm history, operator actions, and equipment events in a form that can be queried by anyone with a report tool.

Methods

MethodHow it worksBest forLimits
Historian query interfaceThe historian exposes a SQL-like interface or a connector that returns time-series data with aggregation and interpolationReports and analysis directly from the historian without copying dataLoad on the historian; query language specific to the product
SCADA logging groupsThe SCADA writes selected tags to database tables on a schedule, on change, or on an event triggerDaily totals, shift reports, event records, batch recordsConfiguration per group; the volume must be designed, not defaulted
Historian to reporting databaseA scheduled job aggregates historian data into a relational reporting databaseOffice reporting from a database in the DMZ with no load on the control systemLatency of the schedule; another database to maintain
Publish-subscribe through a brokerThe SCADA or edge device publishes values and events; a subscriber on the enterprise side writes them to a databaseModern architectures, cloud and enterprise integration, one-way data flowA broker and a subscriber to run; the design of topics and payloads
Direct database writes from a controllerA controller module writes to a databaseRarely; a specialized needCredentials in the controller, a database on the control network, no buffering; avoid

A schema that lasts

Time-series data in a relational database is stored one row per sample in a narrow table, with the tag identified by a key into a metadata table, the timestamp in universal time, the value, and the quality. A wide table with a column per tag looks convenient and breaks the first time a tag is added, renamed, or has a different sample rate. The narrow table is indexed on the tag key and the timestamp, partitioned by time on large systems, and its rows are inserted in batches. Aggregates that reports use repeatedly, such as daily totals, are computed once by a scheduled job into their own table rather than recomputed from raw rows on every report.

A narrow sample table with metadata and a precomputed daily aggregate
CREATE TABLE tag (
  tag_id      INT PRIMARY KEY,
  tag_name    VARCHAR(120) NOT NULL UNIQUE,
  description VARCHAR(255),
  units       VARCHAR(32),
  site        VARCHAR(64)
);

CREATE TABLE sample (
  tag_id   INT      NOT NULL REFERENCES tag(tag_id),
  ts_utc   DATETIME NOT NULL,
  value    FLOAT,
  quality  SMALLINT NOT NULL,   -- 0 bad, 1 uncertain, 2 good
  PRIMARY KEY (tag_id, ts_utc)
);

CREATE TABLE daily_total (
  tag_id     INT  NOT NULL REFERENCES tag(tag_id),
  day_local  DATE NOT NULL,
  total      FLOAT,
  min_value  FLOAT,
  max_value  FLOAT,
  good_pct   FLOAT,               -- share of samples with good quality
  PRIMARY KEY (tag_id, day_local)
);
A report query against the aggregate table, not the raw samples
-- Daily flow total for the plant effluent meter, last 31 days,
-- computed from a totalizer tag that the controller integrates every scan
SELECT d.day_local, d.total, d.good_pct
FROM daily_total d
JOIN tag t ON t.tag_id = d.tag_id
WHERE t.tag_name = 'FIT-401_TOTAL'
  AND d.day_local >= DATEADD(day, -31, CAST(GETDATE() AS DATE))
ORDER BY d.day_local;

The daily total in that example comes from a controller totalizer, which counts every scan, rather than from an integral of sampled flow, which misses whatever fell between samples. The job that fills the aggregate table reads the totalizer value at the day boundary in local time, converts it from universal time correctly across daylight saving changes, and records what fraction of the day the tag had good quality so that a report can flag a day that is incomplete.

Mistakes

  • Writing every tag every scan. A thousand tags at one second is 86 million rows a day; the database fills, the inserts fall behind, and the SCADA logging queue overflows. Log summaries and events; the historian keeps the raw data.
  • Synchronous writes. A logging path that waits for the database blocks the SCADA when the database is slow or down. Writes are queued, buffered to disk, and retried.
  • The database on the control network with office users connected to it. Every report tool in the office then has a path into the control zone. The office reads a replica in the DMZ.
  • Credentials in scripts and controllers. Service accounts with minimum rights, stored in the platform credential store, rotated.
  • Local time in the database. Stored data in universal time; conversion in the report.
  • Floating point money. Totals for billing computed with appropriate precision and rounding rules, agreed with the billing system.
  • No quality column. A report that cannot tell a zero from a bad sample produces wrong totals with confidence.

Security

RuleWhy
Data flows outward: control network to DMZ to officeA compromised office system cannot reach the control network through the database path
The office reads a replica in the DMZLoad and access are isolated from the database the SCADA writes
Read-only accounts for reporting; a write account only for the SCADA logging serviceA report tool cannot alter or delete control records
Inbound values, such as laboratory results, arrive through a controlled path and are validated before displayData from the office is untrusted until checked
Database patched, backed up, and monitored like the SCADA serversIt holds regulatory records
Encrypted connections and no default accountsThe database is a target

Frequently asked questions

Should the historian be replaced by a SQL database?
No. A historian is built for millions of samples a day with compression and fast time-range queries; a relational database is built for structured records and joins. Keep the raw time series in the historian and put summaries, events, and integration data in SQL. Some products combine both; the division of work still applies.
How do I get laboratory results into the SCADA displays?
The laboratory system writes results to a table in the DMZ database, or exports a file to a DMZ share; a job on the control side reads them through a one-way path, validates the tag, time, and range, and writes them to SCADA tags marked as laboratory values. Display them beside the online analyzer with their sample time; never let an imported value drive control.
The monthly report totals do not match the operator log.
Common causes: the report integrates sampled flow while the operator reads the totalizer; the day boundary is in universal time rather than local; bad-quality samples were treated as zero; a daylight saving change doubled or dropped an hour. Compute totals from the totalizer tag at local midnight, carry the quality, and reconcile the two methods once so the difference is understood.
Can the SCADA write to a database in the office?
It can, through the DMZ, one direction, to a database the office then reads. It should not connect directly to an office database server, and the office should not connect to anything on the control network. The DMZ database, or a broker in the DMZ, is the meeting point.

Direct contact

Have a controls question?

Reach Eric Sullivan directly about anything on this site, a controls or automation topic, or one of his personal projects.