Skip to content

Tag 메타데이터

개요

Tag 메타데이터는 태그의 정적 속성을 저장하는 영역입니다. 센서 위치, 장비 상태, 설치 정보, 외부 식별자, JSON 문서형 속성을 메타데이터에 둘 수 있습니다.

이번 기능으로 다음 작업을 TAG METADATA 문법으로 직접 처리할 수 있습니다.

  • 메타데이터 전용 조회
  • 메타데이터 조건 기반 UPDATE / DELETE
  • metadata row 마지막 변경 시각 조회
  • JSON 타입 메타데이터 컬럼 선언
  • JSON path 조회와 JSON path 인덱스
  • JSON 문서 일부만 수정하는 부분 갱신

사용자는 내부 저장 테이블을 직접 다룰 필요 없이 TAG METADATA 문법만 사용하면 됩니다.

메타데이터 컬럼 정의

메타데이터 컬럼은 CREATE TAG TABLEMETADATA (...) 절에서 정의합니다.

CREATE TAG TABLE sensors (
    name VARCHAR(20) PRIMARY KEY,
    time DATETIME BASETIME,
    value DOUBLE
)
METADATA (
    location VARCHAR(100),
    status VARCHAR(20),
    srcip IPV4
);

메타데이터 컬럼은 태그 이름별로 1행만 저장됩니다.

메타데이터 입력

메타데이터는 INSERT INTO ... METADATA 로 입력합니다.

INSERT INTO sensors METADATA VALUES (
    'TEMP_001',
    'Building-A/F1',
    'READY',
    '192.168.0.11'
);

컬럼 목록을 지정할 수도 있습니다.

INSERT INTO sensors METADATA (name, status, srcip, location)
VALUES ('TEMP_002', 'STOP', '192.168.0.12', 'Building-A/F2');

주의사항:

  • VALUES (...) 의 순서는 항상 NAME 다음에 메타데이터 정의 순서를 따릅니다.
  • 지정하지 않은 메타데이터 컬럼은 NULL 로 저장됩니다.
  • 메타데이터 행의 식별자는 항상 NAME 입니다.
  • 메타데이터 행이 생성되면 _LAST_UPDATE_TIME 이 서버 시각으로 자동 기록됩니다.

메타데이터 조회

메타데이터만 조회

메타데이터 전용 조회는 FROM TAG METADATA 를 사용합니다.

SELECT name, location, status, srcip
  FROM sensors METADATA
 ORDER BY name;

이 조회는 태그 이름당 1행을 반환합니다.

SELECT *
  FROM sensors METADATA
 ORDER BY name;

SELECT *table_alias.*NAME 과 메타데이터 컬럼만 반환합니다.

_LAST_UPDATE_TIME 같은 시스템 관리 컬럼은 SELECT * 결과에 표시되지 않습니다. 필요한 경우 컬럼명을 명시합니다.

마지막 변경 시각 조회

TAG metadata에는 각 metadata row의 마지막 변경 시각을 나타내는 시스템 관리 컬럼 _LAST_UPDATE_TIME 이 있습니다.

_LAST_UPDATE_TIME 은 tag data row의 마지막 입력 시각이 아니라, tag metadata row가 생성되거나 실제 metadata 값이 변경된 시각입니다.

조회 방법

_LAST_UPDATE_TIME 은 명시적으로 컬럼명을 지정해서 조회합니다.

SELECT name, _last_update_time
  FROM sensors METADATA;

다른 metadata 컬럼과 함께 조회하거나 조건에 사용할 수 있습니다.

SELECT name, location, status, _last_update_time
  FROM sensors METADATA
 WHERE name = 'TEMP_001';

SELECT *table_alias.* 결과에는 _LAST_UPDATE_TIME 이 표시되지 않습니다.

자동 기록 및 갱신 규칙

metadata row가 새로 생성되면 _LAST_UPDATE_TIME 이 자동으로 기록됩니다.

INSERT INTO sensors METADATA(name, location, status)
VALUES('TEMP_003', 'Building-A/F3', 'READY');

사용자 metadata 값이 실제로 변경되면 _LAST_UPDATE_TIME 이 갱신됩니다.

