在父/子查询中对SQL Server中的父级进行分页

时间:2018-05-24 16:29:53

标签: sql-server tsql sql-server-2012

(SQL Server 2012 - 网络版)

我在查询中有一个父/子(一对多)关系:

SELECT a.a, a.b, b.c
FROM tablea INNER JOIN 
tableb ON b.pk = a.fk

我有一个巨大的分页查询,包含使用标准(伪代码):

WITH C as (SELECT top(@perpage*@pagenum) rowID = row_number() OVER (somefield)),
 SELECT c.* FROM C (query) WHERE DT_RowId > (@pagenum-1)*@perpage

我遇到的问题是在这种情况下是否可以对父表(a)进行分页,而不是整个查询?我可以修改我的分页查询(而不是自己提取查询的sql),这样当我要求10行时,它会从父项中获取10行,并附加“x”个子项吗?

我知道我不是在这里提供更大的图片,但更大的图片是丑陋的。如果需要,我们可以去那里,但它就在那里。这是我们对此的一个小尝试:

    IF UPPER(LEFT(@rSQL, 6)) = 'SELECT'
        BEGIN
            SET @rSQL = 'SELECT * FROM (' + @rSQL + ')' + ' as rTBL';
            SET @rSQL = RIGHT(@rSQL, LEN(@rSQL)-7);
            IF (LEN(LTRIM(@search)) > 0)
                BEGIN
                    SET @rPaging = 
                        'IF (@schemaonly=1) SET FMTONLY ON; 
                        SELECT @ttlrows = COUNT(*) FROM (SELECT ' + @rSQL + @rWhere + ') AS TBL; 
                        WITH C as (select top(@perpage*@pagenum) DT_RowId = ROW_NUMBER() OVER (' + @rOrder + '), ';
                    SET @rPaging = @rPaging + @rSQL + @rWhere + ')  
                        SELECT C.*' + @rcols + ', (@perpage-1) * @pagenum as pagenum, @ttlrows as ct, CEILING(@ttlrows / CAST(@perpage AS FLOAT)) as pages 
                        FROM C '+ @query + ' WHERE DT_RowId > (@pagenum-1) * @perpage ';
                END
            ELSE
                BEGIN

                    SET @rPaging = 
                        'IF (@schemaonly=1) SET FMTONLY ON; 
                        SELECT @ttlrows = COUNT(*) FROM (' + @oSQL + ') AS SUBQUERY; 
                        WITH C as (select top(@perpage*@pagenum) DT_RowId = ROW_NUMBER() OVER (' + @rOrder + '), ';
                    SET @rPaging = @rPaging + @rSQL + ')  
                        SELECT C.*' + @rcols + ',(@perpage-1) * @pagenum as pagenum, @ttlrows as ct, CEILING(@ttlrows / CAST(@perpage AS FLOAT)) as pages 
                        FROM C '+ @query + ' WHERE DT_RowId > (@pagenum-1) * @perpage ';
                END
            PRINT @rPaging;
            EXECUTE SP_EXECUTESQL @rPaging, @parms, @ttlrows out, @schemaonly, @perpage, @pagenum, @fksiteID, @filter1, @filter2, @filter3, @filter4, @intfilter1, @intfilter2, @intfilter3, @intfilter4, @datefilter1, @datefilter2, @search;
            SET FMTONLY OFF;
        END
    ELSE
        BEGIN
            SET @rSQL = LTRIM(REPLACE(UPPER(@rSQL), 'EXEC',''));
            EXECUTE SP_EXECUTESQL @rSQL, @parms, @ttlrows out, @schemaonly, @perpage, @pagenum, @fksiteID, @filter1, @filter2, @filter3, @filter4, @intfilter1, @intfilter2, @intfilter3, @intfilter4, @datefilter1, @datefilter2;
        END

2 个答案:

答案 0 :(得分:3)

您可以在仅获取父行的CTE中进行分页,然后在后续CTE或主查询中连接子行。

由于您使用此动态方式,这可能需要从您用于构建@query的相同构建块构建分页查询。如果没有看到构建@query的代码,我就不能比这更具体了。

答案 1 :(得分:3)

你可以添加

,DENSE_RANK() OVER (ORDER BY table_a.primary_key)

这将间接提供与

相同的结果
,ROW_NUMBER() OVER(ORDER BY table_a.primary_key) 

但是前者将在最终结果集上,而不是返回到表a中的后一个代码片段。

但请注意缺点:任何额外的排名功能都会对结果集强制执行额外的排序操作!这可能会显着影响查询性能。如果您的方案中出现这种情况,我建议您遵循Tab Allemans解决方案并使用cte。