dicemy/DataClawEval huggingface dataset:
# 异常事件过滤 + 窗口 TopN (FlinkSQL)

## Prompt
我需要你写一段 Flink SQL，用 datagen 内置表造设备事件流，过滤出异常事件后按省份做窗口 TopN 排名，输出到 console。

**业务背景与目标**：用内置 datagen 表生成设备事件数据，字段包括 device_id（int）、event_type（int）、province（string）、ts（timestamp），每秒 80 条，event_type 取值 1-10，province 长度 4 字符，用 ts 字段作为事件时间开 1 分钟滚动窗口（watermark 延迟 5 秒），先过滤 event_type=3 的异常事件，然后按省份分组统计每分钟的异常事件数，按异常数降序排名取 Top-5，输出字段包括窗口开始时间、省份、异常事件数、排名，打印到 console。

**源表定义**：
- `device_source`（设备事件流，datagen 连接器）：
  - device_id INT：设备 ID，随机生成
  - event_type INT：事件类型，随机取值 1~10
  - province VARCHAR：省份，随机生成，长度 4 字符
  - event_time：使用 LOCALTIMESTAMP 生成事件时间，设置 WATERMARK 延迟 5 秒
  - 生成速率：rows-per-second = 80

**输出表定义**：
- `console_output`（print 连接器）：
  - window_start TIMESTAMP：窗口开始时间
  - province VARCHAR：省份
  - abnormal_cnt BIGINT：异常事件数
  - `rank` BIGINT：排名（注意 rank 是关键字，需要用反引号包裹）

**查询逻辑**：
- 过滤：WHERE event_type = 3（异常事件）
- 窗口：TUMBLE(event_time, INTERVAL '1' MINUTE) 滚动窗口
- 聚合：按 province 和窗口分组，COUNT(*) 统计异常事件数
- 排名：ROW_NUMBER() OVER (PARTITION BY window_start ORDER BY abnormal_cnt DESC) AS `rank`
- Top-N：WHERE `rank` <= 5

**输出要求**：
- 使用 INSERT INTO console_output 输出结果
- 输出字段顺序：window_start, province, abnormal_cnt, `rank`

## Reference Solution
-- flinksql_006: 异常事件过滤 + 窗口 TopN
-- datagen 生成设备事件流 @ 80 rows/sec, event_type=3 过滤后 Tumble 1min Top-5

-- 1. 设备事件流 datagen 源表
CREATE TABLE device_source (
    device_id INT,
    event_type INT,
    province VARCHAR,
    event_time AS LOCALTIMESTAMP,
    WATERMARK FOR event_time AS event_time - INTERVAL '5' SECOND
) WITH (
    'connector' = 'datagen',
    'rows-per-second' = '80',
    'fields.event_type.kind' = 'random',
    'fields.event_type.min' = '1',
    'fields.event_type.max' = '10',
    'fields.province.kind' = 'random',
    'fields.province.length' = '4'
);

-- 2. Console 输出表
CREATE TABLE console_output (
    window_start TIMESTAMP,
    province VARCHAR,
    abnormal_cnt BIGINT,
    `rank` BIGINT
) WITH (
    'connector' = 'print'
);

-- 3. 过滤异常事件(event_type=3) → Tumble 窗口 → Top-5
INSERT INTO console_output
SELECT
    window_start,
    province,
    abnormal_cnt,
    `rank`
FROM (
    SELECT
        TUMBLE_START(event_time, INTERVAL '1' MINUTE) AS window_start,
        province,
        COUNT(*) AS abnormal_cnt,
        ROW_NUMBER() OVER (PARTITION BY TUMBLE_START(event_time, INTERVAL '1' MINUTE) ORDER BY COUNT(*) DESC) AS `rank`
    FROM device_source
    WHERE event_type = 3
    GROUP BY TUMBLE(event_time, INTERVAL '1' MINUTE), province
)
WHERE `rank` <= 5;
# 用户行为滚动窗口统计 (FlinkSQL)