UPDATE sensors METADATA
   SET status = 'DONE'
 WHERE name = 'TEMP_003';

같은 값으로 update하거나 JSON missing path 제거처럼 저장 결과가 바뀌지 않는 update는 실제 변경으로 보지 않습니다. 이 경우 _LAST_UPDATE_TIME 은 유지됩니다.

UPDATE sensors METADATA
   SET status = 'DONE'
 WHERE name = 'TEMP_003';

JSON metadata 컬럼 info 가 있는 테이블에서는 존재하지 않는 경로 제거도 저장값을 바꾸지 않으면 no-op으로 처리됩니다.

UPDATE sensors METADATA
   SET info = JSON_REMOVE(info, '$.missing')
 WHERE name = 'TEMP_003';

직접 입력 및 수정 제한

_LAST_UPDATE_TIME 은 시스템 관리 컬럼이므로 사용자가 직접 값을 넣거나 수정할 수 없습니다.

다음 문장은 허용되지 않습니다.

INSERT INTO sensors METADATA(name, location, status, _last_update_time)
VALUES('TEMP_004', 'Building-A/F4', 'READY', now);
UPDATE sensors METADATA
   SET _last_update_time = now
 WHERE name = 'TEMP_003';

또한 TAG name 컬럼명, TAG metadata 컬럼명, ALTER TABLE ... METADATA ADD COLUMN 대상 컬럼명으로 _LAST_UPDATE_TIME 을 사용할 수 없습니다. ALTER TABLE ... METADATA DROP COLUMN 으로 _LAST_UPDATE_TIME 을 삭제할 수도 없습니다.

CREATE TAG TABLE invalid_sensor (
    _last_update_time VARCHAR(128) PRIMARY KEY,
    time              DATETIME BASETIME,
    value             DOUBLE
);
CREATE TAG TABLE invalid_sensor_meta (
    name  VARCHAR(128) PRIMARY KEY,
    time  DATETIME BASETIME,
    value DOUBLE
)
METADATA (
    _last_update_time DATETIME
);

_LAST_UPDATE_TIME2 처럼 prefix만 같은 이름은 별도 사용자 컬럼으로 사용할 수 있습니다.

시간 조건 조회 및 자동 인덱스

_LAST_UPDATE_TIME 에는 시간 조건 조회를 위한 인덱스가 자동으로 제공됩니다.

SELECT name, location, _last_update_time
  FROM sensors METADATA
 WHERE _last_update_time >= TO_DATE('2026-06-08 00:00:00')
 ORDER BY _last_update_time;

따라서 같은 컬럼에 사용자가 별도 인덱스를 중복 생성할 필요가 없습니다.

machloader / tagmetaimport 사용 시 주의사항

TAG metadata를 import할 때 입력 파일이나 form 파일에는 NAME 과 사용자 metadata 컬럼만 포함합니다. 내부 컬럼인 _ID 와 시스템 관리 컬럼 _LAST_UPDATE_TIME 은 입력 대상이 아닙니다.

metadata가 location, status 인 경우 입력 데이터는 다음 형태를 사용합니다.

TEMP_001,Building-A/F1,READY
TEMP_002,Building-A/F2,STOP

_LAST_UPDATE_TIME 은 import 시 서버가 자동으로 채웁니다.

일반 LOG, LOOKUP, VOLATILE 테이블에서 사용자가 _LAST_UPDATE_TIME 이라는 이름의 컬럼을 정의한 경우에는 일반 사용자 컬럼으로 동작합니다. 예약 동작은 TAG metadata 시스템 컬럼에만 적용됩니다.

데이터와 함께 조회

메타데이터 조건으로 시계열 데이터를 조회할 때는 일반 FROM TAG 를 사용합니다.

SELECT name, status, time, value
  FROM sensors
 WHERE status = 'READY'
 ORDER BY name, time;

이 조회는 데이터 row 기준으로 반환되므로, 같은 tag의 메타데이터 값은 각 데이터 row마다 반복됩니다.

