Sổ tay kỹ thuật

ClickHouse SQL – hàm thời gian, quantile, arrayJoin, WITH FILL

cheatsheet#clickhouse#sql#grafana· cập nhật 2026-11-06

Tra nhanh hàm SQL ClickHouse hay dùng cho telemetry và log - gom theo khoảng thời gian, tính bps từ counter, percentile, mảng, lấp chỗ trống.

Sửa trang này
Mục lục

Ví dụ dùng bảng telemetry.interfaces (counter in_octets, cột time, source, name) và logs.syslog định nghĩa trong ClickHouse schema và TTL. Query top talkers cho flow xem ClickHouse top talkers.

Hàm thời gian

Mục đích Biểu thức
Bây giờ / hôm nay now(), now64(3), today()
Khoảng tương đối time > now() - INTERVAL 15 MINUTE
Gom theo khoảng bất kỳ toStartOfInterval(time, INTERVAL 5 MINUTE)
Gom theo phút / 5 phút / giờ / ngày toStartOfMinute(t), toStartOfFiveMinutes(t), toStartOfHour(t), toDate(t)
Đổi múi giờ hiển thị toTimeZone(time, 'Asia/Ho_Chi_Minh')
Chuỗi → DateTime toDateTime('2026-11-06 08:00:00', 'Asia/Ho_Chi_Minh'), parseDateTimeBestEffort('2026-11-06T01:02:03Z')
Unix timestamp toUnixTimestamp(time), toDateTime(1730851200)
Hiệu hai mốc dateDiff('second', t1, t2)
Định dạng formatDateTime(time, '%Y-%m-%d %H:%i') (%i là phút; %M ở bản mới là tên tháng)
Macro Grafana WHERE $__timeFilter(time)

Aggregate hay dùng

Mục đích Biểu thức
Percentile quantile(0.95)(bps)
Nhiều percentile một lần quantiles(0.5, 0.95, 0.99)(bps) (trả mảng)
Percentile chính xác (tốn RAM) quantileExact(0.99)(bps)
Đếm có điều kiện countIf(severity IN ('err', 'crit'))
Tổng có điều kiện sumIf(cnt, mnemonic = 'ADJCHANGE')
Đếm giá trị khác nhau (xấp xỉ / chính xác) uniq(source) / uniqExact(source)
Giá trị mới nhất theo thời gian argMax(in_octets, time)
N giá trị xuất hiện nhiều nhất (xấp xỉ) topK(5)(mnemonic)
Gom thành mảng groupArray(name), groupUniqArray(source)
Điều kiện nhiều nhánh multiIf(bps > 9e9, 'high', bps > 1e9, 'mid', 'low')

bps từ counter (window function)

SELECT time, source, name,
       (in_octets - prev_octets) * 8 / dateDiff('second', prev_time, time) AS bps
FROM
(
    SELECT time, source, name, in_octets,
           lagInFrame(in_octets) OVER w AS prev_octets,
           lagInFrame(time) OVER w AS prev_time
    FROM telemetry.interfaces
    WHERE source = 'pe1-hni' AND time > now() - INTERVAL 1 HOUR
    WINDOW w AS (PARTITION BY source, name ORDER BY time ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)
)
WHERE prev_time > toDateTime(0)        -- bỏ dòng đầu mỗi interface (không có mẫu trước)
  AND in_octets >= prev_octets         -- bỏ điểm counter reset
ORDER BY name, time;

p95 bps theo interface trong 1 ngày: bọc query trên (đổi khoảng thời gian thành INTERVAL 1 DAY) rồi SELECT source, name, quantile(0.95)(bps) ... GROUP BY source, name.

Lấp khoảng trống bằng WITH FILL

Phút không có log sẽ không có dòng, đồ thị bị nối thẳng. WITH FILL thêm dòng giá trị mặc định (0):

SELECT toStartOfInterval(toDateTime(timestamp), INTERVAL 1 MINUTE) AS t, count() AS n
FROM logs.syslog
WHERE timestamp > now() - INTERVAL 1 HOUR AND source = 'pe1-hni'
GROUP BY t
ORDER BY t WITH FILL
  FROM toStartOfInterval(now() - INTERVAL 1 HOUR, INTERVAL 1 MINUTE)
  TO now()
  STEP INTERVAL 1 MINUTE;

Kiểu của FROM/TO phải khớp kiểu cột t (ở đây DateTime, nên bọc toDateTime(...) cho cột DateTime64).

Mảng và arrayJoin

Mục đích Biểu thức
Sinh nhiều dòng từ mảng SELECT arrayJoin(['pe1-hni', 'pe2-hcm']) AS source
Tách chuỗi thành mảng splitByChar(',', 'ge-0/0/0,ge-0/0/1')
Lấy mọi chuỗi khớp regex extractAll(message, '\\d+\\.\\d+\\.\\d+\\.\\d+')
Lọc / biến đổi phần tử arrayFilter(x -> x > 0, arr), arrayMap(x -> x * 8, arr)
Có chứa phần tử has(arr, 'ADJCHANGE')
Độ dài, phần tử đầu (index từ 1) length(arr), arr[1]

IP nào xuất hiện nhiều nhất trong syslog 1 giờ qua (mỗi IP trong một dòng log thành một dòng kết quả):

SELECT arrayJoin(extractAll(message, '\\d+\\.\\d+\\.\\d+\\.\\d+')) AS ip, count() AS n
FROM logs.syslog
WHERE timestamp > now() - INTERVAL 1 HOUR
GROUP BY ip ORDER BY n DESC LIMIT 10;

Chuỗi và IP

Mục đích Biểu thức
Lấy nhóm regex đầu tiên extract(message, 'neighbor (\\S+)')
Khớp regex (RE2) match(message, 'BGP.*(Down|Idle)')
LIKE không phân biệt hoa thường message ILIKE '%adjchange%'
Thay theo regex replaceRegexpAll(name, '^HundredGigE', 'Hu')
IP thuộc dải isIPAddressInRange(source_ip, '198.51.100.0/24')
Chuỗi ↔ kiểu IPv4 toIPv4('198.51.100.1'), IPv4NumToString(ip)

clickhouse-client

Mục đích Lệnh
Chạy một query clickhouse-client --host ch.example.net -q "SELECT count() FROM logs.syslog"
Xuất CSV có header clickhouse-client -q "SELECT * FROM logs.syslog LIMIT 1000 FORMAT CSVWithNames" > out.csv
Tham số an toàn (tránh ghép chuỗi) clickhouse-client --param_dev=pe1-hni -q "SELECT count() FROM logs.syslog WHERE source = {dev:String}"
Nạp CSV vào bảng clickhouse-client -q "INSERT INTO telemetry.interfaces FORMAT CSVWithNames" < data.csv
Chạy file nhiều câu lệnh clickhouse-client --multiquery < schema.sql
Xem dòng theo chiều dọc thêm FORMAT Vertical (hoặc kết thúc bằng \G trong client)
Query file CSV không cần server clickhouse-local -q "SELECT count() FROM file('out.csv', CSVWithNames)"

Chuỗi trong ClickHouse dùng nháy đơn và backslash phải gấp đôi: regex \d viết là '\\d'.