## Prompt
我需要你写一段 Flink SQL，用 datagen 内置表造用户行为流，开滚动窗口统计每个用户的 action 数，结果写到 console。

**业务背景与目标**：用 datagen 内置数据源表模拟用户行为数据流，每秒生成 50 条记录，包含三个字段：userId（用户ID，字符串，长度6）、action（动作类型，字符串，长度8）、ftime（事件时间，timestamp 类型）。请基于这个 datagen 表，按事件时间做 1 分钟滚动窗口聚合，统计每个用户在每个 1 分钟窗口内的 action 总次数，并把结果（用户ID、动作次数、窗口起始时间）写入到内置控制台表中输出。事件时间字段 ftime 使用 LOCALTIMESTAMP 作为 computed column 并设置 watermark 延迟 5 秒。

**源表定义**：
- `user_source`（用户行为流，datagen 连接器）：
  - userId VARCHAR：用户 ID，随机生成，长度 6 字符
  - action VARCHAR：动作类型，随机生成，长度 8 字符
  - ftime：使用 LOCALTIMESTAMP 生成事件时间，设置 WATERMARK 延迟 5 秒
  - 生成速率：rows-per-second = 50

**输出表定义**：
- `console_output`（print 连接器）：
  - user_id VARCHAR：用户 ID
  - action_count BIGINT：动作次数
  - window_start TIMESTAMP：窗口起始时间

**查询逻辑**：
- 窗口：TUMBLE(ftime, INTERVAL '1' MINUTE) 滚动窗口
- 聚合：按 userId 分组，COUNT(*) 统计 action 总次数
- 输出：窗口起始时间 + 用户 ID + 动作次数

**输出要求**：
- 使用 INSERT INTO console_output 输出结果
- 输出字段顺序：user_id, action_count, window_start

## Reference Solution
-- flinksql_007: 用户行为滚动窗口统计
-- datagen 生成用户行为流 @ 50 rows/sec, Tumble 1min 窗口聚合

-- 1. 用户行为流 datagen 源表
CREATE TABLE user_source (
    userId VARCHAR,
    action VARCHAR,
    ftime AS LOCALTIMESTAMP,
    WATERMARK FOR ftime AS ftime - INTERVAL '5' SECOND
) WITH (
    'connector' = 'datagen',
    'rows-per-second' = '50',
    'fields.userId.length' = '6',
    'fields.action.length' = '8'
);

-- 2. Console 输出表
CREATE TABLE console_output (
    user_id VARCHAR,
    action_count BIGINT,
    window_start TIMESTAMP
) WITH (
    'connector' = 'print'
);

-- 3. Tumble 1min 窗口聚合: 统计每个用户每分钟的 action 次数
INSERT INTO console_output
SELECT
    userId AS user_id,
    COUNT(*) AS action_count,
    TUMBLE_START(ftime, INTERVAL '1' MINUTE) AS window_start
FROM user_source
GROUP BY TUMBLE(ftime, INTERVAL '1' MINUTE), userId;
# Tumbling Window PV/UV Statistics (FlinkSQL)

## Prompt
I need you to write a Flink SQL that uses a datagen built-in table to simulate a user click stream, computes PV and UV per page over a window, and writes the results to the console.

**Business Background and Objective**: Simulate a user click stream using a datagen built-in table (`user_id` ranging from 1–1000, `page_id` ranging from 1–50, 10,000 records per second). Compute the PV (`COUNT(*)`) and UV (`COUNT(DISTINCT user_id)`) per page over a 1-minute tumbling window. Output the results (window start, window end, `page_id`, `pv`, `uv`) to the console.

**Source Table Definition**:
- `click_source` (user click stream, datagen connector):
  - `user_id INT`: User ID, randomly generated in the range 1–1000
  - `page_id INT`: Page ID, randomly generated in the range 1–50
  - `event_time`: Uses `LOCALTIMESTAMP` to generate event time, with a WATERMARK delay of 5 seconds
  - Generation rate: `rows-per-second = 10000`

