본문으로 건너뛰기

EQL — 이벤트 쿼리 언어 도움말

목차


개요

주 사용 위치 좌측 메뉴 Automation > EQL 쿼리(/query/index) 에서 직접 실행하며, 고급 EQL 알람 · 트리거·스테이트먼트 · 플로우 의 조건식에서도 동일 문법을 사용합니다.

EQL (Event Query Language) 은 플랜트펄스 플랫폼이 실시간 스트리밍 데이터를 SQL 문법처럼 질의·집계·패턴 매칭할 수 있도록 만든 자체 쿼리 언어입니다. 일반 SQL 이 정적 테이블을 대상으로 동작한다면, EQL 은 계속해서 들어오는 이벤트 스트림 위에서 동작하며 매 순간 결과를 emit 합니다.

운영자는 EQL 한 줄로 다음을 구현하실 수 있습니다.

  • 실시간 모니터링 — 태그 값이 임계 초과한 순간 즉시 결과 emit
  • 자산 단위 집계 — 매 5분 평균/최대/최소 자동 계산
  • 패턴 매칭 — "A 이벤트 후 30초 안에 B 이벤트가 안 오면 알람"
  • 시계열 조인 — 두 태그의 시간 정렬 비교

EQL 은 SQL 과 거의 동일한 문법을 사용하지만 윈도우(Window) 개념이 추가되어 "최근 N초 / N건" 같은 시간/길이 범위를 명시합니다.


어디에서 EQL 을 사용하나

플랫폼 내 EQL 사용 위치는 다음과 같습니다.

EQL 쿼리 · 트리거 · 스테이트먼트는 좌측 메뉴 Automation 섹션에 나란히 있습니다. (CEP 라는 이름의 메뉴는 없습니다 — 예전 문서가 이 셋을 CEP 하위 화면으로 적었습니다.)

메뉴 · 화면내부 URL용도
Automation > EQL 쿼리문서/query/index운영자가 즉석에서 EQL 을 실행해 결과를 확인 (탐색·디버깅)
Automation > 트리거문서/trigger/indexEQL 매칭 결과를 MQ 토픽 또는 스토리지로 자동 발행
Automation > 스테이트먼트문서/statement/index자산 단위로 등록된 룰 (상태/집계/이벤트/명령)
알람 → EQL 알람EQL 룰로 임의 조건 알람 발행
플로우 → 스크립트flow_script_filter / flow_script_transform / flow_switch 의 EQL 표현식 모드
태그 양식 — 인공지능 탭예측·이상감지 룰의 입력 조건

EQL 은 읽기 전용 입니다 — 데이터를 변경하지 않습니다. 매칭 결과로 알람을 발행하거나 플로우 액션 노드를 호출하려면 CEP → 트리거 또는 EQL 알람 으로 결과를 연결해 주세요.


기본 문법

EQL 의 기본 구조는 SQL 과 거의 동일합니다.

SELECT <컬럼 또는 함수>
FROM <스트림>.<윈도우>
WHERE <조건>
GROUP BY <필드>
HAVING <집계 조건>
ORDER BY <필드> [ASC|DESC]

가장 단순한 EQL:

SELECT * FROM Point.win:time(1 sec)

최근 1초 동안 들어온 모든 태그 포인트를 1초마다 emit.

와일드카드와 컬럼 선택

구문의미
SELECT *모든 컬럼 포함
SELECT tag_id, value특정 컬럼만
SELECT value AS v, tag_id AS id별칭(alias) 부여
SELECT value * 9 / 5 + 32 AS fahrenheit산술식 결과
SELECT CASE WHEN value > 80 THEN 'HIGH' ELSE 'OK' END AS level조건식

주석

-- 한 줄 주석
/* 여러 줄
주석 */
SELECT * FROM Point.win:time(1 sec)

이벤트 스트림 (14종 + 내부용 3종)

EQL 의 FROM 절에 올 수 있는 스트림(이벤트 소스)은 다음과 같습니다. 모든 스트림은 플랫폼이 자동으로 발생시키며, 별도 등록 없이 즉시 사용 가능합니다.

여기 없는 이름을 FROM 에 쓰면 룰이 배포되지 않습니다. 등록된 이벤트 타입만 스트림이 될 수 있습니다 (plantpulse.core.engine.eql.EventTypes).

