๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ“ฆ Cloud/๐Ÿ—‚๏ธ KQL

[KQL] ํŒŒ์ดํ”„ ํ•˜๋‚˜๋กœ ํ˜๋ ค๋ณด๋‚ด๋Š” ์ฟผ๋ฆฌ.. KQL

by ์‚๋šค์˜ค๋ฆฌ 2026. 5. 27.

KQL์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

KQL(Kusto Query Language)์€ Azure Data Explorer(ADX)์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•œ ์ „์šฉ ์ฟผ๋ฆฌ ์–ธ์–ด์ด๋‹ค.
Micorsoft๊ฐ€ ๋งŒ๋“ค์—ˆ๊ณ , ๋Œ€๊ทœ๋ชจ ๋กœ๊ทธ ๋ฐ์ดํ„ฐ์™€ ์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ๋ฅผ ๋น ๋ฅด๊ฒŒ ๊ฒ€์ƒ‰·์ง‘๊ณ„ํ•˜๋Š”๋ฐ ์ตœ์ ํ™”๋˜์–ด ์žˆ๋‹ค.

๐Ÿ’ก ๋น„์œ 
SQL์ด MSSQL์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊บผ๋‚ด๋Š” ์–ธ์–ด๋ผ๋ฉด, KQL์€ ADX์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊บผ๋‚ด๋Š” ์–ธ์–ด์ด๋‹ค.
๋‘˜ ๋‹ค "๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” ์–ธ์–ด"๋ผ๋Š” ์ ์€ ๊ฐ™์ง€๋งŒ, ๋ฌธ๋ฒ• ๊ตฌ์กฐ๊ฐ€ ์™„์ „ํžˆ ๋‹ค๋ฅด๋‹ค.

SQL๊ณผ KQL์˜ ๊ตฌ์กฐ์  ์ฐจ์ด

SQL์€ "๋‚ด๊ฐ€ ์›ํ•˜๋Š” ๊ฒƒ์„ ์„ ์–ธ"ํ•˜๋Š” ๊ตฌ์กฐ์ด๋‹ค.
SELECT, FROM, WHERE์„ ํ•œ๊บผ๋ฒˆ์— ์ ์–ด์„œ ์ œ์ถœํ•˜๋Š” ์ฃผ๋ฌธ์„œ ๋ฐฉ์‹์ด๋‹ค.
๋ฐ˜๋ฉด KQL์€ "๋ฐ์ดํ„ฐ๋ฅผ ํŒŒ์ดํ”„๋ผ์ธ์œผ๋กœ ํ˜๋ ค๋ณด๋‚ด๋Š”" ๊ตฌ์กฐ์ด๋‹ค.
ํŒŒ์ดํ”„( | )๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ ๋‹จ๊ณ„์”ฉ ๊ฐ€๊ณตํ•˜๋ฉด์„œ ์•„๋ž˜๋กœ ํ˜๋ ค๋ณด๋‚ธ๋‹ค.

-- SQL
SELECT name, age
FROM users
WHERE age > 20
ORDER BY age


-- KQL
users
| where age > 20
| project name, age
| sort by age

 

๐Ÿ’ก ๋น„์œ 
SQL์€ ์ฃผ๋ฌธ์„œ๋ฅผ ํ•œ ์žฅ ์ž‘์„ฑํ•ด์„œ ํ•œ๊บผ๋ฒˆ์— ๋‚ด๋ฏธ๋Š” ๋А๋‚Œ์ด๊ณ , KQL์€ ๊ณต์žฅ ์ปจ๋ฒ ์ด์–ด ๋ฒจํŠธ์ฒ˜๋Ÿผ ๋ฐ์ดํ„ฐ๋ฅผ ์œ„์—์„œ ์˜ฌ๋ ค๋†“๊ณ  ํ•œ ๋‹จ๊ณ„์”ฉ ๊ฐ€๊ณตํ•˜๋ฉด์„œ ์•„๋ž˜๋กœ ํ˜๋ ค๋ณด๋‚ด๋Š” ๋А๋‚Œ์ด๋‹ค.
๊ตฌ๋ถ„ SQL (MSSQL) KQL (ADX)
์ฝ๋Š” ์ˆœ์„œ SELECT → FROM → WHERE (์‹คํ–‰ ์ˆœ์„œ์™€ ๋‹ค๋ฆ„) ์œ„์—์„œ ์•„๋ž˜๋กœ ์ˆœ์„œ๋Œ€๋กœ (์ง๊ด€์ )
๊ตฌ๋ถ„์ž ์—†์Œ (์ ˆ ํ‚ค์›Œ๋“œ๋กœ ๊ตฌ๋ถ„) ํŒŒ์ดํ”„ | ๋กœ ๋‹จ๊ณ„ ๊ตฌ๋ถ„
์ปฌ๋Ÿผ ์„ ํƒ SELECT project
ํ•„ํ„ฐ WHERE where
์ •๋ ฌ ORDER BY osrt by
์ฃผ์š” ์šฉ๋„ ๊ด€๊ณ„ํ˜• DB ์กฐํšŒ/์กฐ์ž‘ (CRUD) ๋Œ€๊ทœ๋ชจ ๋กœ๊ทธ / ์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ ๋ถ„์„

์™œ ADX์—์„œ SQL ๋Œ€์‹  KQL์„ ์“ธ๊นŒ?