**Output Table Definition**:
- `console_output` (print connector):
  - `window_start TIMESTAMP`: Window start time
  - `window_end TIMESTAMP`: Window end time
  - `page_id INT`: Page ID
  - `pv BIGINT`: Page views (PV)
  - `uv BIGINT`: Unique visitors (UV)

**Query Logic**:
- Window: `TUMBLE(event_time, INTERVAL '1' MINUTE)` tumbling window
- Grouping: Group by `page_id` and the window
- PV: `COUNT(*)` to compute page views
- UV: `COUNT(DISTINCT user_id)` to compute unique visitors
- Window functions: Use `TUMBLE_START` / `TUMBLE_END` to obtain the window start and end times

**Output Requirements**:
- Use `INSERT INTO console_output` to output the results.
- Output field order: `window_start`, `window_end`, `page_id`, `pv`, `uv`

## Reference Solution
-- flinksql_009: 滚动窗口 PV/UV 统计
-- datagen 生成用户点击流 @ 10000 rows/sec, Tumble 1min PV/UV

-- 1. 用户点击流 datagen 源表
CREATE TABLE click_source (
    user_id INT,
    page_id INT,
    event_time AS LOCALTIMESTAMP,
    WATERMARK FOR event_time AS event_time - INTERVAL '5' SECOND
) WITH (
    'connector' = 'datagen',
    'rows-per-second' = '10000',
    'fields.user_id.kind' = 'random',
    'fields.user_id.min' = '1',
    'fields.user_id.max' = '1000',
    'fields.page_id.kind' = 'random',
    'fields.page_id.min' = '1',
    'fields.page_id.max' = '50'
);

-- 2. Console 输出表
CREATE TABLE console_output (
    window_start TIMESTAMP,
    window_end TIMESTAMP,
    page_id INT,
    pv BIGINT,
    uv BIGINT
) WITH (
    'connector' = 'print'
);

-- 3. Tumble 1min 窗口聚合: 按页面统计 PV 和 UV
INSERT INTO console_output
SELECT
    TUMBLE_START(event_time, INTERVAL '1' MINUTE) AS window_start,
    TUMBLE_END(event_time, INTERVAL '1' MINUTE) AS window_end,
    page_id,
    COUNT(*) AS pv,
    COUNT(DISTINCT user_id) AS uv
FROM click_source
GROUP BY TUMBLE(event_time, INTERVAL '1' MINUTE), page_id;
# 滑动窗口(HOP)店铺统计 (FlinkSQL)

## Prompt
我需要你写一段 Flink SQL，用 datagen 模拟订单流，按店铺做滑动窗口统计订单金额和数量，写入 console。

**业务背景与目标**：用 datagen 模拟订单流（order_id 取值 1~1000000、shop_id 取值 1~100、amount 取值 0~1000，每秒 5000 条），按 1 分钟步长、5 分钟窗口大小做 HOP 滑动窗口统计每个店铺的订单总额（SUM）、订单数（COUNT）和平均金额（AVG），写入 console。

**源表定义**：
- `order_source`（订单流，datagen 连接器）：
  - order_id BIGINT：订单 ID，随机取值 1~1000000
  - shop_id INT：店铺 ID，随机取值 1~100
  - amount DOUBLE：订单金额，随机取值 0~1000
  - event_time：使用 LOCALTIMESTAMP 生成事件时间，设置 WATERMARK 延迟 10 秒
  - 生成速率：rows-per-second = 5000

**输出表定义**：
- `console_output`（print 连接器）：
  - window_start TIMESTAMP：窗口开始时间
  - window_end TIMESTAMP：窗口结束时间
  - shop_id INT：店铺 ID
  - total_amount DOUBLE：订单总额
  - order_cnt BIGINT：订单数量
  - avg_amount DOUBLE：平均订单金额

**查询逻辑**：
- 窗口：HOP(event_time, INTERVAL '1' MINUTE, INTERVAL '5' MINUTE) 滑动窗口
  - 步长（slide）：1 分钟
  - 窗口大小（size）：5 分钟
