Files
wwtools/histdb/02-syntax-limits.md
Joseph Doherty 32f26272ae Initial commit: Wonderware / System Platform tools and reference
Five tools under one repo, all docs organized per DOCS-GUIDE.md:

- aalogcli: .NET 4.8 / x86 CliFx CLI for reading System Platform binary
  logs (*.aaLGX) for LLM debugging, built on aaOpenSource/aaLog. Commands:
  last, tail, range, unread, fields. Stable JSON envelope under --llm-json.
  Build template under lib/build/ for rebuilding aaLogReader.dll.

- aot: ArchestrA Object Toolkit 2014 v4.0 reference material. Dev guide
  (Markdown converted from CHM), API reference for the ArchestrA.Toolkit
  namespace, and the Monitor / Watchdog VS sample solutions.

- graccesscli: .NET 4.8 / x86 CliFx CLI that automates Galaxy
  configuration via the ArchestrA GRAccess COM interop. Includes session
  daemon, IPC protocol, and llm-json envelope contract.

- grdb: SQL/DDL exploration of the Galaxy Repository database. DDL
  captures, reusable queries, hierarchy / contained-name <-> tag-name
  translation notes.

- histdb: LLM-oriented reference for AVEVA Historian retrieval. INSQL
  linked-server, extension tables, every wwXxx time-domain extension,
  every retrieval mode, alarm/event SQL recipes, REST API. Distilled
  from the 243-page Historian Retrieval Guide.

Root contains:
- CLAUDE.md: thin index pointing into each tool's README.
- DOCS-GUIDE.md: doctrine for organizing docs for LLM consumption.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 18:22:20 -04:00

7.5 KiB

SQL syntax — what works, what doesn't

The INSQL OLE DB provider supports a subset of T-SQL. Knowing the cliffs in advance saves a lot of "mysteriously empty rowset" debugging.

Quick capability matrix

Syntax element Four-part / view OPENQUERY
ORDER BY inside the body. Wrap and order outside.
GROUP BY
TagName IN (...)
TagName LIKE '...'
Date/time functions (DateAdd, GETDATE, …)
MIN, MAX, AVG, SUM, STDEV All MIN, MAX, AVG, SUM only — no STDEV
Sub-SELECT (one normal SQL Server table + one extension table) with restrictions
Sub-SELECT (two extension tables)
Variables (@foo) outside OPENQUERY inside

Time-domain extensions (the wwXxx columns)

The Historian exposes "virtual columns" on every extension table that double as input parameters. They control retrieval semantics:

wwCycleCount       wwEdgeDetection    wwFilter           wwInterpolationType
wwOption           wwQualityRule      wwResolution       wwRetrievalMode
wwStateCalc        wwTimeDeadband     wwTimeStampRule    wwTimeZone
wwValueDeadband    wwValueSelector    wwVersion

Reserved / deprecated: wwParameters, wwMaxStates (reserved), wwRowCount (deprecated — use wwCycleCount).

Set them in the WHERE:

SELECT DateTime, Value
FROM History
WHERE TagName = 'SysTimeSec'
  AND DateTime >= '2001-12-02 10:00:00'
  AND DateTime <= '2001-12-02 10:02:00'
  AND wwResolution    = 10
  AND wwRetrievalMode = 'cyclic';

Three rules that bite:

  1. The provider is stateless. Every extension parameter must be set in every query that needs it — there is no session memory.
  2. IN and OR cannot multiplex an extension. wwVersion IN ('original','latest') and wwRetrievalMode = 'Delta' OR wwVersion = 'latest' both fail.
  3. Values are case-insensitive even on a case-sensitive SQL Server collation.

Each individual extension is documented in 04-retrieval-options.md.

LIKE — only TagName and Value

LIKE only works on the TagName and Value columns (and Value LIKE only against a string tag).

SELECT TagName, Value
FROM History
WHERE TagName LIKE 'Sys%'
  AND DateTime > '1999-05-24 14:30:00'
  AND DateTime < '1999-05-24 14:32:00';

IN against AnalogTag / DiscreteTag / StringTag with LIKE

If you push a LIKE inside an IN-driven sub-SELECT against the typed tag tables, the query fails unless you select the vValue column. An INNER REMOTE JOIN is more efficient anyway:

-- Clunky (and only works if vValue is in the projection):
SELECT DateTime, TagName, vValue, Quality, QualityDetail
FROM History
WHERE TagName IN (SELECT TagName FROM StringTag WHERE TagName LIKE 'SysString')
  AND DateTime BETWEEN '2001-06-21 16:00:00.000' AND '2001-06-21 16:40:00.000'
  AND wwRetrievalMode = 'Delta';

