Historizing Dataset, Document, and Array Tags in Ignition 8.1.4

Jason IP9 min read
Other ManufacturerSCADA ConfigurationTechnical Reference
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

Historizing Dataset, Document, and Array Tags in Ignition 8.1.4

The Inductive Automation Ignition 8.1.4 Tag Historian does not natively store historical values for tags whose data type is Dataset, Document, or top-level Array. When history is enabled on a tag of these types, no rows are inserted into the sqlth_data_X_X history partition tables, and the On Change sample mode records NULL for the value column. The limitation is rooted in the column definitions of the historian's partition schema, not in tag configuration. This reference covers the cause, the verification procedure, and the field-proven workarounds used to capture these types in an external database or in serialized string history.

1. Overview of the Ignition Tag Historian

The Tag Historian writes timestamped tag values into a relational database (MySQL, MSSQL, PostgreSQL, or Oracle) through a fixed partition schema. The Ignition 8.1 User Manual documents the schema in the sqlth_data_X_X Ignition Database Table Reference. The principal tables are:

Table Purpose
sqlth_partitions Partition metadata (id, name, start/end time, status flag)
sqlth_data_X_X Pair of value storage tables per partition (X = sequence)
sqlth_drv Driver-level event log
sqlth_te Tag-event audit log
sqlth_tags / tag id linkage Master list of historized tags

Each sqlth_data_X_X table contains a fixed set of typed columns. The value columns for each supported JDBC type are pre-allocated:

Column JDBC Type Maps to Ignition Type
intvalue INTEGER / INT4 Boolean, Byte, Short, Integer, Long*
floatvalue FLOAT / DOUBLE Float, Double, Long*
stringvalue VARCHAR / TEXT String, DateTime (ISO-8601)
dataintegrity INTEGER OPC quality code

*Long values are written to either intvalue or floatvalue depending on driver and range.

There is no BLOB, JSON, or VARBINARY column in the sqlth_data_X_X schema. The Tag Historian's storage contract is column-typed, not opaque. Any value that does not coerce into one of the typed columns above is written as NULL by the history writer subsystem.

2. Why Datasets, Documents, and Arrays Fail to Historize

A Dataset in Ignition is a tabular in-memory object (rows × columns of typed values), produced by SQL queries, calculations, and component bindings. Its binary representation does not map to a single primitive JDBC type. The Tag Historian's sqlth_data_X_X schema has no column to receive it, and the history subsystem explicitly rejects the value (writing NULL) instead of coercing.

A Document in Ignition is a structured object (a property bag with typed fields). Like Dataset, it is not a primitive type and is rejected at the history write boundary in the same way.

An Array exhibits a different behavior: the historian partially supports it by splitting the array into one new internal tag per subscript, each with its own tag_id and its own row stream in sqlth_data_X_X. The parent array tag itself stores no history rows, but each arrayTag[0], arrayTag[1], ... gets its own history record. The split is automatic and cannot be disabled from the Designer.

3. Supported Data Types for Tag History

Ignition Type History Support Target Column Notes
Boolean Yes intvalue 0 / 1
Integer (Int1 / Int2 / Int4) Yes intvalue
Long (Int8) Yes intvalue / floatvalue Driver dependent
Float / Double Yes floatvalue
String Yes stringvalue
DateTime Yes stringvalue ISO-8601 serialization
Array (top-level) Partial Per-subscript rows Split into one tag per index
Dataset No NULL on write No column match
Document No NULL on write No column match

This behavior has been documented and is consistent across the 8.0.x, 8.1.x, and 8.2.x release lines. See the Array and Dataset Tags video at Inductive University for the underlying data structures, and the Ignition 8.1 User Manual for the historian schema.

