【问题标题】:T-SQL get row count before TOP is appliedT-SQL 在应用 TOP 之前获取行数
【发布时间】:2013-04-17 01:22:58
【问题描述】:

我有一个 SELECT 可以从表中返回数百行(表可以是 ~50000 行)。我的应用程序有兴趣知道返回的行数,这对我来说很重要,但它实际上只使用了这数百行中的前 5 行。我想要做的是将 SELECT 查询限制为仅返回 5 行,但还要告诉我的应用程序将返回多少行(数百行)。这是原始查询:

SELECT id, a, b, c FROM table WHERE a < 2

这是我想出的 - CTE - 但我对每列中出现的总行数感到不舒服。理想情况下,我想要一个 TOP 5 的结果集和一个返回的总行数参数。

WITH Everything AS
(
   SELECT id, a, b, c FROM table
),
DetermineCount AS
(
   SELECT COUNT(*) AS Total FROM Everything 
)
SELECT TOP (5) id, a, b, c, Total
FROM Everything 
CROSS JOIN DetermineCount;

你能想出更好的方法吗?

T-SQl 中是否有办法在应用顶部之前返回选择顶部查询的受影响行数? @@rowcount 会返回 5,但我想知道是否有 @@rowcountbeforetop 之类的东西。

提前感谢您的帮助。

** 更新**

这就是我现在正在做的事情,我有点喜欢 CTE,尽管 CTE 如此优雅。

-- @count is passed in as an out param to the stored procedure

CREATE TABLE dbo.#everything (id int, a int, b int, c int);

INSERT INTO #everything 
SELECT id, a, b, c FROM table WHERE a < 2;      

SET @count = @@rowcount;
SELECT TOP (5) id FROM #everything;
DROP TABLE #everything; 

【问题讨论】:

  • 执行两个查询有什么问题?一个带有 SELECT COUNT(*),一个带有 TOP?
  • 您不应该使用没有 ORDER 子句的 TOP。你如何定义TOP?
  • @DeCaf 好点,但它看起来很安静,你不觉得吗?特别是如果选择最终增长到多个连接或条件。但是,是的,这会以冗余代码为代价给我想要的东西。
  • @NickVaccaro 很好的观察,在这种情况下我只想要一个随机的 5,订购对应用程序来说意义不大。
  • 那么TOP 没有ORDER BY 不会给你“随机”...

标签: sql-server tsql select


【解决方案1】:

** 更新**

这就是我现在正在做的事情,我在 CTE 上有点喜欢它,尽管 CTE 如此优雅。让我知道你的想法。谢谢!

-- @count is passed in as an out param to the stored procedure

CREATE TABLE dbo.#everything (id int, a int, b int, c int);

INSERT INTO #everything 
SELECT id, a, b, c FROM table WHERE a < 2;      

SET @count = @@rowcount;
SELECT TOP (5) id FROM #everything;
DROP TABLE #everything; 

【讨论】:

  • 为什么需要#temp 表?您是否认为插入新对象并获取 @@ROWCOUNT 比仅从基表中 SELECT COUNT(*) 便宜?同样,没有 ORDER BY 的 TOP 不会给你随机的,如果那是你所追求的。 RANDOM 和 UNDEFINED 有很大的区别。
  • 就个人而言,我不喜欢输出参数。我认为它们太容易错过了,返回带有单个值的第二张表更干净。但是,这是我的观点,来自考虑应用层外观的人。我更希望看到 C#(或其他)处理一组中的 2 个数据表。祝你好运!
  • “没有额外开销”是指除了扫描整个表并将其插入#temp 表之外?为什么你认为这不涉及任何开销?
  • 听起来您有足够的建议来完成这项工作。这些都不会比其他任何一个更好或更差,所以这完全取决于你。我建议选择你的答案,然后关闭这个问题。如果您有更多问题,请单独提出。
  • 好的,因此扫描与 WHERE 子句匹配的整个索引范围(顺便说一句,这不在您的原始问题中!)。为什么您认为#temp 表在“内存中”?我认为您对“效率不再高”和“效率低”的含义有相当曲解的理解。
【解决方案2】:

这是获取 5 个随机行并包括总计数的相对有效的方法。不管你把它放在哪里,随机元素都会引入一个完整的排序。

SELECT TOP (5) id,a,b,c,total = COUNT(*) OVER()
  FROM dbo.mytable
  ORDER BY NEWID();

【讨论】:

  • 这很好!但它仍然将每一行中的计数作为一列包含在内,并且当我尝试它时,它以某种方式在一个名为“工作表”的对象上生成了很多逻辑读取。请参阅我对原始帖子的更新,我倾向于将计数与前 5 名分开。谢谢
  • @sOltan 这些额外的读取真的会导致可观察的性能问题吗?你似乎对自己的目标很困惑。我认为您需要决定是否关心计数是否包含在结果的每一行中。没有简单的方法只包含一次并避免工作台的东西(我怀疑这是由于随机顺序,而不是计数 - 也许您也应该将这两个要求分开)。
  • 没有可观察到的性能问题,您的查询很棒,我已经记下了。现在,我将行数作为输出参数返回,我在下面发布了更新。非常感谢您的帮助,如果您对我的更新提供任何反馈,我们将不胜感激。
  • 我对 Server2008R2 进行了一些测试,并注册了 2 倍的加速(与分别运行“top”和“count”查询相比),但它非常不可靠,而且通常会减速 10 倍。可惜MSSQL不支持游标。
  • 三年后依然有用!使用上述方法加速 Redshift Postgres 查询 - 谢谢!
【解决方案3】:

假设您希望前 5 个按 id 升序排序,这将通过您的表进行一次传递。

; WITH Everything AS 
(
    SELECT id
        , a
        , b
        , c
        , ROW_NUMBER() OVER (ORDER BY id ASC) AS rn_asc
        , ROW_NUMBER() OVER (ORDER BY id DESC) AS rn_desc
    FROM <table>
)
SELECT id
    , a
    , b
    , c
    , rn_asc + rn_desc - 1 AS total_rows
FROM Everything
WHERE rn_asc <= 5

【讨论】:

  • 您可以通过使用COUNT(*) OVER()来更高效、更简洁地完成此操作
  • 谢谢,但结果与我目前得到的结果相似(总行数显示在所有 5 行中)。
  • @sOltan 好吧,您想要总行数显示在哪里?作为不同的结果集?如果是这样,那么只需运行一个单独的查询!
猜你喜欢
  • 2014-03-04
  • 1970-01-01
  • 2015-12-12
  • 1970-01-01
  • 2021-12-22
  • 2022-01-20
  • 1970-01-01
  • 2010-10-24
  • 1970-01-01
相关资源
最近更新 更多