Sql 服务器:分区数据集,同时保留行分组
Sql Server: Partition Dataset While Preserving Row Groupings
我有一个查询 returns 行根据两列分组,如下所示:
Student_ID Module_ID Assignment_date Assignment_grade
1 2 2010-01-01 C
1 2 2012-02-02 E
1 2 2013-03-02 A*
3 6 2013-03-02 B
3 6 2013-03-02 B
我试图通过并行处理结果集来提高性能。为此,我需要将结果集划分为大小接近相等的部分,同时确保行分组不会分布在多个分区中,因为分组对于处理很重要。 这必须使用多个查询来完成,而不是一个查询然后对结果进行分区,因为在查询数据后对行进行分区会产生太多开销(目的是分配每个查询SqlDataReader
).
通常,我会使用 ROW_NUMBER()
为行分配数字,然后向每个查询添加一个 WHERE 子句,类似于 WHERE row_number > lowerbound AND row_number < upperbound
但这可能会导致数据集在中间拆分分组。
如何确保我使用的每个查询都选择数据的不同部分,并且结果中的每个分组都包含分组中的所有行?
我想过使用 DENSE_RANK()
为每个分组分配一个 ID,但我不知道如何结合行号来使用它来实现我想要的结果。
本例中的理想结果是一个查询 returns this:
Student_ID Module_ID Assignment_date Assignment_grade
1 2 2010-01-01 C
1 2 2012-02-02 E
1 2 2013-03-02 A*
另一个查询 returns 这个:
Student_ID Module_ID Assignment_date Assignment_grade
3 6 2013-03-02 B
3 6 2013-03-02 B
您可以使用WINDOWS函数如下:
WITH CTE AS (SELECT T.*,
COUNT(1) OVER () AS CNT,
ROW_NUMBER() OVER (ORDER BY STUDENT_ID, MODULE_ID, ASSIGNMENT_DATE) AS RN,
DENSE_RANK() OVER (ORDER BY STUDENT_ID, MODULE_ID) AS DR
FROM YOUR_TABLE T)
SELECT T1.*
FROM CTE T
-- WHERE DR <= (SELECT MAX(DR) FROM CTE T2 WHERE RN <= CEIL(CNT/2)) -- FIRST PART
-- WHERE DR >= (SELECT MIN(DR) FROM CTE T2 WHERE RN > CEIL(CNT/2)) -- SECOND PART
如果您想识别学生组,您可以使用 RANK()
或 DENSE_RANK()
:
select t.*, rank() over (order by student_id) as number_r
from t;
或:
select t.*, dense_rank() over (order by student_id) as number_dr
from t;
两者有什么区别? RANK()
产生对应于每个组的第一行号的计数。 DENSE_RANK()
仅按组统计。
因此,where number_r <= 3
将提取所有组,包括具有第三行的组。在您的情况下,这将是前三行。
但 where number_dr <= 3
将获取前三组。在您的情况下,它将 select 所有行 - 甚至下一组也不在数据示例中。
我有一个查询 returns 行根据两列分组,如下所示:
Student_ID Module_ID Assignment_date Assignment_grade
1 2 2010-01-01 C
1 2 2012-02-02 E
1 2 2013-03-02 A*
3 6 2013-03-02 B
3 6 2013-03-02 B
我试图通过并行处理结果集来提高性能。为此,我需要将结果集划分为大小接近相等的部分,同时确保行分组不会分布在多个分区中,因为分组对于处理很重要。 这必须使用多个查询来完成,而不是一个查询然后对结果进行分区,因为在查询数据后对行进行分区会产生太多开销(目的是分配每个查询SqlDataReader
).
通常,我会使用 ROW_NUMBER()
为行分配数字,然后向每个查询添加一个 WHERE 子句,类似于 WHERE row_number > lowerbound AND row_number < upperbound
但这可能会导致数据集在中间拆分分组。
如何确保我使用的每个查询都选择数据的不同部分,并且结果中的每个分组都包含分组中的所有行?
我想过使用 DENSE_RANK()
为每个分组分配一个 ID,但我不知道如何结合行号来使用它来实现我想要的结果。
本例中的理想结果是一个查询 returns this:
Student_ID Module_ID Assignment_date Assignment_grade
1 2 2010-01-01 C
1 2 2012-02-02 E
1 2 2013-03-02 A*
另一个查询 returns 这个:
Student_ID Module_ID Assignment_date Assignment_grade
3 6 2013-03-02 B
3 6 2013-03-02 B
您可以使用WINDOWS函数如下:
WITH CTE AS (SELECT T.*,
COUNT(1) OVER () AS CNT,
ROW_NUMBER() OVER (ORDER BY STUDENT_ID, MODULE_ID, ASSIGNMENT_DATE) AS RN,
DENSE_RANK() OVER (ORDER BY STUDENT_ID, MODULE_ID) AS DR
FROM YOUR_TABLE T)
SELECT T1.*
FROM CTE T
-- WHERE DR <= (SELECT MAX(DR) FROM CTE T2 WHERE RN <= CEIL(CNT/2)) -- FIRST PART
-- WHERE DR >= (SELECT MIN(DR) FROM CTE T2 WHERE RN > CEIL(CNT/2)) -- SECOND PART
如果您想识别学生组,您可以使用 RANK()
或 DENSE_RANK()
:
select t.*, rank() over (order by student_id) as number_r
from t;
或:
select t.*, dense_rank() over (order by student_id) as number_dr
from t;
两者有什么区别? RANK()
产生对应于每个组的第一行号的计数。 DENSE_RANK()
仅按组统计。
因此,where number_r <= 3
将提取所有组,包括具有第三行的组。在您的情况下,这将是前三行。
但 where number_dr <= 3
将获取前三组。在您的情况下,它将 select 所有行 - 甚至下一组也不在数据示例中。