4. Verifying the Limitation in Your Project

  1. Open the Tag Browser and locate the offending tag.
  2. Confirm the Data Type field shows Dataset, Document, or Array.
  3. Right-click the tag and select Tag History. Confirm History Enabled is checked and the tag has a valid tag_id in sqlth_tags.
  4. Open your database client and run:
    SELECT tagid, time, intvalue, floatvalue, stringvalue, dataintegrity
    FROM sqlth_data_1_1
    WHERE tagid = <your_tag_id>
    ORDER BY time DESC
    LIMIT 50;
  5. Expected result for Dataset / Document: every value column is NULL; only time, tagid, and dataintegrity are populated.
  6. Expected result for Array: the parent tag returns no rows; query the auto-generated subscript tag IDs to see history.
If the On Change sample mode is selected on a Dataset or Document tag, the history table will still receive a row, but the intvalue, floatvalue, and stringvalue columns are all written as NULL. The write is silently dropped at the value layer.

5. Workarounds for Datasets

Because Dataset cannot be stored in sqlth_data_X_X, use one of the following patterns. All three bypass the in-built historian's column contract while preserving time-series semantics.

5.1 Pattern A: Serialize to a String Memory Tag

Bind a String memory tag to the Dataset via a Tag Expression or property-change script that calls system.dataset.toJSON(). Set the destination tag's data type to String and enable history on that string tag. The stringvalue column accepts the JSON serialization and the row is fully historized.

# Property-change script on the original Dataset tag
from com.inductiveautomation.ignition.common import TypeTags

if event.propertyName == "value":
    payload = system.dataset.toJSON(event.newValue)
    system.tag.writeBlocking(
        ["[default]History/DatasetAsJSON"],
        [payload]
    )

Reconstruct on read with system.dataset.fromJSON(). The string tag is queryable, replays the JSON, and uses the standard historian for retention and partitioning.

5.2 Pattern B: Transaction Group with DB Pen

Configure a Transaction Group with the Dataset as the source. The DB Pen block expands the Dataset columns into individual destination columns or into a single JSON column in a user-defined table. This bypasses the Tag Historian entirely and writes to a custom table you control. Configure Trigger as Tag Value Changed on the parent Dataset tag.

5.3 Pattern C: Gateway Timer Script with system.db

A Gateway Timer Script running every N seconds reads the Dataset, converts it to rows, and writes via system.db.runPrepUpdate(). This gives full control over schema, indexing, and partitioning:

ds = system.tag.readBlocking(["[default]Plant/RecipeTable"])[0].value
headers = ["Recipe", "Setpoint", "Tolerance"]
tstamp = system.date.format(system.date.now(), "yyyy-MM-dd HH:mm:ss")
for row in system.dataset.toPyDataSet(ds):
    system.db.runPrepUpdate(
        "INSERT INTO recipe_history (ts, name, sp, tol) VALUES (?, ?, ?, ?)",
        [tstamp, row[0], row[1], row[2]],
        "MySQL"
    )

6. Workarounds for Documents

Documents are property bags. The same three patterns apply:

  • Serialize via system.util.jsonEncode(document) to a String tag for historian storage.
  • Transaction Group with explicit field mapping (each Document key becomes a destination column).
  • Custom DB write script iterating over document.props.

When reconstructing, system.util.jsonDecode() returns a usable Python dict, and the Document class can be rebuilt from it if needed.

7. Workarounds for Arrays

The historian splits arrays into subscript tags automatically. To get useful history:

  1. Verify the array tag is a standard array, not a Dataset (use Tag Diagnostics in the Designer to confirm).
  2. Enable history on the array tag. Each arrayTag[N] becomes its own history record automatically. The new internal tag IDs are visible in the Tag Browser under the array tag.
  3. Reconstruct the historical array at a point in time with a query that joins the subscript tag IDs:
    SELECT a.time, a.floatvalue AS ph, b.floatvalue AS temp, c.floatvalue AS sp
    FROM sqlth_data_1_1 a
    JOIN sqlth_data_1_1 b ON a.time = b.time
    JOIN sqlth_data_1_1 c ON a.time = c.time
    WHERE a.tagid = <subscript0_id>
      AND b.tagid = <subscript1_id>
      AND c.tagid = <subscript2_id>
    ORDER BY a.time DESC
    LIMIT 1000;