태그 도메인 (3종)

스트림발생 시점주요 필드
Point태그 값이 수신될 때마다tag_id, value, quality, ts, site_id, asset_id, unit, java_type
PointMap동일하지만 tag('<id>') 함수와 함께 사용해 다중 태그를 키-값으로 조회(PointMap 전용 헬퍼)
Alarm태그 알람이 발생할 때tag_id, alarm_band, value, threshold, priority, band_message

자산 도메인 (6종)

스트림발생 시점주요 필드
AssetData자산 단위 시계열 데이터 (집계 결과)asset_id, values(key-value), ts
AssetEvent자산 이벤트 발생 (시작·정지·정비 등)asset_id, event_type, details, ts
AssetAlarm자산 알람 발생asset_id, alarm_band, priority, message
AssetCommand자산에 명령이 발행될 때asset_id, command, args
AssetAggregation1분/5분/1시간 자산 집계 결과asset_id, window, agg_type, values
AssetContext자산 컨텍스트(메타) 변경asset_id, context_key, before, after
AssetHealthStatus · AssetConnectionStatus 는 스트림이 아닙니다

예전 문서에 이 둘이 자산 도메인 스트림으로 실려 있었지만 등록된 이벤트 타입이 아닙니다. FROM AssetHealthStatus... 라고 쓰면 룰이 배포되지 않습니다.

헬스·연결 상태 평가는 스트림이 아니라 주기 실행 잡으로 돌아갑니다 (AssetHealthStatusDeployer / AssetConnectionStatusDeployer). 그 결과는 화면과 API 로 보고, EQL 에서 상태 변화를 잡고 싶다면 원천인 AssetEvent · AssetAlarm 이나 Point 의 미수신을 not 패턴 으로 감지하세요.

생산 도메인 (3종)

스트림발생 시점주요 필드
Calendar시프트 시작/종료/변경shift_id, calendar_id, event, start_time, end_time
WorkOrder워크오더 라이프사이클 전이order_id, status, prev_status, asset_id, product_id
OEEOEE 평가 결과 갱신order_id, asset_id, oee, availability, performance, quality

시스템 도메인 (2종)

스트림발생 시점주요 필드
Log시스템 진단 메시지module, level, code, summary, ts
OSPerformance서버 OS 자원 지표 (주기)CPU·메모리·디스크 등
Status 스트림은 없습니다

Status 라는 이름의 이벤트 타입은 등록되어 있지 않습니다. OPC·엣지의 연결 상태 변화를 EQL 로 잡으려 했다면 그 스트림으로는 안 됩니다 — Point 미수신을 not 패턴 으로 감지하는 방식을 쓰세요.

내부용 스트림 (3종)

등록은 되어 있지만 룰 작성용은 아닙니다. 진단·헬스체크에 쓰입니다.

스트림용도
DateTime주기적으로 발생하는 시각 이벤트 (시간 기준 룰의 트리거)
Ping엔진 생존 확인
Test룰 검증용

모든 스트림은 사용 전 반드시 윈도우와 결합해야 합니다 (Point.win:time(1 sec) 처럼). 윈도우 없이 FROM Point 만 쓰면 결과가 즉시 사라져 보이지 않습니다.


윈도우

윈도우는 "스트림 위에서 잘라낸 시간 또는 길이의 구간"입니다. EQL 은 두 종류의 윈도우를 지원합니다.

시간 윈도우 (Time Window)

문법의미
Point.win:time(1 sec)최근 1초 동안 들어온 모든 이벤트 (sliding)
Point.win:time(10 sec)최근 10초
Point.win:time(5 min)최근 5분
Point.win:time(1 hour)최근 1시간

지원되는 시간 단위: sec(초) · min(분) · hour(시간) · day(일).

길이 윈도우 (Length Window)

문법의미
Point.win:length(100)최근 100건의 이벤트
Point.win:length(1000)최근 1,000건

배치 윈도우 (Batch Window)

win:time_batch / win:length_batch — 윈도우가 가득 차거나 시간이 만료된 순간에만 한 번 emit. 사용량 자원 절감.

