Tag Metadata
Overview
Tag metadata stores static attributes for each tag. You can keep sensor location, device status, installation information, external identifiers, and JSON document-based properties in metadata.
With the current feature set, you can use the following directly through TAG METADATA syntax.
- Metadata-only queries
- Metadata predicate based
UPDATEandDELETE JSONmetadata columns- JSON path queries and JSON path indexes
- Partial updates for JSON documents
You do not need to access any internal metadata table directly.
Define Metadata Columns
Define metadata columns in the METADATA (...) clause of CREATE TAGDATA TABLE.
CREATE TAGDATA TABLE sensors (
name VARCHAR(20) PRIMARY KEY,
time DATETIME BASETIME,
value DOUBLE
)
METADATA (
location VARCHAR(100),
status VARCHAR(20),
srcip IPV4
);Metadata is stored as one row per tag name.
Insert Metadata
Use INSERT INTO ... METADATA to insert metadata.
INSERT INTO sensors METADATA VALUES (
'TEMP_001',
'Building-A/F1',
'READY',
'192.168.0.11'
);You can also specify the column list.
INSERT INTO sensors METADATA (name, status, srcip, location)
VALUES ('TEMP_002', 'STOP', '192.168.0.12', 'Building-A/F2');Notes:
- In
VALUES (...), the order is alwaysNAMEfollowed by metadata columns in declaration order. - Unspecified metadata columns are stored as
NULL. - The row identity of tag metadata is always
NAME.
Query Metadata
Query Metadata Only
Use FROM TAG METADATA for metadata-only queries.
SELECT name, location, status, srcip
FROM sensors METADATA
ORDER BY name;This returns one row per tag name.
SELECT *
FROM sensors METADATA
ORDER BY name;SELECT * and table_alias.* return only NAME and user-defined metadata columns.
Query Data with Metadata Filters
Use normal FROM TAG when you want time-series rows filtered by metadata conditions.
SELECT name, status, time, value
FROM sensors
WHERE status = 'READY'
ORDER BY name, time;This query returns data rows, so the same metadata value is repeated for each data row of the tag.
Notes:
FROM TAG METADATAdoes not allow data columns such asTIMEorVALUE.FROM TAGis data query mode, andFROM TAG METADATAis metadata query mode.- Internal columns such as
_IDand_RIDare not available throughTAG METADATA.
Update Metadata
Use UPDATE TAG METADATA to update metadata.
UPDATE sensors METADATA
SET status = 'DONE',
srcip = '10.0.0.20'
WHERE name = 'TEMP_001';You can also update multiple tags with a metadata predicate.
UPDATE sensors METADATA
SET status = 'DONE'
WHERE status = 'READY';Notes:
- Only
NAMEand metadata columns can be updated. - Data columns such as
TIMEandVALUEcannot be updated throughUPDATE ... METADATA. - Internal columns cannot be updated.
Delete Metadata
Use DELETE FROM TAG METADATA to delete metadata.
DELETE FROM sensors METADATA
WHERE name = 'TEMP_002';You can also delete multiple tags with a metadata predicate.
DELETE FROM sensors METADATA
WHERE status = 'STOP';Notes:
- If any matched tag still has data rows, the whole statement fails.
- Metadata for tags that are still in use cannot be deleted.
JSON Metadata Columns
You can define a JSON metadata column.
CREATE TAGDATA TABLE ships (
name VARCHAR(20) PRIMARY KEY,
time DATETIME BASETIME,
value DOUBLE
)
METADATA (
status VARCHAR(20),
info JSON
);Example insert:
INSERT INTO ships METADATA VALUES (
'SHIP_001',
'READY',
'{"name":"alpha","ship":{"status":"READY"}}'
);Notes:
- Do not specify a length for a
JSONmetadata column. - Invalid JSON text raises an error.
- No automatic index is created for the raw JSON metadata column itself.
Query JSON Paths
Use the -> operator to query JSON metadata.
SELECT name,
info->'$.name',
info->'$.ship.status'
FROM ships METADATA
WHERE info->'$.ship.status' = 'READY'
ORDER BY name;The same path expression can be used in normal tag queries.
SELECT name, time, value
FROM ships
WHERE info->'$.ship.status' = 'READY'
ORDER BY name, time;Path Notation Rules
Use full JSONPath syntax for query and mutation paths.
- Simple key:
$.name - Nested object key:
$.ship.status - Use bracket notation if the key name contains
.or-
SELECT info->'$[''ship.owner'']'
FROM ships METADATA;
SELECT info->'$[''ship-owner'']'
FROM ships METADATA;JSON Path Indexes
Define Indexes When Creating the Table
If you frequently query specific JSON paths, define them at table creation time.
CREATE TAGDATA TABLE ships (
name VARCHAR(20) PRIMARY KEY,
time DATETIME BASETIME,
value DOUBLE
)
METADATA (
status VARCHAR(20),
info JSON INDEX('name', 'ship.status')
);Strings inside INDEX(...) are interpreted as follows.
'name'becomes$.name'ship.status'becomes$.ship.status- Use full JSONPath directly for special keys or complex paths
INFO JSON INDEX('$[''ship.owner'']')Add an Index After Table Creation
You can also add a JSON path index later.
CREATE INDEX idx_ship_owner
ON ships METADATA (info->'$.owner');Drop an Index
Drop the index by name.
DROP INDEX idx_ship_owner;For indexes created automatically by INFO JSON INDEX(...), use SHOW INDEX to confirm the generated index name.
SHOW INDEX idx_ship_owner;Notes on Index Usage
Current JSON path indexes work mainly for string comparisons.
SELECT name
FROM ships METADATA
WHERE info->'$.status' = 'READY';String literal comparison can use the index. Numeric literal comparison may still fall back to a full scan.
Examples:
info->'$.num' = '10': index can be usedinfo->'$.num' = 10: full scan may be used
Partial JSON Updates
You can update part of a JSON metadata document without rewriting the whole document.
JSON_SET
Stores a SQL scalar value as a JSON scalar.
UPDATE ships METADATA
SET info = JSON_SET(info, '$.ship.status', 'DONE')
WHERE name = 'SHIP_001';JSON_SET_JSON
Parses the input string as JSON and stores it as an object or array.
UPDATE ships METADATA
SET info = JSON_SET_JSON(info, '$.owner', '{"name":"machbase","team":"db"}')
WHERE name = 'SHIP_001';JSON_REMOVE
Removes a member or subtree.
UPDATE ships METADATA
SET info = JSON_REMOVE(info, '$.owner.team')
WHERE name = 'SHIP_001';Partial Update Rules
JSON_SET(..., path, NULL)stores JSONnull.JSON_SET_JSON(..., path, NULL)returns SQLNULL.- If the JSON document argument is
NULL, the function result is SQLNULL. - If the path is
NULLor an empty string, the function raises an error. JSON_REMOVEon a missing path is a no-op.JSON_REMOVE(..., '$')is not allowed.- Partial mutation is supported for object paths.
- Array element mutation such as
$.items[0]is not supported.
Full Example
CREATE TAGDATA TABLE ships (
name VARCHAR(20) PRIMARY KEY,
time DATETIME BASETIME,
value DOUBLE
)
METADATA (
status VARCHAR(20),
srcip IPV4,
info JSON INDEX('name', 'ship.status')
);
INSERT INTO ships METADATA VALUES (
'SHIP_001',
'READY',
'192.168.0.11',
'{"name":"alpha","ship":{"status":"READY"}}'
);
INSERT INTO ships VALUES ('SHIP_001', '2026-04-01 00:00:00', 10.5);
SELECT name, status, info
FROM ships METADATA;
SELECT name, time, value
FROM ships
WHERE info->'$.ship.status' = 'READY';
CREATE INDEX idx_ship_owner
ON ships METADATA (info->'$.owner');
UPDATE ships METADATA
SET info = JSON_SET(info, '$.ship.status', 'DONE')
WHERE name = 'SHIP_001';
DROP INDEX idx_ship_owner;Summary
- Use
FROM TAG METADATAfor metadata-only queries - Use
FROM TAGfor time-series data queries - Use
UPDATE/DELETE ... METADATAfor metadata changes - Use
INFO JSONfor JSON metadata - Use
INFO JSON INDEX(...)orCREATE INDEX ... ON TAG METADATA (...)for JSON path indexes - Use
JSON_SET,JSON_SET_JSON, andJSON_REMOVEfor partial JSON updates