我正在寻找一种高效的方法来检索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;
这确实让我得到了我正在寻找的确切数据;然而,它的速度非常慢!
我此时已经尝试了很多不同的解决方案,但都表现得比我想要的慢。值得注意的是,由于数据的性质,并非每个维度组合都有数据。有些组合非常稀疏。
我试过的一种算法看起来很棒,但由于数据的稀疏性,最终表现得非常慢。这个想法是:
每个循环执行得相对较快,因为SQL能够在大多数查询中使用PK搜索。但是,我的一些组的max [colD]非常低,因此循环必须多次迭代。
到目前为止,我发现的最快的解决方案在纸面上看起来很糟糕,但最终表现最好。然而;它仍然比我想要的慢,而且它的扩展性非常差。
有没有人对如何快速检索我正在寻找的行有任何其他想法?绝不是需要在单个查询中完成。我可以根据需要使用尽可能多的临时表或临时表来快速获取数据。此外,我愿意添加索引或其他数据模型更改。我不愿意 - 因为表格太大,任何改变都可能意味着重要的存储考虑因素 - 但是,如果这是唯一的方法,那么我会让它发挥作用。
不能评论,不够大,但好奇,我们在谈论什么版本的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;