- 分组：按 shop_id 和窗口分组
- 聚合：SUM(amount) 订单总额，COUNT(*) 订单数，AVG(amount) 平均金额
- 窗口函数：HOP_START / HOP_END 获取窗口起止时间

**输出要求**：
- 使用 INSERT INTO console_output 输出结果
- 输出字段顺序：window_start, window_end, shop_id, total_amount, order_cnt, avg_amount

## Reference Solution
-- flinksql_010: 滑动窗口(HOP)店铺统计
-- datagen 生成订单流 @ 5000 rows/sec, HOP 1min slide 5min window

-- 1. 订单流 datagen 源表
CREATE TABLE order_source (
    order_id BIGINT,
    shop_id INT,
    amount DOUBLE,
    event_time AS LOCALTIMESTAMP,
    WATERMARK FOR event_time AS event_time - INTERVAL '10' SECOND
) WITH (
    'connector' = 'datagen',
    'rows-per-second' = '5000',
    'fields.order_id.kind' = 'random',
    'fields.order_id.min' = '1',
    'fields.order_id.max' = '1000000',
    'fields.shop_id.kind' = 'random',
    'fields.shop_id.min' = '1',
    'fields.shop_id.max' = '100',
    'fields.amount.kind' = 'random',
    'fields.amount.min' = '0',
    'fields.amount.max' = '1000'
);

-- 2. Console 输出表
CREATE TABLE console_output (
    window_start TIMESTAMP,
    window_end TIMESTAMP,
    shop_id INT,
    total_amount DOUBLE,
    order_cnt BIGINT,
    avg_amount DOUBLE
) WITH (
    'connector' = 'print'
);

-- 3. HOP 滑动窗口聚合: 按店铺统计订单总额、数量和平均金额
INSERT INTO console_output
SELECT
    HOP_START(event_time, INTERVAL '1' MINUTE, INTERVAL '5' MINUTE) AS window_start,
    HOP_END(event_time, INTERVAL '1' MINUTE, INTERVAL '5' MINUTE) AS window_end,
    shop_id,
    SUM(amount) AS total_amount,
    COUNT(*) AS order_cnt,
    AVG(amount) AS avg_amount
FROM order_source
GROUP BY HOP(event_time, INTERVAL '1' MINUTE, INTERVAL '5' MINUTE), shop_id;
# Session Window User Behavior Statistics (FlinkSQL)

## Prompt
I need you to write a Flink SQL that uses the built-in datagen connector to simulate a user behavior data source, computes per-user behavioral statistics over a 30-minute session window (`SESSION`), and outputs the results to the console.

**Business Background and Objective**: Simulate a user behavior stream using a datagen built-in table. Group by a 30-minute session window to compute the total action count (`COUNT`) and the number of distinct action types (`COUNT DISTINCT`) per user within each session window. Output the results to the console.

**Source Table Definition**:
- `user_actions` (user behavior stream, datagen connector):
  - `user_id INT`: User ID, randomly generated in the range 1–5000
  - `action_id INT`: Action type ID, randomly generated in the range 1–10
  - `event_time`: Uses `LOCALTIMESTAMP` to generate event time, with a WATERMARK delay of 5 seconds
  - Generation rate: `rows-per-second = 2000`

**Output Table Definition**:
- `console_output` (print connector):
  - `user_id INT`: User ID
  - `session_start TIMESTAMP(3)`: Session window start time
  - `session_end TIMESTAMP(3)`: Session window end time
  - `action_count BIGINT`: Total action count
  - `unique_actions BIGINT`: Number of distinct action types

**SQL Logic**:
- Apply `SESSION(event_time, INTERVAL '30' MINUTE)` session window on `user_actions`
- Group by `SESSION(event_time, INTERVAL '30' MINUTE)` and `user_id`
- Aggregate `COUNT(*) AS action_count` and `COUNT(DISTINCT action_id) AS unique_actions`
- Use `SESSION_START` and `SESSION_END` functions to obtain the window start and end times

