【问题标题】:Is there a way to split the results of a select query into two equal halfs?有没有办法将选择查询的结果分成相等的两半?
【发布时间】:2010-05-05 19:01:32
【问题描述】:

我需要 Sql Server 2005 中的选择查询解决方案。

我想要一个返回两个 ResultSet 的查询,每个 ResultSet 恰好包含符合特定条件的所有记录的一半。我尝试将 TOP 50 PERCENT 与 Order By 结合使用,但如果表中的记录数为奇数,则两个结果集中都会显示一条记录。我不想在记录集上复制任何记录。示例:

我有一个简单的表,其中包含 TheID (PK) 和 TheValue 字段 (varchar(10)) 以及 5 条记录。现在跳过 where 子句。

SELECT TOP 50 PERCENT * FROM TheTable ORDER BY TheID asc

产生所选 id 的 1,2,3

SELECT TOP 50 PERCENT * FROM TheTable ORDER BY TheID desc

产生所选 id 的 3,4,5

3 是一个重复。当然,在现实生活中,查询相当复杂,包含大量 where 子句和子查询。

【问题讨论】:

  • 谁在使用这两个查询?如果两个消费者都知道彼此(可能是通过他们的结果),为什么不获取整个列表并将其划分到消费者端,这样就没有重复了?
  • 是的。对不起,我忘了提。
  • 你能提供一个更好的例子吗?同一张表上的两个查询返回 50% 的表内容是……整个表。我也不清楚是否接受重复。
  • 另外,您在最终输出中谈论了多少行?这可能会使某些解决方案比其他解决方案更好。
  • 你能有 2 个不相等的一半吗?

标签: sql sql-server result-partitioning


【解决方案1】:

SQL Server 2005 和类似版本:

select *, ntile(2) over(order by theid) as tile_nr from thetable

ntile(n) 将输出分配到 n 个段中,每个段的大小相同(当行数不能被 n 整除时,进行舍入或舍入)。所以这会产生输出:

1 | value1 | 1
2 | value2 | 1
3 | value3 | 1
4 | value4 | 2
5 | value5 | 2

如果只需要上半部分或下半部分,则需要将其放入子查询中,例如:

select theid, thevalue from (
  select theid, thevalue, ntile(2) over(order by theid) as tile_nr from thetable
) x
where x.tile_nr = 1

将返回上半部分,同样使用x.tile_nr = 2 作为下半部分

【讨论】:

  • +1,在我的系统上进行了测试。 @Thomas,没有partition by,只有order by
  • @KM - 是的,也在我的系统上测试过。这对我来说是假设 SQL2k5+ 的最聪明的解决方案。
  • +1 确实是最常被忽视的排名功能之一!很好的答案
【解决方案2】:

您可以使用这两个查询:

SELECT * FROM (
    SELECT *, ROW_NUMBER() OVER (ORDER BY TheID) AS rn FROM TheTable
) T1
WHERE rn % 2 = 0

SELECT * FROM (
    SELECT *, ROW_NUMBER() OVER (ORDER BY TheID) AS rn FROM TheTable
) T1
WHERE rn % 2 = 1

【讨论】:

  • 绝对是一个可行的答案。但这不是条纹分布的一半吗?在 OP 中,他得到的是“上半部分”和“下半部分”,而不是“两个不合格的半部分”。
  • @Mark Canlas:你是对的。我对这个问题的解读是,只要行数正确,每条记录在哪一半都无关紧要。但是你有一点,这个问题可以用任何一种方式来解释。
【解决方案3】:

如果这是 SQL Server 2000,那么我倾向于像这样找到中间值的 PK:

Declare @MiddleId int

Set @MiddleId = (
                Select TOP 1 PK
                From (
                        Select TOP 50 PERCENT PK
                        From Table
                        Order By TheId ASC
                        )
                Order By TheId DESC
                )

Select ...
From Table
Where TheId <= @MiddleId

Select ..
From Table
Where TheId > @MiddleId

对于 SQL Server 2005,我倾向于这样做,但您可以使用 CTE

;With NumProjects As
    (
    Select Id, ROW_NUMBER() OVER (ORDER BY TheId ASC ) As Num
    From Table
    )