ADX๊ฐ€ ๋‹ค๋ฃจ๋Š” ๋ฐ์ดํ„ฐ๋Š” ๋Œ€๋ถ€๋ถ„ ๋กœ๊ทธ ๋ฐ์ดํ„ฐ์ด๋‹ค.
๋กœ๊ทธ๋Š” ์–‘์ด ์–ด๋งˆ์–ด๋งˆํ•˜๊ณ , ์‹œ๊ฐ„ ์ˆœ์„œ๋Œ€๋กœ ์Œ“์ด๋ฉฐ, ๋Œ€๋ถ€๋ถ„ ์ฝ๊ธฐ ์ „์šฉ(INSERT๋งŒ ์žˆ๊ณ  UPDATE/DELETE๊ฐ€ ๊ฑฐ์˜ ์—†์Œ)์ด๋‹ค.
KQL์€ ์ด๋Ÿฐ ๋กœ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ๋น ๋ฅด๊ฒŒ ๊ฒ€์ƒ‰ํ•˜๊ณ  ์ง‘๊ณ„ํ•˜๋Š” ๋ฐ ์ตœ์ ํ™”๋˜์–ด ์žˆ๋‹ค.

๊ด€์  SQL (MSSQL) KQL (ADX)
์„ค๊ณ„ ๋ชฉ์  ์ •ํ˜• ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ (CRUD ๋ชจ๋‘) ๋Œ€๊ทœ๋ชจ ๋กœ๊ทธ / ์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ ์ฝ๊ธฐ ํŠนํ™”
๋ฐ์ดํ„ฐ ์ €์žฅ ํ–‰(Row) ๊ธฐ๋ฐ˜ ์ €์žฅ ์—ด(Column) ๊ธฐ๋ฐ˜ ์ €์žฅ → ์ง‘๊ณ„์— ์œ ๋ฆฌ
์ˆ˜์–ต ๊ฑด ์ง‘๊ณ„ ๋А๋ ค์งˆ ์ˆ˜ ์žˆ์Œ ์ˆ˜์ดˆ ๋‚ด ๊ฐ€๋Šฅ
UPDATE / DELETE โœ… ์ž์œ ๋กญ๊ฒŒ ๊ฐ€๋Šฅ โŒ ๊ฑฐ์˜ ๋ถˆ๊ฐ€ (๋กœ๊ทธ๋Š” ์Œ“๊ธฐ๋งŒ ํ•˜๋‹ˆ๊นŒ OK)
โœ… ์ฐธ๊ณ 
์—ด(Column) ๊ธฐ๋ฐ˜ ์ €์žฅ์ด ์™œ ๋น ๋ฅผ๊นŒ?
"์˜ค๋Š˜ ์—๋Ÿฌ ๋กœ๊ทธ๊ฐ€ ๋ช‡ ๊ฑด์ด์•ผ?" ๊ฐ™์€ ์งˆ๋ฌธ์—์„œ, ํ–‰ ๊ธฐ๋ฐ˜(SQL)์€ ๋ชจ๋“  ํ–‰์„ ์ฝ์œผ๋ฉด์„œ ํ•ด๋‹น ์ปฌ๋Ÿผ์„ ํ™•์ธํ•œ๋‹ค.
์—ด ๊ธฐ๋ฐ˜(KQL/ADX)์€ ํ•„์š”ํ•œ ์ปฌ๋Ÿผ๋งŒ ์ญ‰ ์ฝ๊ณ  ๋‚˜๋จธ์ง€๋Š” ์•„์˜ˆ ์•ˆ๋ณธ๋‹ค.
๋ฐ์ดํ„ฐ๊ฐ€ ํด์ˆ˜๋ก ์ฐจ์ด๊ฐ€ ๊ทน์ ์ด๋‹ค.

๐Ÿ’ก ํ•œ ์ค„ ์ •๋ฆฌ : SQL์€ ๋งŒ๋Šฅ ๋„๊ตฌ(์Šค์œ„์Šค ์•„๋ฏธ ๋‚˜์ดํ”„), KQL์€ ๋กœ๊ทธ ๋ถ„์„ ์ „์šฉ ๊ณ ์„ฑ๋Šฅ ๋„๊ตฌ. ์šฉ๋„์— ๋งž๊ฒŒ ์“ฐ๋Š” ๊ฒƒ!

 

 

KQL์˜ ํ•ต์‹ฌ ์›๋ฆฌ - ์ปจ๋ฒ ์ด์–ด ๋ฒจํŠธ ๊ตฌ์กฐ

KQL ๋ฌธ๋ฒ•์„ ๋ฐฐ์šฐ๊ธฐ ์ „์—, ๋ชจ๋“  KQL ์ฟผ๋ฆฌ๊ฐ€ ๋”ฐ๋ฅด๋Š” ํ•˜๋‚˜์˜ ์›๋ฆฌ๋ฅผ ๋จผ์ € ์ดํ•ดํ•ด์•ผ ํ•œ๋‹ค.

๐Ÿ’ก ๋น„์œ 
KQL์€ ์ปจ๋ฒ ์ด์–ด ๋ฒจํŠธ ๊ตฌ์กฐ์ด๋‹ค.
๋ฐ์ดํ„ฐ๋ฅผ ๋งจ ์œ„์— ์˜ฌ๋ ค๋†“๊ณ , ํŒŒ์ดํ”„( | )๋ฅผ ํ†ตํ•ด ํ•œ ๋‹จ๊ณ„์”ฉ ๊ฐ€๊ณตํ•˜๋ฉด์„œ ์•„๋ž˜๋กœ ํ˜๋ ค๋ณด๋‚ธ๋‹ค.
์œ„์—์„œ ์•„๋ž˜๋กœ ์ฝ์œผ๋ฉด ๊ทธ๋Œ€๋กœ ์‹คํ–‰ ์ˆœ์„œ!

