# Order Stream and Payment Stream Interval Join (FlinkSQL)

## Prompt
I need you to write a Flink SQL that uses the built-in datagen connector to simulate an order stream and a payment stream, performing an Interval Join on `order_id`, and writing matched records directly to the console.

**Business Background and Objective**: Simulate an order stream and a payment stream using two datagen built-in tables, each generating 100 million records. The `order_id` and `user_id` in the order stream range from 1 to 1,000,000, and the `order_id` in the payment stream also ranges from 1 to 1,000,000. Both tables use `LOCALTIMESTAMP` as the event time and set a 5-second watermark delay.

Perform an Interval Join on the two tables using `order_id`, matching only records where the payment time falls within 5 minutes after the order time. Upon a successful match, output the original fields directly to the console table, including `order_id`, `user_id`, `pay_amount`, the order time, and the payment time.

**Source Table Definitions**:
- `orders_source` (order stream, datagen connector):
  - `order_id INT`: Order ID, randomly generated in the range 1–1,000,000
  - `user_id INT`: User ID, randomly generated in the range 1–1,000,000
  - `order_time`: Uses `LOCALTIMESTAMP` to generate event time, with a WATERMARK delay of 5 seconds
  - Generation rate: `rows-per-second = 1000`

- `payments_source` (payment stream, datagen connector):
  - `order_id INT`: Order ID, randomly generated in the range 1–1,000,000
  - `pay_amount DOUBLE`: Payment amount, randomly generated in the range 1–100,000
  - `pay_time`: Uses `LOCALTIMESTAMP` to generate event time, with a WATERMARK delay of 5 seconds
  - Generation rate: `rows-per-second = 1000`

**Output Table Definition**:
- `console_output` (print connector):
  - `order_id INT`
  - `user_id INT`
  - `pay_amount DOUBLE`
  - `order_time TIMESTAMP(3)`
  - `pay_time TIMESTAMP(3)`

**Join Logic**:
- Join condition: `orders_source.order_id = payments_source.order_id`
- Time window: `payments_source.pay_time BETWEEN orders_source.order_time AND orders_source.order_time + INTERVAL '5' MINUTE`
- Note: When writing to the sink, you must handle the dual rowtime attribute conflict by applying `CAST(pay_time AS TIMESTAMP(3))` to downgrade `pay_time` to a plain timestamp.

**Output Requirements**:
- Use `INSERT INTO console_output` to output the matched results.
- Output field order: `order_id`, `user_id`, `pay_amount`, `order_time`, `pay_time`

## Reference Solution
-- online_001: 订单流 + 支付流 Interval Join
-- 每个源生成 1 亿条数据 @ 1000 rows/sec

-- 1. 订单流 datagen 源表
CREATE TABLE orders_source (
    order_id INT,
    user_id INT,
    order_time AS LOCALTIMESTAMP,
    WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND
) WITH (
    'connector' = 'datagen',
    'rows-per-second' = '1000',
    'fields.order_id.kind' = 'random',
    'fields.order_id.min' = '1',
    'fields.order_id.max' = '1000000',
    'fields.user_id.kind' = 'random',
    'fields.user_id.min' = '1',
    'fields.user_id.max' = '1000000'
);

-- 2. 支付流 datagen 源表
CREATE TABLE payments_source (
    order_id INT,
    pay_amount DOUBLE,
    pay_time AS LOCALTIMESTAMP,
    WATERMARK FOR pay_time AS pay_time - INTERVAL '5' SECOND
) WITH (
    'connector' = 'datagen',
    'rows-per-second' = '1000',
    'fields.order_id.kind' = 'random',
    'fields.order_id.min' = '1',
    'fields.order_id.max' = '1000000',
    'fields.pay_amount.kind' = 'random',
    'fields.pay_amount.min' = '1',
    'fields.pay_amount.max' = '100000'
);

-- 3. Console 输出表
CREATE TABLE console_output (
    order_id INT,
    user_id INT,
    pay_amount DOUBLE,
    order_time TIMESTAMP(3),
    pay_time TIMESTAMP(3)
) WITH (
    'connector' = 'print'
);

-- 4. Interval Join: 匹配支付时间在订单时间后 5 分钟内的记录
INSERT INTO console_output
SELECT
    o.order_id,
    o.user_id,
    p.pay_amount,
    o.order_time,
    CAST(p.pay_time AS TIMESTAMP(3)) AS pay_time
FROM orders_source o
JOIN payments_source p
    ON o.order_id = p.order_id
WHERE
    p.pay_time BETWEEN o.order_time AND o.order_time + INTERVAL '5' MINUTE;