**Output Requirements**:
- Use `INSERT INTO console_output` to output the results.
- Output field order: `user_id`, `session_start`, `session_end`, `action_count`, `unique_actions`

## Reference Solution
-- flinksql_012: 会话窗口用户行为统计
-- 30-min SESSION + COUNT + COUNT DISTINCT

-- 1. 用户行为 datagen 源表
CREATE TABLE user_actions (
    user_id INT,
    action_id INT,
    event_time AS LOCALTIMESTAMP,
    WATERMARK FOR event_time AS event_time - INTERVAL '5' SECOND
) WITH (
    'connector' = 'datagen',
    'rows-per-second' = '2000',
    'fields.user_id.kind' = 'random',
    'fields.user_id.min' = '1',
    'fields.user_id.max' = '5000',
    'fields.action_id.kind' = 'random',
    'fields.action_id.min' = '1',
    'fields.action_id.max' = '10'
);

-- 2. Console 输出表
CREATE TABLE console_output (
    user_id INT,
    session_start TIMESTAMP(3),
    session_end TIMESTAMP(3),
    action_count BIGINT,
    unique_actions BIGINT
) WITH (
    'connector' = 'print'
);

-- 3. SESSION 窗口聚合
INSERT INTO console_output
SELECT
    user_id,
    SESSION_START(event_time, INTERVAL '30' MINUTE) AS session_start,
    SESSION_END(event_time, INTERVAL '30' MINUTE) AS session_end,
    COUNT(*) AS action_count,
    COUNT(DISTINCT action_id) AS unique_actions
FROM user_actions
GROUP BY
    SESSION(event_time, INTERVAL '30' MINUTE),
    user_id;
# 累积窗口日活统计 (Cumulate UV) (FlinkSQL)

## Prompt
我需要你写一段 Flink SQL，用 datagen 内置连接器模拟用户登录数据源，通过累积窗口（CUMULATE）统计日活 UV 和登录次数，输出到 console。

**业务背景与目标**：用 datagen 内置表模拟用户登录流水。使用 CUMULATE TVF 累积窗口，窗口步长 1 分钟、最大窗口 1 天，统计每个累积窗口内的独立用户数（UV）和登录总次数，输出到 console。

**源表定义**：
- `user_source`（用户登录流，datagen 连接器）：
  - user_id INT：用户 ID，随机取值 1~100000
  - device_id INT：设备 ID，随机取值 1~5
  - event_time：使用 LOCALTIMESTAMP 生成事件时间，设置 WATERMARK 延迟 5 秒
  - 生成速率：rows-per-second = 3000

**输出表定义**：
- `console_output`（print 连接器）：
  - window_start TIMESTAMP(3)：累积窗口起始时间
  - window_end TIMESTAMP(3)：累积窗口结束时间
  - uv BIGINT：独立用户数（去重 user_id）
  - login_cnt BIGINT：登录总次数

**SQL 逻辑**：
- 使用 CUMULATE TVF：TABLE(CUMULATE(TABLE user_source, DESCRIPTOR(event_time), INTERVAL '1' MINUTE, INTERVAL '1' DAY))
- 按 window_start 和 window_end 分组
- 聚合 COUNT(DISTINCT user_id) AS uv 和 COUNT(*) AS login_cnt

**输出要求**：
- 使用 INSERT INTO console_output 将结果输出
- 输出字段顺序：window_start, window_end, uv, login_cnt

## Reference Solution
-- flinksql_013: 累积窗口日活统计 (Cumulate UV)
-- CUMULATE(1min step, 1day max) + COUNT DISTINCT + COUNT

-- 1. 用户登录 datagen 源表
CREATE TABLE user_source (
    user_id INT,
    device_id INT,
    event_time AS LOCALTIMESTAMP,
    WATERMARK FOR event_time AS event_time - INTERVAL '5' SECOND
) WITH (
    'connector' = 'datagen',
    'rows-per-second' = '3000',
    'fields.user_id.kind' = 'random',
    'fields.user_id.min' = '1',
    'fields.user_id.max' = '100000',
    'fields.device_id.kind' = 'random',
    'fields.device_id.min' = '1',
    'fields.device_id.max' = '5'
);