Select @MiddleId = Id
From Table
Where Num = CEILING( (Select Count(*) From Table) / 2 )

【讨论】:

    【解决方案4】:

    试试这个:

    DECLARE @CountOf int,@Top int,@Bottom int
    SELECT @CountOf=COUNT(*) FROM YourTable
    SET @Top=@CountOf/2
    SET @Bottom=@CountOf-@Top
    SELECT TOP (@Top) * FROM YourTable ORDER BY 1 asc --assumes column 1 is your PK
    SELECT TOP (@Bottom) * FROM YourTable ORDER BY 1 desc --assumes column 1 is your PK
    

    【讨论】:

      【解决方案5】:

      这是另一个解决方案:

      您需要使用临时表来保存前 50%,如下所示:

      select top 50 percent * 
      into #YourTempTable
      from TheTable 
      
      -- The below would give the first half
      select * from #YourTempTable
      
      -- The below woud give rest of the half 
      select * from TheTable where TheID not in (select TheID from #YourTempTable)
      

      【讨论】:

        【解决方案6】:

        这是the query 我觉得很有用(当然修改后):

        DECLARE @numberofitemsperpage INT DECLARE @numberofpages INT DECLARE @currentpage int

        DECLARE @countRecords float SET @countRecords = (Select COUNT(*) From sz_hold_visitsData) -- Excel 一次可以保存大约一百万条记录。如果@countRecords >= 1000000 SET @numberofitemsperpage = 500000 ELSE IF @countRecords = 500000 设置 @numberofitemsperpage = 250000 ELSE IF @countRecords = 100000 设置 @numberofitemsperpage = 50000 其他设置 @numberofitemsperpage = 10000

        DECLARE @numberofpages_deci float SET @numberofpages_deci = @countRecords / @numberofitemsperpage

        SET @numberofpages = CEILING(@numberofpages_deci) 选择 @countRecords AS countRecords, @numberofitemsperpage AS numberofitemsperpage , @numberofpages_deci AS numberofpages_deci, @numberofpages AS numberofpagesFnl

        SET @currentpage =0 WHILE @currentpage = @currentpage * @numberofitemsperpage +1 AND Row

        如果@@ROWCOUNT = 0 BREAK SET @currentpage = @currentpage +1 END

        在此摘录中,“sz_hold_visitsData”是我数据库中的一个表,而“person_ID”是其中的一个列。 您还可以进一步修改脚本以输出到文件:

        声明 @numberofitemsperpage INT DECLARE @numberofpages INT DECLARE @currentpage int

        DECLARE @countRecords float SET @countRecords = (Select COUNT(*) From sz_hold_visitsData) -- Excel 一次可以保存大约一百万条记录。如果@countRecords >= 1000000 SET @numberofitemsperpage = 500000 ELSE IF @countRecords = 500000 设置 @numberofitemsperpage = 250000 ELSE IF @countRecords = 100000 设置 @numberofitemsperpage = 50000 其他设置 @numberofitemsperpage = 10000

        DECLARE @numberofpages_deci float SET @numberofpages_deci = @countRecords / @numberofitemsperpage

        SET @numberofpages = CEILING(@numberofpages_deci) 选择 @countRecords AS countRecords, @numberofitemsperpage AS numberofitemsperpage , @numberofpages_deci AS numberofpages_deci, @numberofpages AS numberofpagesFnl

        DECLARE @sevrName nvarchar(50) SET @sevrName = '.\sql14' 声明 @outputFile nvarchar(500)

        SET @currentpage =0 WHILE @currentpage = @currentpage * @numberofitemsperpage +1 AND Row

        声明@cmd_varchar(500) = 'sqlcmd -S' + @sevrName + ' -E -Q "SELECT a.* FROM (SELECT row_number() OVER (ORDER BY person_ID) AS ROW, * FROM sz_hold_visitsData) WHERE ROW >= '+ CONVERT(nvarchar(500),@currentpage * @numberofitemsperpage +1) +' AND 行

        如果@@ROWCOUNT = 0 BREAK SET @currentpage = @currentpage +1 END

        希望有所帮助。

        【讨论】:

        • 请按照规定的方式标记您的代码 - 留一个空行,每行代码缩进 4 个空格。
        猜你喜欢
        • 2021-09-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多