You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将事件型表转时间序列:SQL/Django ORM实现方案问询

解决方案

一、SQLite 原生SQL实现

  • 步骤1:获取每个账户每日最后一次策略变更
    从事件表中筛选每个账户每天的最后一次变更记录,用窗口函数ROW_NUMBER()按账户和日期分组,按变更时间降序排序,取每组第一条:

    WITH daily_last_changes AS (
        SELECT
            account_id,
            DATE(change_time) AS change_date,
            strategy_id,
            ROW_NUMBER() OVER (
                PARTITION BY account_id, DATE(change_time)
                ORDER BY change_time DESC
            ) AS rn
        FROM account_strategy_changes
    )
    SELECT account_id, change_date, strategy_id
    FROM daily_last_changes
    WHERE rn = 1
    
  • 步骤2:生成目标日期范围的序列
    SQLite没有内置日期序列生成函数,用递归CTE生成所需日期区间(替换'2023-01-01'和'2023-12-31'为实际起止日期):

    WITH date_series AS (
        SELECT '2023-01-01' AS target_date
        UNION ALL
        SELECT DATE(target_date, '+1 day')
        FROM date_series
        WHERE target_date < '2023-12-31'
    )
    
  • 步骤3:关联账户、日期序列并填充缺失值
    结合前两个CTE,先获取所有账户与日期序列的笛卡尔积,左连接每日最后变更记录,最后用LAST_VALUE()窗口函数向前填充缺失的策略值:

    WITH daily_last_changes AS (
        SELECT
            account_id,
            DATE(change_time) AS change_date,
            strategy_id,
            ROW_NUMBER() OVER (
                PARTITION BY account_id, DATE(change_time)
                ORDER BY change_time DESC
            ) AS rn
        FROM account_strategy_changes
    ),
    date_series AS (
        SELECT '2023-01-01' AS target_date
        UNION ALL
        SELECT DATE(target_date, '+1 day')
        FROM date_series
        WHERE target_date < '2023-12-31'
    ),
    all_account_dates AS (
        SELECT a.id AS account_id, ds.target_date
        FROM accounts a
        CROSS JOIN date_series ds
    )
    SELECT
        account_id,
        target_date,
        LAST_VALUE(strategy_id IGNORE NULLS) OVER (
            PARTITION BY account_id
            ORDER BY target_date
            ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
        ) AS current_strategy
    FROM all_account_dates
    LEFT JOIN daily_last_changes dlc
        ON all_account_dates.account_id = dlc.account_id
        AND all_account_dates.target_date = dlc.change_date
    ORDER BY account_id, target_date;
    

    注意:替换accounts为你的账户表实际名称;若无需包含从未有过变更的账户,可调整all_account_dates逻辑,仅保留有变更记录的账户。

二、Django ORM 实现

  • 步骤1:获取每个账户每日最后一次变更
    使用Django的Window和RowNumber函数筛选每日最后一次变更:

    from django.db.models import Window, F
    from django.db.models.functions import RowNumber, TruncDate
    
    daily_last_changes = AccountStrategyChange.objects.annotate(
        change_date=TruncDate('change_time')
    ).annotate(
        rn=Window(
            expression=RowNumber(),
            partition_by=[F('account_id'), F('change_date')],
            order_by=F('change_time').desc()
        )
    ).filter(rn=1).values('account_id', 'change_date', 'strategy_id')
    
  • 步骤2:生成日期序列
    Django ORM无直接生成日期序列的方法,用Python生成目标日期范围:

    from datetime import date, timedelta
    
    start_date = date(2023, 1, 1)
    end_date = date(2023, 12, 31)
    date_range = [start_date + timedelta(days=i) for i in range((end_date - start_date).days + 1)]
    
  • 步骤3:关联并填充缺失值
    由于Django ORM对LAST_VALUE的IGNORE NULLS支持有限,可直接用RawSQL执行完整SQL逻辑(替换表名为你的Django模型对应数据库表名):

    from django.db.models import RawSQL
    
    result = Account.objects.raw("""
    WITH daily_last_changes AS (
        SELECT
            account_id,
            DATE(change_time) AS change_date,
            strategy_id,
            ROW_NUMBER() OVER (
                PARTITION BY account_id, DATE(change_time)
                ORDER BY change_time DESC
            ) AS rn
        FROM account_strategy_changes
    ),
    date_series AS (
        SELECT '2023-01-01' AS target_date
        UNION ALL
        SELECT DATE(target_date, '+1 day')
        FROM date_series
        WHERE target_date < '2023-12-31'
    ),
    all_account_dates AS (
        SELECT a.id AS account_id, ds.target_date
        FROM accounts a
        CROSS JOIN date_series ds
    )
    SELECT
        account_id,
        target_date,
        LAST_VALUE(strategy_id IGNORE NULLS) OVER (
            PARTITION BY account_id
            ORDER BY target_date
            ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
        ) AS current_strategy
    FROM all_account_dates
    LEFT JOIN daily_last_changes dlc
        ON all_account_dates.account_id = dlc.account_id
        AND all_account_dates.target_date = dlc.change_date
    ORDER BY account_id, target_date
    """)
    

内容的提问来源于stack exchange,提问作者Durand

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 04:40:43