-- 매 1분마다 한 번씩 그 1분간 평균을 emit
SELECT avg(value) FROM Point.win:time_batch(1 min) WHERE tag_id = 'MOTOR-001.TEMP'

윈도우 선택 가이드

상황추천 윈도우
실시간 모니터링 (값 자체 보기)win:time(1 sec)
단기 평균/최대 (5분 평균 등)win:time(5 min)
시간 단위 집계win:time_batch(1 hour)
최근 N건 비교win:length(N)
변화량 추적win:length(2) + prev() 함수

WHERE 조건

SQL 과 동일한 비교 연산자를 사용합니다.

연산자예시의미
= != <>tag_id = 'MOTOR-001'같음/다름
< <= > >=value > 80비교
BETWEENvalue BETWEEN 70 AND 90범위
INtag_id IN ('A','B','C')목록 포함
LIKEtag_id LIKE 'MOTOR-%'패턴 매칭 (% 와일드카드)
IS NULL / IS NOT NULLquality IS NOT NULLnull 여부
AND OR NOTvalue > 80 AND quality = 'GOOD'논리 결합

정규식

SELECT * FROM Point.win:time(1 sec)
WHERE tag_id REGEXP '.*\\.TEMP$'

모든 끝이 .TEMP 인 태그만 매칭. 백슬래시는 두 번 (\\.) 입력.

동적 사이트/자산 필터

SELECT * FROM Point.win:time(1 sec)
WHERE site_id = 'SITE-A'
AND asset_id LIKE 'LINE-1.%'
AND value > 75

집계 함수

윈도우 내의 이벤트들을 집계합니다.

기본 집계

함수의미예시
count(*)이벤트 건수SELECT count(*) FROM Point.win:time(10 sec)
sum(field)합계sum(value)
avg(field)평균avg(value)
min(field)최솟값min(value)
max(field)최댓값max(value)
median(field)중앙값median(value)
stddev(field)표준편차stddev(value)
variance(field)분산variance(value)
first(field)윈도우 처음 값first(value)
last(field)윈도우 마지막 값last(value)

DISTINCT 집계

SELECT count(distinct tag_id) AS unique_tags
FROM Point.win:time(1 min)

CASE 와 결합

SELECT
count(CASE WHEN value > 80 THEN 1 END) AS high_count,
count(CASE WHEN value <= 80 THEN 1 END) AS ok_count
FROM Point.win:time(1 min)
WHERE tag_id = 'MOTOR-001.TEMP'

시계열 함수

함수의미예시
prev(N, field)N 단계 이전 값value - prev(1, value) (직전 값과 차이)
prevwindow(field)윈도우 안 모든 이전 값 배열prevwindow(value)
rate(field)단위 시간당 변화율rate(value)

그룹화·정렬

GROUP BY

-- 자산별 최근 1분 평균 온도
SELECT asset_id, avg(value) AS avg_temp
FROM Point.win:time(1 min)
WHERE tag_id LIKE '%.TEMP'
GROUP BY asset_id

HAVING — 집계 후 필터

-- 평균 > 80 인 자산만
SELECT asset_id, avg(value) AS avg_temp
FROM Point.win:time(1 min)
WHERE tag_id LIKE '%.TEMP'
GROUP BY asset_id
HAVING avg(value) > 80

ORDER BY · LIMIT

-- 가장 뜨거운 5개 자산
SELECT asset_id, max(value) AS peak
FROM Point.win:time(5 min)
WHERE tag_id LIKE '%.TEMP'
GROUP BY asset_id
ORDER BY peak DESC
LIMIT 5

LIMIT 은 결과 emit 시점의 가장 상위 N개만 포함합니다.


JOIN — 스트림 결합

두 개 이상의 스트림을 시간 정렬해 조인할 수 있습니다.

-- 같은 자산에서 같은 시간대의 온도와 압력을 한 행으로
SELECT p1.value AS temp, p2.value AS pressure, p1.asset_id
FROM Point.win:time(1 sec) AS p1,
Point.win:time(1 sec) AS p2
WHERE p1.tag_id LIKE '%.TEMP'
AND p2.tag_id LIKE '%.PRESSURE'
AND p1.asset_id = p2.asset_id

자산 이벤트 + 워크오더 조인