-- 2. Console 输出表
CREATE TABLE console_output (
    window_start TIMESTAMP(3),
    window_end TIMESTAMP(3),
    uv BIGINT,
    login_cnt BIGINT
) WITH (
    'connector' = 'print'
);

-- 3. CUMULATE TVF 窗口聚合
INSERT INTO console_output
SELECT
    window_start,
    window_end,
    COUNT(DISTINCT user_id) AS uv,
    COUNT(*) AS login_cnt
FROM TABLE(
    CUMULATE(
        TABLE user_source,
        DESCRIPTOR(event_time),
        INTERVAL '1' MINUTE,
        INTERVAL '1' DAY
    )
)
GROUP BY window_start, window_end;
# Regular Join 曝光点击关联 (FlinkSQL)

## Prompt
我需要你写一段 Flink SQL，用两个 datagen 内置连接器分别模拟广告曝光流和广告点击流，通过 Regular Join（常规内连接）关联曝光与点击，输出到 console。

**业务背景与目标**：用两张 datagen 内置表分别模拟广告曝光和广告点击数据。Regular Join 不需要事件时间或 Watermark，直接按 user_id 和 ad_id 做内连接，将同一用户对同一广告的曝光和点击关联起来输出。

**源表定义**：
- `impression_source`（曝光流，datagen 连接器）：
  - imp_id BIGINT：曝光 ID，随机取值 1~10000000
  - user_id INT：用户 ID，随机取值 1~100000
  - ad_id INT：广告 ID，随机取值 1~500
  - 生成速率：rows-per-second = 5000
  - 注意：Regular Join 不需要定义 event_time 或 WATERMARK

- `click_source`（点击流，datagen 连接器）：
  - click_id BIGINT：点击 ID，随机取值 1~10000000
  - user_id INT：用户 ID，随机取值 1~100000
  - ad_id INT：广告 ID，随机取值 1~500
  - 生成速率：rows-per-second = 1000
  - 注意：Regular Join 不需要定义 event_time 或 WATERMARK

**输出表定义**：
- `console_output`（print 连接器）：
  - user_id INT：用户 ID
  - ad_id INT：广告 ID
  - imp_id BIGINT：曝光 ID
  - click_id BIGINT：点击 ID

**Join 逻辑**：
- 使用 INNER JOIN 关联曝光流和点击流
- 关联条件：i.user_id = c.user_id AND i.ad_id = c.ad_id
- 这是一个 Regular Join，不需要 Interval Join 的时间窗口条件
- 注意：Regular Join 的 state 会持续增长，在生产环境需注意 TTL 配置

**输出要求**：
- 使用 INSERT INTO console_output 将匹配结果输出
- 输出字段顺序：user_id, ad_id, imp_id, click_id

## Reference Solution
-- flinksql_015: Regular Join (广告曝光 + 点击)
-- 双源无 WATERMARK + INNER JOIN on user_id AND ad_id

-- 1. 曝光流 datagen 源表 (无 event_time, 无 WATERMARK)
CREATE TABLE impression_source (
    imp_id BIGINT,
    user_id INT,
    ad_id INT
) WITH (
    'connector' = 'datagen',
    'rows-per-second' = '5000',
    'fields.imp_id.kind' = 'random',
    'fields.imp_id.min' = '1',
    'fields.imp_id.max' = '10000000',
    'fields.user_id.kind' = 'random',
    'fields.user_id.min' = '1',
    'fields.user_id.max' = '100000',
    'fields.ad_id.kind' = 'random',
    'fields.ad_id.min' = '1',
    'fields.ad_id.max' = '500'
);

