寻找一种更高效的方法来检索SQL中组的前两行

问题描述 投票:0回答:1

我正在寻找一种高效的方法来检索SQL中每组数据的前两行。我有一个非常大的数据表(大约100亿行)。每行数据由四个维度(构成主键)描述,表格由一个维度(主键的最后一列)分区。

-- Medium table (2 to 3 million rows)
CREATE TABLE [smallDatabase].[dbo].[dimTableA] (
    [colA] [int] NOT NULL PRIMARY KEY
    ,[valueA] [int]
);

-- Small table (<1000 rows)
CREATE TABLE [smallDatabase].[dbo].[dimTableB] (
    [colB] [int] NOT NULL PRIMARY KEY
    ,[valueB] [int]
);

-- Small table (<10000 rows)
CREATE TABLE [smallDatabase].[dbo].[dimTableC] (
    [colC] [int] NOT NULL PRIMARY KEY
    ,[valueC] [int]
);

-- Small table (100 to 200 rows)
CREATE TABLE [smallDatabase].[dbo].[dimTableD] (
    [colD] [int] NOT NULL PRIMARY KEY
    ,[grouperD] [int] NOT NULL
    ,[dateD] [date]
);

CREATE PARTITION FUNCTION [pfColD](int) AS RANGE RIGHT FOR VALUES (1, 2, 3, ..., n);
CREATE PARTITION SCHEME [psColD] AS PARTITION [pfColD] TO ([PRIMARY], [PRIMARY], [PRIMARY], ..., [PRIMARY]);

-- Large table (~10 billion rows)
CREATE TABLE [bigDatabase].[dbo].[factBigTable] (
    [colA] [int] NOT NULL
    ,[colB] [int] NOT NULL
    ,[colC] [int] NOT NULL
    ,[colD] [int] NOT NULL
    ,[datum] [float] NULL
    ,PRIMARY KEY (
        [colA] ASC
        ,[colB] ASC
        ,[colC] ASC
        ,[colD] ASC
    )
) ON psColD([colD]);

另一个要求是我只需要随时为数据子集执行此操作。要表示需要定位的数据,可以使用临时表进行过滤。

CREATE TABLE #filter (
    [colA] [int] NOT NULL
    ,[colB] [int] NOT NULL
    ,PRIMARY KEY (
        [colA] ASC
        ,[colB] ASC
    )
);

我在网上找到了一些其他解决方案,建议使用行号选择前2位,如下所示:

-- Get the most recent two data points for each group of data
SELECT *
FROM (
    SELECT big.*
        ,dimD.[grouperD]
        ,ROW_NUMBER() OVER (
            PARTITION BY dimD.[grouperD], big.[colA], big.[colB], big.[colC]
            ORDER BY dimD.[dateD] DESC
        ) AS rowNumber
    FROM [bigDatabase].[dbo].[factBigTable] AS big
    INNER JOIN [smallDatabase].[dbo].[dimTableD] AS dimD
        ON big.[colD] = dimD.[colD]
    INNER JOIN #filter
        ON big.[colA] = #filter.[colA]
            AND big.[colB] = #filter.[colB]
) AS bigDataRanked
WHERE rowNumber <= 2;

这确实让我得到了我正在寻找的确切数据;然而,它的速度非常慢!

我此时已经尝试了很多不同的解决方案,但都表现得比我想要的慢。值得注意的是,由于数据的性质,并非每个维度组合都有数据。有些组合非常稀疏。

我试过的一种算法看起来很棒,但由于数据的稀疏性,最终表现得非常慢。这个想法是:

  1. 缓存每个组的列表。即,[grouperD],[colA],[colB]和[colC]。跟踪为每个组找到的行。
  2. 光标在[colD]上方,由[dateD]排序。当每个组找到2行时停止。
  3. 从[factBigTable]中选择与找到的行小于2的组匹配的行。缓存结果。
  4. 对于缓存的结果,增加了找到的行数。
  5. 将缓存的结果移动到临时表以供以后使用。
  6. 继续循环下一个[colD]。

每个循环执行得相对较快,因为SQL能够在大多数查询中使用PK搜索。但是,我的一些组的max [colD]非常低,因此循环必须多次迭代。

到目前为止,我发现的最快的解决方案在纸面上看起来很糟糕,但最终表现最好。然而;它仍然比我想要的慢,而且它的扩展性非常差。

  1. 对于我们关心的数据子集(即加入过滤器),缓存每个组的所有主键。
  2. 选择并缓存每个组的最大[colD]。
  3. 从PK按组列表中删除最大值。
  4. 再次选择并缓存每个组的最大[colD]。获得第二个到最大[colD]。
  5. 使用max和second到max cached键来查找我们需要的所有行。

有没有人对如何快速检索我正在寻找的行有任何其他想法?绝不是需要在单个查询中完成。我可以根据需要使用尽可能多的临时表或临时表来快速获取数据。此外,我愿意添加索引或其他数据模型更改。我不愿意 - 因为表格太大,任何改变都可能意味着重要的存储考虑因素 - 但是,如果这是唯一的方法,那么我会让它发挥作用。

sql sql-server tsql database-performance
1个回答
0
投票

不能评论,不够大,但好奇,我们在谈论什么版本的sql server?

我怀疑它会更快(特别是如果我们在rowNumber <= 2之前说'数十亿行),但我总是喜欢将row_number()操作卸载到一个较小的子集(如果能够的话)。

;with bigDataRanked as (
SELECT big.*
        ,dimD.[grouperD]
        ,dimD.[dateD]
    FROM [bigDatabase].[dbo].[factBigTable] AS big
    INNER JOIN [smallDatabase].[dbo].[dimTableD] AS dimD
        ON big.[colD] = dimD.[colD]
    INNER JOIN #filter
        ON big.[colA] = #filter.[colA]
            AND big.[colB] = #filter.[colB]
) 
select bdr.*, ROW_NUMBER() OVER (
            PARTITION BY bdr.[grouperD], bdr.[colA], bdr.[colB], bdr.[colC]
            ORDER BY bdr.[dateD] DESC) rowNumber
from bigDataRanked bdr -- will have an additional column dateD returned from dimTableD (from bdr.*)
where rowNumber <= 2;
© www.soinside.com 2019 - 2024. All rights reserved.