SQL๊ณผ ๋‹ฌ๋ฆฌ ์ฝ๋Š” ์ˆœ์„œ = ์‹คํ–‰ ์ˆœ์„œ์ด๊ธฐ ๋•Œ๋ฌธ์— ํ›จ์”ฌ ์ง๊ด€์ ์ด๋‹ค.
์ด ์›๋ฆฌ๋งŒ ๊ธฐ์–ตํ•˜๋ฉด ์–ด๋–ค KQL ์ฟผ๋ฆฌ๋“  ์œ„์—์„œ ์•„๋ž˜๋กœ ์ฝ์œผ๋ฉด ๋œ๋‹ค.

 

 

๊ธฐ์ดˆ ๋ฌธ๋ฒ• - ๊ธฐ๋ณธ ์กฐํšŒ

ํ…Œ์ด๋ธ” ์กฐํšŒ

ํ…Œ์ด๋ธ” ์ด๋ฆ„๋งŒ ์“ฐ๋ฉด ์ „์ฒด ๋ฐ์ดํ„ฐ๊ฐ€ ์กฐํšŒ๋œ๋‹ค.
SQL์˜ SELECT * FROM ๊ณผ ๋™์ผํ•˜๋‹ค.

-- SQL
SELECT *
FROM StormEvents


-- KQL
StormEvents

where - ํ•„ํ„ฐ๋ง

์กฐ๊ฑด์— ๋งž๋Š” ํ–‰๋งŒ ๊ฑธ๋Ÿฌ๋‚ธ๋‹ค.

-- SQL
SELECT *
FROM StormEvents
WHERE State = 'TEXAS'


-- KQL
StromEvents
| where State == "TEXAS"
์—ฐ์‚ฐ์ž ์˜๋ฏธ ์ฃผ์˜์‚ฌํ•ญ
= ๊ฐ™๋‹ค SQL์€ =, KQL์€ ==
๊ฐ™์ง€ ์•Š๋‹ค  
>, <, ≤, ≥ ์ˆซ์ž ๋น„๊ต SQL๊ณผ ๋™์ผ
and / or ์กฐ๊ฑด ์กฐํ•ฉ SQL์˜ AND, OR๊ณผ ๋™์ผ
๋ฌธ์ž์—ด ๋”ฐ์˜ดํ‘œ ํฐ๋”ฐ์˜ดํ‘œ " " SQL์€ ์ž‘์€๋”ฐ์˜ดํ‘œ ' ', KQL์€ ํฐ๋”ฐ์˜ดํ‘œ๊ฐ€ ๊ธฐ๋ณธ

์—ฌ๋Ÿฌ ์กฐ๊ฑด์„ ์กฐํ•ฉํ•  ๋•Œ๋Š” ํ•œ ์ค„์— and / or๋กœ ์“ฐ๊ฑฐ๋‚˜, where๋ฅผ ์—ฌ๋Ÿฌ ์ค„๋กœ ๋‚˜๋ˆ ๋„ ๋œ๋‹ค.

-- ํ•œ ์ค„์— ์ž‘์„ฑ
StormsEvents
| where State == "TEXAS" and EventType == "Flood"


-- ์—ฌ๋Ÿฌ ์ค„๋กœ ๋ถ„๋ฆฌ (๊ฐ€๋…์„ฑ ์ข‹์Œ)
StormEvents
| where State == "TEXAS"
| where EventType == "Flood"

project - ์ปฌ๋Ÿผ ์„ ํƒ

๋ณด๊ณ  ์‹ถ์€ ์ปฌ๋Ÿผ๋งŒ ๊ณจ๋ผ์„œ ๊ฒฐ๊ณผ์— ํ‘œ์‹œํ•œ๋‹ค.

-- SQL
SELECT EventType, StartTime
FROM StormEvents


-- KQL
StormEvents
| project EventType, StartTime
๋ช…๋ น์–ด ์—ญํ•  ์„ค๋ช…
project ์ง€์ • ์ปฌ๋Ÿผ๋งŒ ํ‘œ์‹œ ๋‚˜๋จธ์ง€ ์ปฌ๋Ÿผ์€ ๋ชจ๋‘ ์ œ๊ฑฐ๋จ
project-away ์ง€์ • ์ปฌ๋Ÿผ๋งŒ ์ œ์™ธ ๋‚˜๋จธ์ง€ ์ปฌ๋Ÿผ์€ ์ „๋ถ€ ์œ ์ง€
project-rename ์ปฌ๋Ÿผ ์ด๋ฆ„ ๋ณ€๊ฒฝ ๊ธฐ์กด ๊ตฌ์กฐ ์œ ์ง€ํ•˜๋ฉด์„œ ์ด๋ฆ„๋งŒ ๋ฐ”๊ฟˆ
extend ์ƒˆ ์ปฌ๋Ÿผ ์ถ”๊ฐ€ ๊ธฐ์กด ์ปฌ๋Ÿผ ์œ ์ง€ + ์ƒˆ ์ปฌ๋Ÿผ ์ถ”๊ฐ€

extend๋Š” ํŠนํžˆ ์ž์ฃผ ์“ฐ์ด๋Š”๋ฐ, SQL์˜ SELECT *, ๊ณ„์‚ฐ์‹ AS ๋ณ„๋ช… ๊ณผ ๊ฐ™์€ ์—ญํ• ์ด๋‹ค.

StromEvents
| extend DamageTotal = DamageProperty + DamageCrops