-- 2. 点击流 datagen 源表 (无 event_time, 无 WATERMARK)
CREATE TABLE click_source (
    click_id BIGINT,
    user_id INT,
    ad_id INT
) WITH (
    'connector' = 'datagen',
    'rows-per-second' = '1000',
    'fields.click_id.kind' = 'random',
    'fields.click_id.min' = '1',
    'fields.click_id.max' = '10000000',
    'fields.user_id.kind' = 'random',
    'fields.user_id.min' = '1',
    'fields.user_id.max' = '100000',
    'fields.ad_id.kind' = 'random',
    'fields.ad_id.min' = '1',
    'fields.ad_id.max' = '500'
);

-- 3. Console 输出表
CREATE TABLE console_output (
    user_id INT,
    ad_id INT,
    imp_id BIGINT,
    click_id BIGINT
) WITH (
    'connector' = 'print'
);

-- 4. Regular Join: 按 user_id + ad_id 关联曝光和点击
INSERT INTO console_output
SELECT
    i.user_id,
    i.ad_id,
    i.imp_id,
    c.click_id
FROM impression_source i
INNER JOIN click_source c
    ON i.user_id = c.user_id
    AND i.ad_id = c.ad_id;
# 滚动窗口多维聚合 (GROUPING SETS) (FlinkSQL)

## Prompt
我需要你写一段 Flink SQL，用 datagen 内置连接器模拟事件流，按 1 分钟滚动窗口做 GROUPING SETS 多维聚合，结果打到 console。

**业务背景与目标**：用 datagen 内置表生成设备事件流，共 5000 行/秒。字段包括 os_id（操作系统 ID，1~3）、device_id（设备 ID，1~20）、city_id（城市 ID，1~100）。使用 LOCALTIMESTAMP 作为事件时间，设置 5 秒 Watermark 延迟。

按 1 分钟 TUMBLE 滚动窗口，使用 GROUPING SETS 进行多维分组聚合，分组组合包括：(os_id, device_id)、(city_id)、以及全局汇总()。统计每个维度组合的事件数 COUNT(*)，输出到 console 表。

**源表定义**：
- `event_source`（datagen 连接器）：
  - os_id INT：操作系统 ID，随机取值 1~3
  - device_id INT：设备 ID，随机取值 1~20
  - city_id INT：城市 ID，随机取值 1~100
  - event_time：使用 LOCALTIMESTAMP 生成事件时间，设置 WATERMARK 延迟 5 秒
  - 生成速率：rows-per-second = 5000

**输出表定义**：
- `console_output`（print 连接器）：
  - window_start TIMESTAMP(3)：窗口起始时间
  - os_id INT：操作系统 ID（聚合维度可为 NULL）
  - device_id INT：设备 ID（聚合维度可为 NULL）
  - city_id INT：城市 ID（聚合维度可为 NULL）
  - event_cnt BIGINT：事件计数

**聚合逻辑**：
- 滚动窗口：TUMBLE 1 分钟
- 分组方式：GROUPING SETS ((os_id, device_id), (city_id), ())
- 聚合指标：COUNT(*) AS event_cnt
- 窗口起始时间用 TUMBLE_START 计算

**输出要求**：
- 使用 INSERT INTO console_output 将结果输出
- 输出字段顺序：window_start, os_id, device_id, city_id, event_cnt

## Reference Solution
-- flinksql_016: 滚动窗口多维聚合 (GROUPING SETS) — TVF syntax
-- 生成 5000 行/秒的设备事件流

-- 1. 设备事件流 datagen 源表
CREATE TABLE event_source (
    os_id INT,
    device_id INT,
    city_id INT,
    event_time AS LOCALTIMESTAMP,
    WATERMARK FOR event_time AS event_time - INTERVAL '5' SECOND
) WITH (
    'connector' = 'datagen',
    'rows-per-second' = '5000',
    'fields.os_id.kind' = 'random',
    'fields.os_id.min' = '1',
    'fields.os_id.max' = '3',
    'fields.device_id.kind' = 'random',
    'fields.device_id.min' = '1',
    'fields.device_id.max' = '20',
    'fields.city_id.kind' = 'random',
    'fields.city_id.min' = '1',
    'fields.city_id.max' = '100'
);