If the array contains complex values, apply the JSON-serialization pattern from section 5.1 to each subscript.

8. Tag History Configuration Reference

Setting Recommended Value Reason
History Enabled true Required for any pattern to write rows
Historical Deadband 0 (for JSON / String payload) Numeric comparison does not apply to opaque payloads
Min Time Between Samples 0 Avoid dropping writes during burst updates
Max Time Between Samples 0 (disabled) or interval Use interval only if deadband alone would miss writes
Sample Mode On Change Triggers on every assignment
Tag Group Default or custom Controls storage partitioning and retention

History is configured at Gateway → Config → Tags → [provider] → [tag] → History tab. Settings are persisted in the taghistory Gateway table; the actual data lands in sqlth_data_X_X.

9. Diagnostics & Troubleshooting Matrix

Symptom Root Cause Action
Tag shows recent value in Tag Browser; history table is empty for that tag id Data type not supported in sqlth_data_X_X Switch to JSON string tag, Transaction Group, or custom DB write
History rows present but all value columns are NULL History write succeeded but no matching column exists Confirm type with Tag Diagnostics; do not rely on row count alone
Child tags appear under the original array tag in the browser Array split working as designed Query the auto-created subscript tag IDs
Memory tag with On Change shows "value: null" in DB Type mismatch — value rejected at write boundary Verify Data Type matches the actual value class
Reconstructed JSON fails parsing on read Database collation truncated trailing characters Use NVARCHAR / TEXT with sufficient length; encode before write
Transaction Group row missing after Dataset change Trigger set to polling instead of On Change Set Trigger = Tag Value Changed and bind to parent tag

10. Architecture Notes and Design Intent

The Tag Historian's design is intentionally narrow: a fixed set of typed columns optimized for high-throughput numeric and string writes at a fixed cadence. BLOB and VARBINARY storage were avoided to keep indexing, compression, partitioning, and replay simple. Datasets and Documents are designed as in-memory transport objects (produced by bindings, queries, and component calculations), not persistent storage primitives.

When you historize them, you serialize the object and write outside the historian's column contract. The string-value column is the documented escape hatch for opaque payloads, and Transaction Groups or custom Gateway scripts give full control over schema, indexing, and retention.

Confirm behavior against the release notes for the exact Ignition version installed. The 8.x line keeps the same sqlth_data_X_X schema, but new tag types or sample modes have been added in minor releases. Always test on a development Gateway before promoting history schema changes to production.

FAQ

Can I enable history on a Dataset tag in Ignition 8.1.4?

No. The sqlth_data_X_X schema has no column that accepts a Dataset's encoded value. The historian writes NULL for the value columns. Use a JSON-serialized String tag, a Transaction Group, or a custom DB write script instead.

Why are my array tag's child tags getting history but the parent isn't?

This is the documented split behavior. The Tag Historian treats an array as a collection of independent tags and creates one internal tag per subscript. The parent array tag has no storage representation; only the subscripts do.

What is the simplest workaround for historizing a Dataset?

Bind a String memory tag to the Dataset via a property-change script that calls system.dataset.toJSON(). Enable history on the String tag. Reconstruct the dataset on read with system.dataset.fromJSON(). This uses the standard historian for retention and partitioning.

Does this limitation exist in Ignition 8.2 or later?

The column schema of sqlth_data_X_X is unchanged across the 8.x line as of the relevant release. Datasets and Documents are not directly historized in 8.2; the workarounds described in this article apply unchanged. Confirm the current behavior against the Ignition User Manual release notes for your installed version.

Can Transaction Groups replace the Tag Historian for Datasets?

Yes. Configure the Dataset as the source, use DB Pens to map columns or a single JSON column, and schedule the group to write on change. The data lands in a user-defined table, not sqlth_data_X_X, so indexing and query patterns are your responsibility.

Back to blog