sort by - ์ •๋ ฌ

-- SQL
SELECT *
FROM StormEvents
ORDER BY DamageProperty DESC


-- KQL
StormEvents
| sort by DamageProperty desc

take / limit - ๊ฐœ์ˆ˜ ์ œํ•œ

SQL์˜ SELECT TOP N ๊ณผ ๋™์ผํ•˜๋‹ค.
take์™€ limit์€ ์™„์ „ํžˆ ๊ฐ™์€ ๋™์ž‘์ด๋‹ค.

-- SQL
SELECT TOP 10 *
FROM StormEvents


-- KQL
StormEvents
| take 10

summarize - ์ง‘๊ณ„ (๊ฐ€์žฅ ์ค‘์š”)

KQL์˜ ๊ฝƒ์ด๋‹ค.
SQL์˜ GROUP BY + ์ง‘๊ณ„ ํ•จ์ˆ˜์™€ ๊ฐ™์€ ์—ญํ• ์ธ๋ฐ, ๋ฌธ๋ฒ•์ด ๋” ๊ฐ„๊ฒฐํ•˜๋‹ค.

-- SQL
SELECT  State
	,	COUNT(*)
FROM StormEvents
GROUP BY State


-- KQL
StormEvents
| summarize count() by State
KQL ํ•จ์ˆ˜ SQL ๋Œ€์‘ ์„ค๋ช…
count() COUNT(*) ํ–‰ ์ˆ˜
sum(col) SUM(col) ํ•ฉ๊ณ„
avg(col) AVG(col) ํ‰๊ท 
min(col) / max(col) MIN / MAX ์ตœ์†Ÿ๊ฐ’ / ์ตœ๋Œ“๊ฐ’
dcount(col) COUNT(DISTINCT col) ๊ณ ์œ ๊ฐ’ ์ˆ˜
countif(col) ์—†์Œ (CSAE WHEN ํ•„์š”) ์กฐ๊ฑด์— ๋งž๋Š” ํ–‰๋งŒ ์นด์šดํŠธ

countif๋Š” SQL์—๋Š” ์—†๋Š” KQL๋งŒ์˜ ํŽธ๋ฆฌํ•œ ํ•จ์ˆ˜์ด๋‹ค.
SQL์—์„œ SUM(CASE WHEN ... THEN 1 ELSE 0 END) ๋กœ ์จ์•ผ ํ•˜๋Š” ๊ฒƒ์„ ํ•œ ๋‹จ์–ด๋กœ ํ•ด๊ฒฐํ•œ๋‹ค.

StormEvents
| summarize
	TotalEvents = count(),
    FloodEvents = countif(EventType == "Flood")
    by State

์‹œ๊ฐ„ ๊ด€๋ จ - ๋กœ๊ทธ ๋ถ„์„์˜ ํ•ต์‹ฌ

๋กœ๊ทธ ๋ฐ์ดํ„ฐ์—์„œ ์‹œ๊ฐ„ ๋‹ค๋ฃจ๋Š” ์ผ์€ ํ•„์ˆ˜์ด๋‹ค.
KQL์€ ์‹œ๊ฐ„ ์ฒ˜๋ฆฌ๊ฐ€ ํŠนํžˆ ๊ฐ•๋ ฅํ•˜๋‹ค.

-- SQL
SELECT *
FROM StormEvents
WHERE  StartTime > DATEADD(DAY, -7, GETDATE())


-- KQL
StormEvents
| where StartTime > ago(7d)
์‹œ๊ฐ„ ํ‘œํ˜„ ์˜๋ฏธ
ago(1h) 1์‹œ๊ฐ„ ์ „
ago(30m) 30๋ถ„ ์ „
ago(7d) 7์ผ ์ „

bin( ) ํ•จ์ˆ˜๋ฅผ ์“ฐ๋ฉด ์‹œ๊ฐ„ ๋‹จ์œ„๋กœ ์ง‘๊ณ„ํ•  ์ˆ˜ ์žˆ๋‹ค.
SQL์—์„œ ๋‚ ์งœ๋ฅผ ์ž˜๋ผ์„œ GROUP BY ํ•˜๋ ค๋ฉด CAST๋‚˜ CONVERT๊ฐ€ ํ•„์š”ํ•˜์ง€๋งŒ, KQL์€ bin( ) ํ•˜๋‚˜๋ฉด ๋์ด๋‹ค!

 

 

์ค‘๊ธ‰ ๋ฌธ๋ฒ• - ๋ณ€์ˆ˜, ๋ฒ”์œ„, ํƒ€์ž…

let - ๋ณ€์ˆ˜ ์„ ์–ธ

KQL์—์„œ ๊ฐ€์žฅ ์ž์ฃผ ์“ฐ์ด๋Š” ๋ฌธ๋ฒ• ์ค‘ ํ•˜๋‚˜์ด๋‹ค.
๊ฐ’์ด๋‚˜ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ์— ์ด๋ฆ„์„ ๋ถ™์—ฌ์„œ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ’ก ๋น„์œ 
์š”๋ฆฌ ์‹œ์ž‘ ์ „์— ์žฌ๋ฃŒ๋ฅผ ๋ฏธ๋ฆฌ ์†์งˆํ•ด์„œ ๊ทธ๋ฆ‡์— ๋‹ด์•„๋‘๋Š” ๊ฒƒ์ด๋‹ค.
์–‘ํŒŒ๋Š” ์—ฌ๊ธฐ, ๋งˆ๋Š˜์€ ์ €๊ธฐ - ๋ฏธ๋ฆฌ ์ค€๋น„ํ•ด๋†“์œผ๋ฉด ๋ณธ ์š”๋ฆฌ์—์„œ ๋ฐ”๋กœ ๊บผ๋‚ด ์“ธ ์ˆ˜ ์žˆ๋‹ค.
-- ๋‹จ์ˆœ ๊ฐ’ ์ €์žฅ
let targetDate = datetime('2024-06-01');
let maxCount = 100;


-- ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๋ณ€์ˆ˜์— ์ €์žฅ (๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ!)
let errorLogs = 
	AppLogs
    | where Level == "Error"
    | where Time > ago(1d);


errorLogs
| summarize count() by ServiceName

 

โš ๏ธ ์ฃผ์˜
let ๋ฌธ์€ ๋ฐ˜๋“œ์‹œ ์„ธ๋ฏธ์ฝœ๋ก ( ; )์œผ๋กœ ๋๋‚˜์•ผ ํ•œ๋‹ค.
์•ˆ ๋ถ™์ด๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
โœ… ์ฐธ๊ณ 
SQL์˜ CTE( WITH ๋ฌธ )์™€ ๊ฐ€์žฅ ๋น„์Šทํ•˜์ง€๋งŒ, CTE๋Š” ๋ฐ”๋กœ ๋‹ค์Œ SELECT์—์„œ๋งŒ ์“ธ ์ˆ˜ ์žˆ๋Š” ๋ฐ˜๋ฉด let ์€ ์—ฌ๋Ÿฌ ๋ฒˆ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.
"CTE์˜ ํŽธ๋ฆฌํ•จ + ์ž„์‹œ ํ…Œ์ด๋ธ”์˜ ์žฌ์‚ฌ์šฉ์„ฑ" ์„ ํ•ฉ์นœ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.

datatable - ์ž„์‹œ ํ…Œ์ด๋ธ” ๋งŒ๋“ค๊ธฐ

DB์— ์—†๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ”๋“œ ์•ˆ์—์„œ ์ง์ ‘ ๋งŒ๋“ค์–ด ์“ธ ์ˆ˜ ์žˆ๋‹ค.

let targets = datatable (Deviced:string) [
	"SRV-01",
    "SRV-02",
    "SRV-03"
];

in - ๋ชฉ๋ก ํฌํ•จ ํ•„ํ„ฐ

-- ์ง์ ‘ ๋ชฉ๋ก ์ง€์ •
AppLogs
| where ServiceName in ("Auth", "Payment")


-- ๋ณ€์ˆ˜(์„œ๋ธŒ์ฟผ๋ฆฌ)์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ
AppLogs
| where Deviced in (targets | project DeviceId)


-- ๋ฐ˜๋Œ€ - ํฌํ•จ๋˜์ง€ ์•Š๋Š” ๊ฒƒ
AppLogs
| where Servicename !in ("Auth", "Payment")

between - ๋ฒ”์œ„ ํ•„ํ„ฐ

AppLogs
| where Time between (datetime('2024-06-01') .. datetime('2024-06-30'))


-- ์ˆซ์ž์—๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
| where DamageProperty between (100 .. 500)

 

โœ… ํฌ์ธํŠธ
between ๋ฌธ๋ฒ•์€ ๊ด„ํ˜ธ ( ) ์™€ ์  ๋‘ ๊ฐœ .. ๋ฅผ ํ•จ๊ป˜ ์จ์•ผ ํ•œ๋‹ค.
between (A .. B) → A ์ด์ƒ B ์ดํ•˜.

ํƒ€์ž… ๋ณ€ํ™˜ ํ•จ์ˆ˜

KQL์€ ๋ฐ์ดํ„ฐ ํƒ€์ž…์— ๋ฏผ๊ฐํ•˜๋‹ค.
JSON์—์„œ ๊บผ๋‚ธ ๊ฐ’์€ dynamic ํƒ€์ž…์ด๋ผ ๋น„๊ตํ•˜๋ ค๋ฉด ๋ณ€ํ™˜์ด ํ•„์š”ํ•˜๋‹ค.

KQL ํ•จ์ˆ˜ SQL ๋Œ€์‘ ์—ญํ• 
tostring(๊ฐ’) CAST(๊ฐ’ AS NVARCHAR) ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜
toint(๊ฐ’) CAST(๊ฐ’ AS INT) ์ •์ˆ˜๋กœ ๋ณ€ํ™˜
tolong(๊ฐ’) CAST(๊ฐ’ AS BIGINT) ํฐ ์ •์ˆ˜๋กœ ๋ณ€ํ™˜
todouble(๊ฐ’) CAST(๊ฐ’ AS FLOAT) ์‹ค์ˆ˜๋กœ ๋ณ€ํ™˜
todatetime(๊ฐ’) CAST(๊ฐ’ AS DATETIME) ๋‚ ์งœ๋กœ ๋ณ€ํ™˜

๋‚ ์งœ / ์‹œ๊ฐ„ ํ•จ์ˆ˜

KQL ํ•จ์ˆ˜ SQL ๋Œ€์‘ ์—ญํ• 
datetime_add('hour', 9, dt) DATEADD(HOUR, 9 dt) ๋‚ ์งœ์— ์‹œ๊ฐ„ ๋”ํ•˜๊ธฐ
endofday(dt) ์ง์ ‘ ๋Œ€์‘ ์—†์Œ ํ•ด๋‹น ๋‚ ์งœ์˜ 23:59:59 ๋ฐ˜ํ™˜
startofday(dt) ์ง์ ‘ ๋Œ€์‘ ์—†์Œ ํ•ด๋‹น ๋‚ ์งœ์˜ 00:00:00 ๋ฐ˜ํ™˜
startofmonth(dt) ์ง์ ‘ ๋Œ€์‘ ์—†์Œ ํ•ด๋‹น ์›”์˜ 1์ผ 00:00:00
format_datetime(dt, 'yyyy-MM-dd') FORMAT(dt, 'yyyy-MM-dd') ์›ํ•˜๋Š” ํ˜•์‹์œผ๋กœ ์ถœ๋ ฅ

 

 