SELECT e.asset_id, e.event_type, w.order_id, w.status
FROM AssetEvent.win:time(5 min) AS e,
WorkOrder.win:time(5 min) AS w
WHERE e.asset_id = w.asset_id
AND e.event_type = 'STARTUP'
AND w.status = 'START'

두 스트림의 시간 윈도우가 겹치는 동안만 조인 결과가 발생합니다. 시간 단위가 다른 데이터를 비교할 때는 win:time_batch 로 비슷한 주기에 맞춰 주세요.


패턴 매칭

EQL 의 강력한 기능 — "이벤트 A 이후 N초 안에 B 가 (안) 일어남" 같은 시퀀스를 정의할 수 있습니다.

every — 매번 매칭

SELECT * FROM pattern [
every a = AssetEvent(event_type = 'STARTUP')
-> b = AssetEvent(event_type = 'SHUTDOWN', asset_id = a.asset_id)
where timer:within(60 sec)
]

시작 이벤트 → 60초 안에 정지 이벤트의 쌍을 매번 매칭. 짧은 가동 후 즉시 정지하는 비정상 패턴 탐지.

not — N 시간 동안 안 일어남

SELECT * FROM pattern [
every a = AssetEvent(event_type = 'STARTUP')
-> ( timer:interval(30 min)
and not AssetData(asset_id = a.asset_id) )
]

시작 후 30분 동안 데이터가 들어오지 않으면 매칭 (응답 없는 자산 탐지).

and / or — 동시 또는 둘 중 하나

SELECT * FROM pattern [
( every AssetAlarm(priority = 'ERROR') )
and
( every WorkOrder(status = 'START') )
]

패턴 매칭의 핵심 키워드

키워드의미
every <e>e 가 발생할 때마다 매칭
e1 -> e2e1 다음에 e2 가 발생 (순서 매칭)
e1 and e2두 이벤트가 모두 발생 (순서 무관)
e1 or e2둘 중 하나 발생
not ee 가 발생하지 않음
timer:within(<duration>)지정 시간 내에만 매칭
timer:interval(<duration>)지정 시간 만큼 대기

시간·상태 함수

시간 함수

함수의미
current_timestamp현재 시각 (ms)
current_date()오늘 날짜 (yyyy-MM-dd)
timestamp(field)이벤트의 timestamp 추출
hour_of_day(ts)0~23 시
day_of_week(ts)1(일)~7(토)
minute_of_hour(ts)0~59
dayofmonth(ts)1~31
-- 야간 (22시~6시) 발생 알람만
SELECT * FROM Alarm.win:time(1 hour)
WHERE hour_of_day(ts) >= 22 OR hour_of_day(ts) < 6

상태 함수

함수의미
tag('<tag_id>')그 태그의 가장 최근 값 (PointMap 컨텍스트)
prev_status(asset_id)자산의 직전 상태
health(asset_id)자산 헬스 상태 (OK/WARN/ERROR/UNKNOWN)
is_in_shift(asset_id)자산이 시프트 내부에 있는지

산술·문자열 함수

함수의미
abs(x) / round(x, n) / floor(x) / ceil(x)수학
pow(x, y)x의 y 제곱
sqrt(x)제곱근
length(s)문자열 길이
upper(s) / lower(s)대소 변환
substring(s, start, len)부분 문자열
concat(a, b, ...)연결

변수와 컨텍스트

변수 — 동적 임계값

-- 변수 정의 (운영자가 화면에서 조정 가능)
create variable double max_temp = 80;

-- 변수 사용
SELECT * FROM Point.win:time(1 sec)
WHERE tag_id LIKE '%.TEMP' AND value > max_temp

변수 값은 운영 중 변경할 수 있으며 모든 EQL 룰이 즉시 새 값을 사용합니다.

컨텍스트 — 시간/조건 기반 활성화

플랫폼에는 자주 쓰는 컨텍스트가 미리 정의되어 있어 그대로 가져다 쓰실 수 있습니다.

