SELECT语句后需要行计数:什么是最佳SQL方法?

时间:2008-10-28 15:40:58

标签: sql odbc sqlncli

我正在尝试从单个表中选择一个列(没有连接),我需要计算行数,最好是在开始检索行之前。我提出了两种提供所需信息的方法。

方法1:

SELECT COUNT( my_table.my_col ) AS row_count
  FROM my_table
 WHERE my_table.foo = 'bar'

然后

SELECT my_table.my_col
  FROM my_table
 WHERE my_table.foo = 'bar'

方法2

SELECT my_table.my_col, ( SELECT COUNT ( my_table.my_col )
                            FROM my_table
                           WHERE my_table.foo = 'bar' ) AS row_count
  FROM my_table
 WHERE my_table.foo = 'bar'

我这样做是因为我的SQL驱动程序(SQL Native Client 9.0)不允许我在SELECT语句中使用SQLRowCount但我需要知道结果中的行数,以便在分配信息之前分配数组它。遗憾的是,在我的程序的这个区域中使用动态分配的容器是不可取的。

我担心可能会出现以下情况:

  • 发生计数选择
  • 发生另一条指令,添加或删除行
  • 发生数据选择,突然数组的大小错误    - 在更糟糕的情况下,这将尝试写入超出数组限制的数据并使我的程序崩溃。

方法2是否禁止此问题?

此外,两种方法中的一种会更快吗?如果是这样,哪个?

最后,是否有一个更好的方法我应该考虑(也许是一种指示驱动程序使用SQLRowCount返回SELECT结果中的行数的方法?)

对于那些提出问题的人,我使用的是Native C ++和上面提到的SQL驱动程序(由Microsoft提供)。

10 个答案:

答案 0 :(得分:29)

如果您正在使用SQL Server,则在查询后可以选择@@RowCount函数(或者如果您的结果集可能超过20亿行使用RowCount_Big()函数)。这将返回前一个语句选择的行数或受插入/更新/删除语句影响的行数。

SELECT my_table.my_col
  FROM my_table
 WHERE my_table.foo = 'bar'

SELECT @@Rowcount

或者,如果您想要发送的结果中包含的行计数类似于方法#2,则可以使用OVER clause

SELECT my_table.my_col,
    count(*) OVER(PARTITION BY my_table.foo) AS 'Count'
  FROM my_table
 WHERE my_table.foo = 'bar'

使用OVER子句比使用子查询获得行计数要好得多。使用@@ RowCount将获得最佳性能,因为select @@ RowCount语句不会有任何查询成本

响应评论更新:我给出的示例将给出分区中的行数 - 在本例中由“PARTITION BY my_table.foo”定义。每行中列的值是具有相同my_table.foo值的行数。由于您的示例查询具有子句“WHERE my_table.foo ='bar'”,因此结果集中的所有行将具有相同的my_table.foo值,因此列中的值对于所有行都相同且相等(在这种情况)这是查询中的行数。

这是一个更好/更简单的示例,说明如何在每行中包含一列,即结果集中的总行数。只需删除可选的Partition By子句。

SELECT my_table.my_col, count(*) OVER() AS 'Count'
  FROM my_table
 WHERE my_table.foo = 'bar'

答案 1 :(得分:16)

只有两种方法可以100%确定COUNT(*)和实际查询会产生一致的结果:

  • COUNT(*)与查询结合起来,如您的方法2.我建议您在示例中显示的表单,而不是kogus评论中显示的相关子查询表单。
  • SNAPSHOTSERIALIZABLE隔离级别启动事务后,使用两个查询,如方法1中所示。

使用其中一个隔离级别非常重要,因为任何其他隔离级别都允许其他客户端创建的新行在当前事务中可见。有关详细信息,请阅读SET TRANSACTION ISOLATION上的MSDN文档。

答案 2 :(得分:3)

方法2将始终返回与结果集匹配的计数。

我建议您将子查询链接到外部查询,以确保计数条件与数据集上的条件匹配。

SELECT 
  mt.my_row,
 (SELECT COUNT(mt2.my_row) FROM my_table mt2 WHERE mt2.foo = mt.foo) as cnt
FROM my_table mt
WHERE mt.foo = 'bar';

答案 3 :(得分:3)

如果您担心满足条件的行数可能会在执行查询和检索结果后的几毫秒内发生变化,您可以/应该在事务中执行查询:

BEGIN TRAN bogus

SELECT COUNT( my_table.my_col ) AS row_count
FROM my_table
WHERE my_table.foo = 'bar'

SELECT my_table.my_col
FROM my_table
WHERE my_table.foo = 'bar'
ROLLBACK TRAN bogus

这将始终返回正确的值。

此外,如果您使用的是SQL Server,则可以使用@@ ROWCOUNT来获取受上一个语句影响的行数,并将 real 查询的输出重定向到临时表或表变量,所以你可以完全返回所有内容,而不需要事务:​​

DECLARE @dummy INT

SELECT my_table.my_col
INTO #temp_table
FROM my_table
WHERE my_table.foo = 'bar'

SET @dummy=@@ROWCOUNT
SELECT @dummy, * FROM #temp_table

答案 4 :(得分:1)

以下是一些想法:

  • 使用方法#1并调整数组大小以保存其他结果或使用自动调整大小的类型(您没有提到您正在使用的语言,所以我不能更具体)。
  • 您可以在事务中的方法#1中执行这两个语句,以保证如果您的数据库支持,则两次计数都相同。
  • 我不确定您对数据做了什么,但是如果可以先处理结果而不先存储所有数据,这可能是最好的方法。

答案 5 :(得分:1)

如果您真的担心行数会在select count和select语句之间发生变化,为什么不首先在temp表中选择行?这样,你知道你会同步。

答案 6 :(得分:0)

为什么不将结果放入矢量?这样你就不必事先知道尺寸了。

答案 7 :(得分:0)

您可能想要考虑一种更好的模式来处理这种类型的数据。

没有自我推测的SQL驱动程序会告诉您在返回行之前您的查询将返回多少行,因为答案可能会更改(除非您使用事务,这会产生自己的问题。)

行数不会改变 - 谷歌的ACID和SQL。

答案 8 :(得分:0)

IF (@@ROWCOUNT > 0)
BEGIN
SELECT my_table.my_col
  FROM my_table
 WHERE my_table.foo = 'bar'
END

答案 9 :(得分:0)

添加此内容是因为这是google针对此问题的最佳结果。 在sqlite中,我使用它来获取rowcount。

WITH temptable AS
  (SELECT one,two
   FROM
     (SELECT one, two
      FROM table3
      WHERE dimension=0
      UNION ALL SELECT one, two
      FROM table2
      WHERE dimension=0
      UNION ALL SELECT one, two
      FROM table1
      WHERE dimension=0)
   ORDER BY date DESC)
SELECT *
FROM temptable
LEFT JOIN
  (SELECT count(*)/7 AS cnt,
                        0 AS bonus
   FROM temptable) counter
WHERE 0 = counter.bonus