์ค‘๊ธ‰ ๋ฌธ๋ฒ• - arg_max, union, join

arg_max - ๊ทธ๋ฃน๋ณ„ ์ตœ์‹  ํ–‰ ๊ฐ€์ ธ์˜ค๊ธฐ

KQL์—์„œ ๊ฐ€์žฅ ์ž์ฃผ ์“ฐ์ด๋ฉด์„œ๋„ SQL์—์„œ๋Š” ๊ฝค ๋ฒˆ๊ฑฐ๋กœ์šด ์ž‘์—…์ด๋‹ค.
"๊ฐ ๊ทธ๋ฃน์—์„œ ํŠน์ • ์ปฌ๋Ÿผ์ด ๊ฐ€์žฅ ํฐ(์ตœ์‹ ์ธ) ํ–‰์„ 1๊ฑด๋งŒ ๊ฐ€์ ธ์™€"๋ผ๋Š” ์˜๋ฏธ๋‹ค.

๐Ÿ’ก ๋น„์œ 
ํ•™์ƒ 30๋ช…์˜ ์‹œํ—˜ ๊ธฐ๋ก์ด ์—ฌ๋Ÿฌ ๊ฑด์”ฉ ์žˆ์„ ๋•Œ, "๊ฐ ํ•™์ƒ์˜ ๊ฐ€์žฅ ์ตœ๊ทผ ์‹œํ—˜ ๊ฒฐ๊ณผ๋งŒ ๊ฐ€์ ธ์™€"๋ผ๋Š” ์š”์ฒญ์„ ํ•œ ์ค„๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.
-- SQL
SELECT *
FROM (
    SELECT   *
        ,    ROW_NUMBER() OVER (PARTITION BY UserId, ORDER BY Time DESC) AS rn
    FROM AppLogs
) AS t
WHERE rn = 1


-- KQL
AppLogs
| summarize arg_max(Time, *) by UserId
ํ•จ์ˆ˜ ์˜๋ฏธ ์šฉ๋„
arg_max(Time, *) Time์ด ๊ฐ€์žฅ ํฐ(์ตœ์‹ ) ํ–‰์˜ ๋ชจ๋“  ์ปฌ๋Ÿผ ์ตœ์‹  ์ƒํƒœ ์กฐํšŒ
arg_min(Time, *) Time์ด ๊ฐ€์žฅ ์ž‘์€(์ตœ์ดˆ) ํ–‰์˜ ๋ชจ๋“  ์ปฌ๋Ÿผ ์ตœ์ดˆ ๊ธฐ๋ก ์กฐํšŒ

union - ๋‘ ๊ฒฐ๊ณผ ํ•ฉ์น˜๊ธฐ

SQL์˜ UNION ALL ๊ณผ ๋™์ผํ•˜๋‹ค.
๋‘ ๊ฐœ์˜ ๊ฒฐ๊ณผ๋ฅผ ํ•˜๋‚˜๋กœ ํ•ฉ์นœ๋‹ค.

๐Ÿ’ก ๋น„์œ 
๋‘ ๊ฐœ์˜ ์ ‘์‹œ์— ๋‹ด๊ธด ์Œ์‹์„ ํ•˜๋‚˜์˜ ํฐ ์ ‘์‹œ์— ํ•ฉ์น˜๋Š” ๊ฒƒ์ด๋‹ค.
let morningLogs = AppLogs | where Time between (sDate .. noon);
let eveningLogs = AppLogs | where Time between (noon .. eDate);

morningLogs
| union eveningLogs
| summarize count() by Level

join - ๋‘ ํ…Œ์ด๋ธ” ์—ฐ๊ฒฐํ•˜๊ธฐ

Users
| join kind-inner Purchase on UserId
KQL SQL ๋Œ€์‘ ์„ค๋ช…
kind=inner INNER JOIN ์–‘์ชฝ ๋ชจ๋‘ ์žˆ๋Š” ๊ฒƒ๋งŒ
kind=leftouter LEFT JOIN ์™ผ์ชฝ ์ „๋ถ€ + ์˜ค๋ฅธ์ชฝ ๋งค์นญ
kind=rightouter RIGHT JOIN ์˜ค๋ฅธ์ชฝ ์ „๋ถ€ + ์™ผ์ชฝ ๋งค์นญ
kind=fullouter FULL OUTER JOIN ์–‘์ชฝ ์ „๋ถ€
kind=leftnanti ์ง์ ‘ ๋Œ€์‘ ์—†์Œ โญ ์™ผ์ชฝ์—๋งŒ ์žˆ๊ณ  ์˜ค๋ฅธ์ชฝ์—๋Š” ์—†๋Š” ๊ฒƒ
kind=leftsemi ์ง์ ‘ ๋Œ€์‘ ์—†์Œ ์™ผ์ชฝ ์ค‘์—์„œ ์˜ค๋ฅธ์ชฝ์— ์žˆ๋Š” ๊ฒƒ๋งŒ

leftanti - ํ•ต์‹ฌ ์กฐ์ธ ํŒจํ„ด