컨텍스트활성 조건
EVERY_1_MINUTES매 1분
EVERY_5_MINUTES매 5분
EVERY_10_MINUTES매 10분
EVERY_30_MINUTES매 30분
EVERY_1_HOURS매 1시간
EVERY_3_HOURS매 3시간
EVERY_6_HOURS매 6시간
EVERY_12_HOURS매 12시간
DAY_WORK_TIME평일 09:00~18:00
-- 5분마다 한 번씩 평균 온도 emit (그 사이에는 결과가 안 옴)
context EVERY_5_MINUTES
SELECT avg(value) AS avg_temp
FROM Point.win:time(5 min)
WHERE tag_id = 'MOTOR-001.TEMP'
-- 주간 근무시간에만 평가
context DAY_WORK_TIME
SELECT * FROM AssetAlarm.win:time(1 sec)
WHERE priority = 'ERROR'

실전 예제 12 선

예제 1 — 실시간 한 태그 모니터링

SELECT value, ts, quality
FROM Point.win:time(1 sec)
WHERE tag_id = 'MOTOR-001.SPEED'

예제 2 — 임계 초과 즉시 감지

SELECT tag_id, value, ts
FROM Point.win:time(1 sec)
WHERE tag_id LIKE '%.TEMP' AND value > 90

예제 3 — 자산별 5분 평균 (5분마다 emit)

context EVERY_5_MINUTES
SELECT asset_id, avg(value) AS avg_temp, max(value) AS peak
FROM Point.win:time(5 min)
WHERE tag_id LIKE '%.TEMP'
GROUP BY asset_id

예제 4 — 변화량 (직전 값 대비)

SELECT tag_id, value, value - prev(1, value) AS delta
FROM Point.win:length(2)
WHERE tag_id = 'TANK-001.LEVEL'

예제 5 — 야간 시프트의 ERROR 알람만

SELECT tag_id, priority, band_message, ts
FROM Alarm.win:time(1 sec)
WHERE priority = 'ERROR'
AND hour_of_day(ts) >= 22 OR hour_of_day(ts) < 6

예제 6 — 자산 헬스 전이 (OK → WARN)

SELECT asset_id, status, prev_status, ts
FROM AssetHealthStatus.win:time(1 sec)
WHERE prev_status = 'OK' AND status = 'WARN'

예제 7 — 워크오더 OEE 저조

SELECT order_id, asset_id, oee, availability, performance, quality
FROM OEE.win:time(1 sec)
WHERE oee < 0.6

예제 8 — 30 분 응답 없는 자산 감지

SELECT * FROM pattern [
every a = AssetData()
-> ( timer:interval(30 min)
and not AssetData(asset_id = a.asset_id) )
]

예제 9 — 시작 후 곧바로 정지 (불안정 가동)

SELECT * FROM pattern [
every a = AssetEvent(event_type = 'STARTUP')
-> b = AssetEvent(event_type = 'SHUTDOWN', asset_id = a.asset_id)
where timer:within(60 sec)
]

예제 10 — 다중 태그 동시 비교 (PointMap)

SELECT
tag('MOTOR-001.TEMP') AS temp,
tag('MOTOR-001.PRESSURE') AS pressure,
tag('MOTOR-001.VIBRATION') AS vibration
FROM PointMap.win:time(1 sec)

예제 11 — 라인 단위 시간당 누적 생산량

context EVERY_1_HOURS
SELECT asset_id, sum(value) AS hourly_count
FROM AssetAggregation.win:time(1 hour)
WHERE agg_type = 'PRODUCED' AND asset_id LIKE 'LINE-%'
GROUP BY asset_id

예제 12 — TOP 10 알람 발생 자산

context EVERY_10_MINUTES
SELECT asset_id, count(*) AS alarm_count
FROM AssetAlarm.win:time(10 min)
GROUP BY asset_id
ORDER BY alarm_count DESC
LIMIT 10

성능 가이드

1. 윈도우 크기를 의도적으로 작게

-- ❌ 나쁜 예 — 24시간을 메모리에 유지
SELECT avg(value) FROM Point.win:time(24 hour)

-- ✅ 좋은 예 — 1시간만 메모리에 유지 + context 로 emit 주기 분리
context EVERY_1_HOURS
SELECT avg(value) FROM Point.win:time(1 hour)

2. WHERE 절을 트리거 가까이

태그 ID·자산 ID·사이트 ID 같은 정적 필터는 WHERE 절 가장 앞에 두세요. 후속 GROUP BY/HAVING 보다 먼저 평가됩니다.

