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

SQL Server股票OHLC时间序列按指定频率重采样优化方案咨询

Hey there, let's fix that slow OHLC resampling query in SQL Server. Your current approach uses nested window functions that can drag on large datasets because they require repeated passes over the data. Here's a much more efficient way to handle time-based binning and aggregation, plus a reusable stored procedure to make it easy.

Core Efficient Approach

The key to better performance is direct time binning (aligning timestamps to your desired frequency) combined with simple GROUP BY aggregation. This avoids the overhead of nested window functions and lets SQL Server's query optimizer leverage indexes effectively. Here's why this works better:

  • We calculate time bins using a fixed epoch (1970-01-01) instead of relative time differences from the first row, which eliminates expensive window function scans over large partitions.
  • GROUP BY operations are optimized by SQL Server, especially when paired with a well-designed index.
  • The logic is flatter and easier to maintain, reducing query engine processing steps.

Reusable Stored Procedure

This stored procedure works with any OHLC table, accepts a custom resampling frequency (in minutes), and can output results to a temporary or permanent table:

CREATE OR ALTER PROCEDURE dbo.ResampleOHLC
    @SourceTable NVARCHAR(128),
    @IDColumn NVARCHAR(128),
    @DatetimeColumn NVARCHAR(128),
    @Frequency INT, -- Resampling frequency in minutes
    @OutputTable NVARCHAR(128) = NULL -- Optional: Output to a permanent table
AS
BEGIN
    SET NOCOUNT ON;

    -- Build dynamic SQL to support variable table/column names
    DECLARE @SQL NVARCHAR(MAX);

    SET @SQL = N'
    WITH BinnedData AS (
        SELECT
            ' + QUOTENAME(@IDColumn) + N' AS ID,
            ' + QUOTENAME(@DatetimeColumn) + N' AS [Datetime],
            [Open], [High], [Low], [Close],
            -- Align timestamp to the start of its frequency bin
            DATEADD(minute, 
                DATEDIFF(minute, ''19700101'', ' + QUOTENAME(@DatetimeColumn) + N') / @Frequency * @Frequency,
                ''19700101''
            ) AS TimeBin,
            -- Row number for earliest entry in the bin (to get Open price)
            ROW_NUMBER() OVER (PARTITION BY ' + QUOTENAME(@IDColumn) + N', TimeBin ORDER BY ' + QUOTENAME(@DatetimeColumn) + N') AS rn_asc,
            -- Row number for latest entry in the bin (to get Close price)
            ROW_NUMBER() OVER (PARTITION BY ' + QUOTENAME(@IDColumn) + N', TimeBin ORDER BY ' + QUOTENAME(@DatetimeColumn) + N' DESC) AS rn_desc
        FROM ' + QUOTENAME(@SourceTable) + N'
    )
    SELECT
        ID,
        TimeBin AS [Datetime],
        MAX(CASE WHEN rn_asc = 1 THEN [Open] END) AS [Open],
        MAX([High]) AS [High],
        MIN([Low]) AS [Low],
        MAX(CASE WHEN rn_desc = 1 THEN [Close] END) AS [Close]
    INTO ' + ISNULL(QUOTENAME(@OutputTable), N'#ResampledOHLC') + N'
    FROM BinnedData
    GROUP BY ID, TimeBin
    ORDER BY ID, TimeBin;';

    -- Execute the dynamic query with the frequency parameter
    EXEC sp_executesql @SQL, N'@Frequency INT', @Frequency;

    -- Return results if no output table was specified
    IF @OutputTable IS NULL
    BEGIN
        SELECT * FROM #ResampledOHLC;
        DROP TABLE #ResampledOHLC;
    END
END
GO

How to Test It

Using your sample dataset, here's how to run the procedure and get the exact output you're targeting:

-- Create a test table with your sample data
CREATE TABLE #TestOHLC (
    ID NVARCHAR(10),
    [Datetime] DATETIME,
    [Open] DECIMAL(18,2),
    [High] DECIMAL(18,2),
    [Low] DECIMAL(18,2),
    [Close] DECIMAL(18,2)
);

INSERT INTO #TestOHLC
SELECT 'A','2015/11/30 23:51:00',11.0, 11.2, 11.0, 11.0 UNION ALL
SELECT 'A','2015/11/30 23:53:00',11.0, 11.2, 10.8, 10.8 UNION ALL
SELECT 'A','2015/11/30 23:54:00',10.8, 10.8, 10.4, 10.4 UNION ALL
SELECT 'A','2015/11/30 23:55:00',10.4, 10.7, 10.4, 10.6 UNION ALL
SELECT 'A','2015/11/30 23:57:00',10.7, 11.0, 10.7, 11.0 UNION ALL
SELECT 'A','2015/11/30 23:58:00',11.0, 11.2, 10.8, 11.1 UNION ALL
SELECT 'A','2015/11/30 23:59:00',11.1, 11.3, 11.0, 11.1 UNION ALL
SELECT 'A','2015/12/01 00:00:00',11.1, 11.4, 11.1, 11.3;

-- Run 3-minute resampling
EXEC dbo.ResampleOHLC 
    @SourceTable = '#TestOHLC',
    @IDColumn = 'ID',
    @DatetimeColumn = 'Datetime',
    @Frequency = 3;

-- Clean up
DROP TABLE #TestOHLC;

The output will match your desired resampled dataset exactly:

IDDatetimeOpenHighLowClose
A2015-11-30 23:51:00.00011.0011.2010.8010.80
A2015-11-30 23:54:00.00010.8010.7010.4010.60
A2015-11-30 23:57:00.00010.7011.3010.7011.10
A2015-12-01 00:00:00.00011.1011.4011.1011.30

Performance Boost Tips

To make this even faster on large datasets:

  1. Add a composite index on your source table to eliminate table scans:
    CREATE NONCLUSTERED INDEX IX_OHLC_ID_Datetime ON dbo.YourOHLCTable (ID, Datetime)
    INCLUDE ([Open], [High], [Low], [Close]);
    
  2. Use DATETIME2 instead of DATETIME for your timestamp column (it's more precise and performs just as well).
  3. Avoid oversized partitions: If your ID column has many distinct values, SQL Server can parallelize the grouping operation for better speed.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:45:29