๐Ÿ’ก ๋น„์œ 
"A ๋ฐ˜ ๋ช…๋‹จ์—๋Š” ์žˆ๋Š”๋ฐ B ๋ฐ˜ ๋ช…๋‹จ์—๋Š” ์—†๋Š” ํ•™์ƒ๋งŒ ์ฐพ์•„์ค˜."๋ผ๋Š” ์š”์ฒญ์ด๋‹ค.
-- SQL
SELECT *
FROM anyRange AS a
WHERE NOT EXISTS (
    SELECT 1 
    FROM inRange AS b
    WHERE b.DeviceId = a.DeviceId
)


-- KQL
anyRange
| join kind=leftanti inRange on DeviceId
โœ… ํฌ์ธํŠธ
์‹ค๋ฌด ํ™œ์šฉ ํŒจํ„ด : "์˜ค๋Š˜ ๋กœ๊ทธ๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ๊ฒƒ, ์—†์œผ๋ฉด ๊ณผ๊ฑฐ ์ตœ์‹  ๋กœ๊ทธ"๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ „๋žต์—์„œ union + leftanti๊ฐ€ ํ•จ๊ป˜ ์“ฐ์ธ๋‹ค.
๋ชจ๋“  ๋Œ€์ƒ์˜ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ๋น ์ง์—†์ด ํ™•๋ณดํ•˜๋Š” ํ•ต์‹ฌ ํŒจํ„ด์ด๋‹ค.
let lastest_rows = inRange
| union (anyRange | join kind leftanti inRange on DeviceId);

-- inRange : ์˜ค๋Š˜ ๋กœ๊ทธ ์žˆ๋Š” ๋Œ€์ƒ
-- anyRange leftanti inRange : ์˜ค๋Š˜ ๋กœ๊ทธ ์—†๋Š” ๋Œ€์ƒ์˜ ๊ณผ๊ฑฐ ์ตœ์‹ 
-- union : ๋‘˜์„ ํ•ฉ์ณ์„œ ๋ชจ๋“  ๋Œ€์ƒ ํ™•๋ณด!

 

 

๊ณ ๊ธ‰ ๋ฌธ๋ฒ• - ๋™์  ํƒ€์ž…๊ณผ mv-expand

๋™์  ํƒ€์ž… (Dynamic Type) - JSON ๋ฐ์ดํ„ฐ ์ ‘๊ทผ

ADX์˜ ๋กœ๊ทธ ๋ฐ์ดํ„ฐ๋Š” ํ•œ ์ปฌ๋Ÿผ์— JSON์ด ํ†ต์งธ๋กœ ๋“ค์–ด์žˆ๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.
KQL์—์„œ๋Š” ์ด๋ฅผ dynamic ํƒ€์ž…์ด๋ผ ๋ถ€๋ฅด๋ฉฐ, ์ ( . )์œผ๋กœ ๋‚ด๋ถ€์— ์ ‘๊ทผํ•œ๋‹ค.

๐Ÿ’ก ๋น„์œ 
MSSQL์—์„œ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์—‘์…€ ํ‘œ์ฒ˜๋Ÿผ ๊น”๋”ํ•˜๊ฒŒ ์ปฌ๋Ÿผ๋ณ„๋กœ ์ €์žฅ๋œ๋‹ค.
ํ•˜์ง€๋งŒ ADX์˜ ๋กœ๊ทธ ๋ฐ์ดํ„ฐ๋Š” ํ•œ ์นธ ์•ˆ์— ํƒ๋ฐฐ ์ƒ์ž(JSON)๊ฐ€ ํ†ต์งธ๋กœ ๋“ค์–ด์žˆ๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.
KQL์€ ๊ทธ ์ƒ์ž๋ฅผ ์ ( . )๋งŒ ์ฐ์œผ๋ฉด ์—ด ์ˆ˜ ์žˆ๋‹ค.
{
    "Device": {
        "Id": "SRV-01",
        "Name": "Server-A01"
    },
    "Metrics": [
        {
            "MetircType": "CPU",
            "Value": 85
        },
        {
            "MetricType": "Memory",
            "Value": 72
        },
        {
            "MetricType": "Disk",
            "Value": 60
        }
    ]
}

 

์ƒํ™ฉ ํ•ด๊ฒฐ์ฑ…
JSON์—์„œ ๊บผ๋‚ธ ๊ฐ’์€ dynamic ํƒ€์ž… tostring(), toint() ๋“ฑ์œผ๋กœ ๋ณ€ํ™˜ ํ•„์š”
ํ‚ค์— ํŠน์ˆ˜๋ฌธ์ž/๊ณต๋ฐฑ์ด ์žˆ์„ ๋•Œ Data["server-info"] ๋Œ€๊ด„ํ˜ธ ํ‘œ๊ธฐ๋ฒ• ์‚ฌ์šฉ
์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ‚ค์— ์ ‘๊ทผํ•˜๋ฉด ์—๋Ÿฌ ์—†์ด null ๋ฐ˜ํ™˜ (์•ˆ์ „ํ•จ)

mv-expand - ๋ฐฐ์—ด์„ ํ–‰์œผ๋กœ ํŽผ์น˜๊ธฐ

๐Ÿ’ก ๋น„์œ 
ํƒ๋ฐฐ ์ƒ์ž(JSON) ์•ˆ์— ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ž‘์€ ์ƒํ’ˆ(๋ฐฐ์—ด)์ด ํ•จ๊ป˜ ํฌ์žฅ๋˜์–ด ์žˆ์„ ๋•Œ, ์ƒํ’ˆ ํ•˜๋‚˜ํ•˜๋‚˜๋ฅผ ๊บผ๋‚ด์„œ ๊ฐ๊ฐ ๋ณ„๋„์˜ ํ–‰์œผ๋กœ ๋‚˜์—ดํ•˜๋Š” ์ž‘์—…์ด๋‹ค.