주의사항:

  • FROM TAG METADATA 에서는 TIME, VALUE 같은 데이터 컬럼을 조회할 수 없습니다.
  • FROM TAG 는 데이터 조회 모드이고, FROM TAG METADATA 는 메타데이터 조회 모드입니다.
  • TAG METADATA 에서는 내부 컬럼 _ID, _RID 를 사용할 수 없습니다.

메타데이터 수정

메타데이터 수정은 UPDATE TAG METADATA 를 사용합니다.

UPDATE sensors METADATA
   SET status = 'DONE',
       srcip = '10.0.0.20'
 WHERE name = 'TEMP_001';

메타데이터 조건으로 여러 태그를 한 번에 수정할 수도 있습니다.

UPDATE sensors METADATA
   SET status = 'DONE'
 WHERE status = 'READY';

주의사항:

  • 수정 대상은 NAME 과 메타데이터 컬럼입니다.
  • TIME, VALUE 같은 데이터 컬럼은 UPDATE ... METADATA 에서 수정할 수 없습니다.
  • 내부 컬럼은 수정할 수 없습니다.
  • 실제 metadata 값이 바뀐 경우에만 _LAST_UPDATE_TIME 이 갱신됩니다.

메타데이터 삭제

메타데이터 삭제는 DELETE FROM TAG METADATA 를 사용합니다.

특정 태그의 메타데이터를 삭제하려면 WHERE 절에서 tag name 조건을 지정합니다.

DELETE FROM sensors METADATA
 WHERE name = 'TEMP_002';

메타데이터 조건으로 여러 태그를 한 번에 삭제할 수도 있습니다.

DELETE FROM sensors METADATA
 WHERE status = 'STOP';

WHERE 절을 생략하면 해당 TAG 테이블의 모든 메타데이터를 삭제합니다.

DELETE FROM sensors METADATA;

주의사항:

  • 삭제 대상 중 하나라도 실제 데이터 row를 가지고 있으면 문장 전체가 실패합니다.
  • 즉, 사용 중인 tag의 메타데이터는 삭제할 수 없습니다.
  • 전체 삭제 시에도 사용 중인 tag가 하나라도 있으면 일부만 삭제하지 않고 문장 전체가 실패합니다.

사용 중인 tag의 메타데이터를 삭제하려면 먼저 해당 tag의 데이터 row를 삭제한 뒤 메타데이터 삭제를 다시 수행합니다.

DELETE FROM sensors
 WHERE name = 'TEMP_001';

DELETE FROM sensors METADATA;

JSON 메타데이터 컬럼

메타데이터에 JSON 컬럼을 선언할 수 있습니다.

CREATE TAG TABLE ships (
    name VARCHAR(20) PRIMARY KEY,
    time DATETIME BASETIME,
    value DOUBLE
)
METADATA (
    status VARCHAR(20),
    info JSON
);

JSON 메타데이터 입력 예:

INSERT INTO ships METADATA VALUES (
    'SHIP_001',
    'READY',
    '{"name":"alpha","ship":{"status":"READY"}}'
);

주의사항:

  • JSON 메타데이터 컬럼은 길이를 지정하지 않습니다.
  • 유효하지 않은 JSON 문자열은 오류가 발생합니다.
  • raw JSON 컬럼 자체에는 자동 인덱스가 생성되지 않습니다.

JSON path 조회

JSON 메타데이터는 -> 연산자로 조회할 수 있습니다.

SELECT name,
       info->'$.name',
       info->'$.ship.status'
  FROM ships METADATA
 WHERE info->'$.ship.status' = 'READY'
 ORDER BY name;

데이터 조회에서도 같은 방식으로 사용할 수 있습니다.

SELECT name, time, value
  FROM ships
 WHERE info->'$.ship.status' = 'READY'
 ORDER BY name, time;

경로 표기 규칙

조회와 부분 갱신에서 사용하는 path는 full JSONPath를 사용합니다.

  • 일반 key: $.name
  • 중첩 key: $.ship.status
  • key 이름에 . 또는 - 가 포함되면 bracket 표기 사용
SELECT info->'$[''ship.owner'']'
  FROM ships METADATA;

SELECT info->'$[''ship-owner'']'
  FROM ships METADATA;

JSON path 인덱스

테이블 생성 시 함께 선언