-- 2. Console 输出表
CREATE TABLE console_output (
    window_start TIMESTAMP(3),
    os_id INT,
    device_id INT,
    city_id INT,
    event_cnt BIGINT
) WITH (
    'connector' = 'print'
);

-- 3. TUMBLE TVF 1min + GROUPING SETS 多维聚合
INSERT INTO console_output
SELECT
    window_start,
    os_id,
    device_id,
    city_id,
    COUNT(*) AS event_cnt
FROM TABLE(
    TUMBLE(TABLE event_source, DESCRIPTOR(event_time), INTERVAL '1' MINUTE)
)
GROUP BY window_start, window_end,
    GROUPING SETS ((os_id, device_id), (city_id), ());
# Row-Level TopN (Windowless) (FlinkSQL)

## Prompt
I need you to write a Flink SQL that uses the built-in datagen connector to simulate a spending stream, retrieves the top 3 records by spending amount grouped by `country_id` without any window, and outputs the results directly to the console.

**Business Background and Objective**: Generate user spending records using a datagen built-in table at a rate of 1000 rows per second. Fields include `country_id` (country ID, 1–30), `user_id` (user ID, 1–100,000), and `cost_money` (spending amount, 0–10,000). No event time or watermark is required, since no window operations are involved.

Use the `ROW_NUMBER()` window function, partitioned by `country_id` and sorted by `cost_money` in descending order, keeping only the top 3 records per partition (`row_num <= 3`), and output the results directly to the console table. This is a continuous TopN query where each country's top 3 dynamically updates as new data flows in.

**Source Table Definition**:
- `spend_source` (datagen connector):
  - `country_id INT`: Country ID, randomly generated in the range 1–30
  - `user_id INT`: User ID, randomly generated in the range 1–100,000
  - `cost_money DOUBLE`: Spending amount, randomly generated in the range 0–10,000
  - Generation rate: `rows-per-second = 1000`

**Output Table Definition**:
- `console_output` (print connector):
  - `country_id INT`: Country ID
  - `user_id INT`: User ID
  - `cost_money DOUBLE`: Spending amount
  - `row_num BIGINT`: Row number

**TopN Logic**:
- Use `ROW_NUMBER() OVER (PARTITION BY country_id ORDER BY cost_money DESC)` to generate row numbers
- Apply `WHERE row_num <= 3` to keep the top 3 per country
- No window operation is required; no event time or watermark needs to be defined

**Output Requirements**:
- Use `INSERT INTO console_output` to output the results.
- Output field order: `country_id`, `user_id`, `cost_money`, `row_num`

## Reference Solution
-- flinksql_017: 行级 TopN (无窗口)
-- 生成 1000 行/秒的用户消费流

-- 1. 用户消费流 datagen 源表（无事件时间/Watermark）
CREATE TABLE spend_source (
    country_id INT,
    user_id INT,
    cost_money DOUBLE
) WITH (
    'connector' = 'datagen',
    'rows-per-second' = '1000',
    'fields.country_id.kind' = 'random',
    'fields.country_id.min' = '1',
    'fields.country_id.max' = '30',
    'fields.user_id.kind' = 'random',
    'fields.user_id.min' = '1',
    'fields.user_id.max' = '100000',
    'fields.cost_money.kind' = 'random',
    'fields.cost_money.min' = '0',
    'fields.cost_money.max' = '10000'
);

-- 2. Console 输出表
CREATE TABLE console_output (
    country_id INT,
    user_id INT,
    cost_money DOUBLE,
    row_num BIGINT
) WITH (
    'connector' = 'print'
);

-- 3. 行级 TopN: 每个 country_id 取 cost_money 最高的前 3 条
INSERT INTO console_output
SELECT
    country_id,
    user_id,
    cost_money,
    row_num
FROM (
    SELECT
        country_id,
        user_id,
        cost_money,
        ROW_NUMBER() OVER (
            PARTITION BY country_id
            ORDER BY cost_money DESC
        ) AS row_num
    FROM spend_source
) t
WHERE row_num <= 3;