latest_rows
| mv-expand Data_Metrics = Data.Metrics
| project
    DeviceId = DeviceId,
    MetricType = tostring(Data_Metrics.MetricType),
    Value = tostring(Data_Metrics.Value)
โš ๏ธ ์ฃผ์˜
ํ–‰์ด ํฌ๊ฒŒ ๋Š˜์–ด๋‚จ : ๋ฐฐ์—ด ์›์†Œ ์ˆ˜๋งŒํผ ํ–‰์ด ๊ณฑํ•ด์ง„๋‹ค.
1๋งŒ ํ–‰์— ๊ฐ 10๊ฐœ์”ฉ์ด๋ฉด → 10๋งŒํ–‰. ๋˜ํ•œ ๋นˆ ๋ฐฐ์—ด[ ] ์€ ํ•ด๋‹น ์—ด์ด ๊ฒฐ๊ณผ์—์„œ ์‚ฌ๋ผ์ง€๋ฏ€๋กœ ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค.

 

 

์‹ค์ „ ์ฟผ๋ฆฌ - ์ „์ฒด ํ๋ฆ„ ๋ถ„์„

 

 

SQL ↔ KQL ์น˜ํŠธ์‹œํŠธ

SQL์„ ์•„๋Š” ์ƒํƒœ์—์„œ KQL์„ ๋น ๋ฅด๊ฒŒ ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ๋„๋ก ์ •๋ฆฌํ•œ ํ‘œ

ํ•˜๊ณ  ์‹ถ์€ ๊ฒƒ SQL (MSSQL) KQL (ADX)
์ „์ฒด ์กฐํšŒ SELECT * FROM T T
์กฐ๊ฑด ํ•„ํ„ฐ WHERE col = 'a' | where col = "a"
์ปฌ๋Ÿผ ์„ ํƒ SELECT a, b | project a, b
์ƒˆ ์ปฌ๋Ÿผ ์ถ”๊ฐ€ SELECT *, a+b AS c | extend c = a + b
์ •๋ ฌ ORDER BY a DESC | sort by a desc
๊ฐœ์ˆ˜ ์ œํ•œ SELECT TOP 10 | take 10
์ง‘๊ณ„  GROUP BY + COUNT(*) | summarize count() by col
๊ณ ์œ ๊ฐ’ ์ˆ˜ COUNT(DISTINCT col) dcount(col)
์กฐ๊ฑด๋ถ€ ์นด์šดํŠธ SUM(CASE WHEN ...) countif(์กฐ๊ฑด)
์ตœ๊ทผ N์ผ DATEADD(DAY, -7, GETDATE()) ago(7d)
์‹œ๊ฐ„ ๋‹จ์œ„ ์ง‘๊ณ„ CAST + GROUP BY bin(col, 1h)
๋ณ€์ˆ˜ ์„ ์–ธ DECLARE @v / CTE let v = ...;
๊ทธ๋ฃน๋ณ„ ์ตœ์‹  ํ–‰ ROW_NUMBER() OVER(...) arg_max(Time, *) by col
JSON ์ ‘๊ทผ JSON_VALUE(col, '$.key') col.key
๋ฐฐ์—ด ํŽผ์น˜๊ธฐ CROSS APPLY OPENJSON mv-expand col
๊ฒฐ๊ณผ ํ•ฉ์น˜๊ธฐ UNION ALL union
์—†๋Š” ๊ฒƒ๋งŒ ํ•„ํ„ฐ NOT EXISTS / LEFT JOIN + IS NULL join kind-leftanti

 

 

References

์ฃผ์ œ ๋ฌธ์„œ ๋งํฌ
KQL ์ „์ฒด ๋ ˆํผ๋Ÿฐ์Šค Kusto Query Language ๊ฐœ์š” https://learn.microsoft.com/ko-kr/kusto/query/?view=microsoft-fabric
KQL ํŠœํ† ๋ฆฌ์–ผ Kusto ์ฟผ๋ฆฌ ์ž์Šต์„œ https://learn.microsoft.com/ko-kr/kusto/query/tutorials/learn-common-operators?view=microsoft-fabric
SQL → KQL ๋ณ€ํ™˜ SQL์—์„œ Kusto๋กœ์˜ ์น˜ํŠธ ์‹œํŠธ https://learn.microsoft.com/ko-kr/kusto/query/sql-cheat-sheet?view=microsoft-fabric
summarize ์—ฐ์‚ฐ์ž summarize ๊ณต์‹ ๋ฌธ์„œ https://learn.microsoft.com/ko-kr/kusto/query/summarize-operator?view=microsoft-fabric
join ์—ฐ์‚ฐ์ž join ์ข…๋ฅ˜์™€ ์‚ฌ์šฉ๋ฒ• https://learn.microsoft.com/ko-kr/kusto/query/join-operator?view=microsoft-fabric
mv-expand ์—ฐ์‚ฐ์ž mv-expand ๊ณต์‹ ๋ฌธ์„œ https://learn.microsoft.com/ko-kr/kusto/query/mv-expand-operator?view=microsoft-fabric
Azure Data Explorer ADX(Azure Data Explorer) ๊ฐœ์š” https://learn.microsoft.com/ko-kr/azure/data-explorer/data-explorer-overview