32f26272ae
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>
186 lines
7.5 KiB
Markdown
186 lines
7.5 KiB
Markdown
# 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:
|
|
|
|
```text
|
|
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`:
|
|
|
|
```sql
|
|
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`](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).
|
|
|
|
```sql
|
|
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:
|
|
|
|
```sql
|
|
-- 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:
|
|
|
|
```sql
|
|
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:
|
|
|
|
```text
|
|
<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:
|
|
|
|
```sql
|
|
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:
|
|
|
|
```sql
|
|
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`](01-overview.md). Add another harmless predicate to keep the clause "needed":
|
|
|
|
```sql
|
|
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
|
|
|
|
- [`03-retrieval-modes.md`](03-retrieval-modes.md) — every `wwRetrievalMode` value.
|
|
- [`04-retrieval-options.md`](04-retrieval-options.md) — every other `wwXxx` parameter.
|