3. LIKE '%...%' 양면 와일드카드는 비용 큼

-- ❌ 나쁜 예
WHERE tag_id LIKE '%TEMP%'

-- ✅ 좋은 예
WHERE tag_id LIKE '%.TEMP' -- 접미사 매칭만

4. 패턴 매칭은 짧은 윈도우와 함께

pattern [ ... timer:within(<수 분>) ] 의 시간 한정자를 빠뜨리지 마세요. 무한대 패턴은 메모리를 계속 점유합니다.

5. 결과 emit 빈도 조정

context EVERY_N_MINUTES 컨텍스트로 EQL 자체 평가는 1초 단위로 하되, emit 만 N분마다 하도록 분리하면 다운스트림 부하가 줄어듭니다.


자주 묻는 질문

Q. EQL 쿼리를 입력했는데 결과 화면에 아무 것도 안 나옵니다. A. 다음을 확인해 주세요. ① FROM Point 만 쓰지 않았는지 — 윈도우 필요 (Point.win:time(1 sec)). ② WHERE 조건이 너무 좁아 매칭이 안 되는 것은 아닌지. ③ 우상단 ▶ 시작 버튼을 눌렀는지. ④ 결과 패널이 ⏸ 중지 상태가 아닌지.

Q. SQL 과 무엇이 다른가요? A. 가장 큰 차이는 윈도우 개념입니다. EQL 은 무한 스트림 위에서 동작하므로 "어느 범위" 를 명시해야 합니다. 그 외 pattern [ ... ] 시퀀스 매칭과 context 시간 컨텍스트는 SQL 에 없는 기능입니다.

Q. win:time(1 sec)win:time_batch(1 sec) 의 차이는? A. win:time(1 sec)sliding — 매 이벤트가 들어올 때마다 결과 갱신. win:time_batch(1 sec)tumbling — 1초 단위로 묶어 1초가 만료되는 순간에만 한 번 emit. 정확히 1초마다 결과가 필요하면 batch 를 쓰세요.

Q. pattern [ ... ] 와 일반 SELECT ... WHERE ... 의 차이는? A. SELECT 는 한 이벤트(또는 윈도우)를 보고 평가하지만, pattern이벤트 시퀀스 (A 다음 B, A 없이 B 등)를 평가합니다. 시간 순서·인과관계가 중요하면 pattern 을 쓰세요.

Q. 등록한 변수가 즉시 반영되나요? A. 네. create variable 로 정의한 변수의 값을 화면에서 변경하면 이미 배포된 모든 EQL 룰이 다음 평가부터 새 값을 사용합니다.

Q. EQL 매칭 결과를 알람으로 보내려면? A. CEP → 트리거 에 EQL 을 등록한 뒤 "MQ 스트리밍" 또는 "스토리지 저장" 옵션을 켜시거나, 알람 → EQL 알람 에 EQL 을 등록하시면 매칭 시 알람이 자동 발행됩니다.

Q. 자산 단위 룰을 모든 자산에 일괄 적용할 수 있나요? A. CEP → 스테이트먼트 에서 자산 단위 EQL 룰을 정의하고, 모델/온톨로지에서 그 룰을 자산 카테고리에 매핑하면 같은 카테고리의 모든 자산에 자동 배포됩니다. 자산이 추가될 때마다 자동으로 적용됩니다.

Q. EQL 한 줄에서 두 스트림을 동시에 다룰 수 있나요? A. 네. 위 JOIN 섹션의 예제처럼 두 스트림을 FROM 절에 콤마로 나열하고 WHERE 로 조인 조건을 작성하면 됩니다.

Q. 결과가 너무 많이 나옵니다. A. LIMIT N 으로 상위 N개만 받으시거나, context EVERY_N_MINUTES 로 emit 주기를 늘리시거나, win:time_batch 를 사용해 배치 윈도우로 묶어 주세요. WHERE 절을 더 엄격하게 잡는 것도 효과적입니다.

Q. EQL 작성을 어디서 연습하나요? A. CEP → 쿼리 화면이 EQL 실습 도구입니다. 한 줄을 입력하고 ▶ 실행하면 즉시 결과를 받아볼 수 있습니다. 위 실전 예제 12 선 부터 복사해 보세요.


관련 화면