-- Preferred:
SELECT h.TagName, DateTime, Value
FROM SnapshotTag st
INNER REMOTE JOIN INSQL.Runtime.dbo.History h
  ON st.TagName = h.TagName
WHERE EventTagName = 'SysStatusEvent'
  AND DateTime = '2001-12-20 0:00';

Joins

  • Inside OPENQUERY: not supported. Both implicit (FROM Tag t, Live v) and explicit (JOIN) joins fail. Always pull the join out:

    SELECT v.DateTime, v.TagName, v.Value, e.Unit
    FROM OPENQUERY(INSQL, '
      SELECT DateTime, TagName, Value FROM Live
      WHERE TagName LIKE "%Date%"
    ') v
    JOIN AnalogTag t      ON v.TagName = t.TagName
    JOIN EngineeringUnit e ON t.EUKey   = e.EUKey
    ORDER BY t.TagName;
    
  • Joining a SQL Server table against an extension table: use INNER REMOTE JOIN with the SQL Server table on the left and the extension table on the right:

    <SQLServerTable> INNER REMOTE JOIN <HistorianExtensionTable>
    

    This forces SQL Server to send the join predicate down to the OLE DB provider in one round trip rather than dragging the whole extension table back to the engine.

CONVERT on vValue inside OPENQUERY

CONVERT(...) against the variant vValue column is not supported inside the OPENQUERY body. Filter outside:

SELECT * FROM OPENQUERY(INSQL, '
  SELECT DateTime, Quality, OPCQuality, QualityDetail, Value, vValue, TagName
  FROM History
  WHERE TagName IN ("SysTimeMin", "SysPulse")
    AND DateTime >= "2001-12-30 04:00:00.000"
    AND DateTime <= "2001-12-30 09:00:00.000"
    AND wwRetrievalMode = "Delta"
') 
WHERE CONVERT(float, vValue) = 20.0;

Sub-SELECTs against a SQL Server table + extension table

Permitted but inefficient — SQL Server fans the whole sub-query out badly. Always rewrite as INNER REMOTE JOIN. Two-extension-table sub-SELECTs are not supported at all.

Optimizer dropping criteria

The SQL Server optimizer sometimes decides a WHERE clause is "redundant" and never sends it to INSQL, producing wrong-but-not-erroring results. Diagnose with the SSMS query plan; remediate by rewriting so the criteria flow through a more direct path, e.g. put the small driver table on the left of INNER REMOTE JOIN so its tagnames flow into the right side:

DECLARE @TagList TABLE (TagName nvarchar(256));
INSERT @TagList SELECT 'SysTimeSec' UNION SELECT 'SysPerfCPUTotal';

-- Wrong: TagName criteria gets optimized out
SELECT DateTime, h.vValue, h.TagName
FROM History h
INNER REMOTE JOIN @TagList l ON h.TagName = l.TagName
WHERE DateTime >= DATEADD(hour, -1, GETDATE()) AND DateTime < GETDATE()
  AND wwRetrievalMode = 'AVG' AND wwCycleCount = 1;

-- Right: driver table on the left, criteria reach the OLE DB provider
SELECT DateTime, h.vValue, h.TagName
FROM @TagList l
INNER REMOTE JOIN History h ON h.TagName = l.TagName
WHERE DateTime >= DATEADD(hour, -1, GETDATE()) AND DateTime < GETDATE()
  AND wwRetrievalMode = 'AVG' AND wwCycleCount = 1;

Variant columns + functions

If you wrap a variant column (vValue) with a function like ROUND(), SQL Server emits a syntax error that doesn't propagate back through OLE DB — you get silently wrong / empty rows. Cast first or filter on the typed Value column.

StartDateTime cannot drive query criteria

Many retrieval modes return a StartDateTime for the cycle. You can SELECT it, but you cannot use it in WHERE — the predicate is silently ignored. Use DateTime only.

Comparisons and NULL

A predicate like Value > 0 will error when the provider returns a NULL. Always pair it with AND Value IS NOT NULL.

WHERE clause anomalies

If the SQL Server optimizer decides your WHERE is "useless" (typically a LIKE '%' against TagName), it removes the clause entirely and you get the OLE DB error from 01-overview.md. Add another harmless predicate to keep the clause "needed":

SELECT DateTime, Value, QualityDetail
FROM History
WHERE TagName LIKE '%'
  AND wwRetrievalMode = 'delta';   -- forces the WHERE to survive optimization

Compatibility notes for the older datetime type

If your client uses the older SQL Server datetime type (instead of datetime2), expect rounding to ~3.3 ms vs. the 100 ns native FILETIME resolution.

What OPENQUERY and Microsoft Query do not talk

Microsoft Query (the legacy Excel/Access tool) cannot parse OPENQUERY statements. Use one of the other connection paths from Excel.

Next