자주 조회하는 JSON path는 메타데이터 정의 시 함께 인덱싱할 수 있습니다.

CREATE TAG TABLE ships (
    name VARCHAR(20) PRIMARY KEY,
    time DATETIME BASETIME,
    value DOUBLE
)
METADATA (
    status VARCHAR(20),
    info JSON INDEX('name', 'ship.status')
);

INDEX(...) 안의 문자열은 다음 규칙으로 해석합니다.

  • 'name'$.name
  • 'ship.status'$.ship.status
  • 특수 문자가 있는 key나 복잡한 경로는 full JSONPath를 직접 사용
INFO JSON INDEX('$[''ship.owner'']')

생성 후 인덱스 추가

테이블 생성 후에도 JSON path 인덱스를 추가할 수 있습니다.

CREATE INDEX idx_ship_owner
ON ships METADATA (info->'$.owner');

인덱스 삭제

인덱스 이름만으로 삭제합니다.

DROP INDEX idx_ship_owner;

생성 시 INFO JSON INDEX(...) 로 자동 생성된 인덱스 이름은 SHOW INDEX 로 확인하는 것이 가장 안전합니다.

SHOW INDEX idx_ship_owner;

인덱스 사용 시 주의사항

현재 JSON path 인덱스는 문자열 비교 중심으로 동작합니다.

SELECT name
  FROM ships METADATA
 WHERE info->'$.status' = 'READY';

문자열 literal 비교는 인덱스를 사용할 수 있습니다. 반면 숫자 literal 비교는 full scan으로 처리될 수 있습니다.

예:

  • info->'$.num' = '10' : 인덱스 사용 가능
  • info->'$.num' = 10 : full scan 가능

JSON 부분 갱신

JSON 메타데이터는 문서 전체를 다시 쓰지 않고 일부만 갱신할 수 있습니다.

JSON_SET

SQL scalar 값을 JSON scalar로 저장합니다.

UPDATE ships METADATA
   SET info = JSON_SET(info, '$.ship.status', 'DONE')
 WHERE name = 'SHIP_001';

JSON_SET_JSON

입력 문자열을 JSON으로 해석해 object 또는 array를 저장합니다.

UPDATE ships METADATA
   SET info = JSON_SET_JSON(info, '$.owner', '{"name":"machbase","team":"db"}')
 WHERE name = 'SHIP_001';

JSON_REMOVE

특정 멤버 또는 하위 경로를 삭제합니다.

UPDATE ships METADATA
   SET info = JSON_REMOVE(info, '$.owner.team')
 WHERE name = 'SHIP_001';

부분 갱신 규칙

  • JSON_SET(..., path, NULL) 은 JSON null 을 저장합니다.
  • JSON_SET_JSON(..., path, NULL) 의 결과는 SQL NULL 입니다.
  • JSON 문서 인자가 NULL 이면 함수 결과는 SQL NULL 입니다.
  • path 가 NULL 이거나 빈 문자열이면 오류가 발생합니다.
  • 존재하지 않는 경로에 대한 JSON_REMOVE 는 오류가 아니라 no-op 입니다.
  • JSON_REMOVE(..., '$') 는 허용되지 않습니다.
  • 부분 갱신은 object 경로 중심으로 지원합니다.
  • array element 경로 갱신 예: $.items[0] 는 지원하지 않습니다.

전체 예제

CREATE TAG 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;

요약

  • 메타데이터 전용 조회는 FROM TAG METADATA
  • 데이터 조회는 FROM TAG
  • 메타데이터 수정/삭제는 UPDATE/DELETE ... METADATA
  • JSON 메타데이터는 INFO JSON
  • _LAST_UPDATE_TIME 은 metadata row의 마지막 변경 시각이며 명시적으로 조회할 수 있습니다
  • JSON path 인덱스는 INFO JSON INDEX(...) 또는 CREATE INDEX ... ON TAG METADATA (...)
  • JSON 부분 갱신은 JSON_SET, JSON_SET_JSON, JSON_REMOVE
  • _LAST_UPDATE_TIME 은 서버가 자동 관리하며 standard와 cluster 환경에서 동일하게 동작합니다
최근 업데이트