在大量数据上选择Count(*)

时间:2009-12-09 16:04:00

标签: sql-server-2005 select large-data-volumes

我想为报告执行此操作,但我的表中有20,000,000条记录,它会在我的应用程序中导致TimeOut。

SELECT
        T.transactionStatusID,
        TS.shortName AS TransactionStatusDefShortName,
        count(*) AS qtyTransactions
    FROM
        Transactions T 

    INNER JOIN TransactionTypesCurrencies TTC
                ON  T.id_Ent = TTC.id_Ent
                    AND T.trnTypeCurrencyID = TTC.trnTypeCurrencyID
            INNER JOIN TransactionStatusDef TS
                ON  T.id_Ent = TS.ent_Ent
                AND T.transactionStatusID = TS.ID
WHERE
    T.id_Ent = @id_Ent
GROUP BY
        T.transactionStatusID,
        TS.shortName

据我所知,COUNT(*)会导致全表扫描,这会让我的查询占用太多时间,即使用MS SQL 2005,还有什么帮助吗?

编辑:

项目负责人告诉我,查询只有一天可以提供帮助吗?

4 个答案:

答案 0 :(得分:2)

  

据我所知,COUNT(*)会导致全表扫描,这会让我的查询占用太多时间,即使用MS SQL 2005,还有什么帮助吗?

COUNT(*)可以使用任何能够给出答案的来源,包括索引。

在您的情况下,我会使用(id_ent, transactionStatusID)trnTypeCurrencyID上创建覆盖索引:

CREATE INDEX ON Transactions (id_ent, transactionStatusID) INCLUDE (trnTypeCurrencyID)

并稍微重写一下查询:

SELECT  transactionStatusID, qtyTransactions, TS.shortName
FROM    (
        SELECT  T.transactionStatusID,
                COUNT(*) AS qtyTransactions
        FROM    Transactions T
        JOIN    TransactionTypesCurrencies TTC
        ON      TTC.id_Ent = T.id_Ent
                AND TTC.trnTypeCurrencyID = T.trnTypeCurrencyID
        WHERE   T.id_Ent = @id_Ent
        GROUP BY
                T.transactionStatusID
        ) TD
JOIN    TransactionStatusDef TS
ON      TS.ent_Ent = @id_Ent
        AND TS.ID = TD.transactionStatusID

索引将在id_ent上过滤,并在transactionStatusID上并行化。由于您已覆盖trnTypeCurrencyID,因此引擎不必查找表中的值,它已经存在于索引中。

GROUP BY子句也只包含索引中的列,因此可以更好地并行化。

<强>更新

通过添加WITH (ONLINE = ON),您可以在创建索引时让表保持运行状态:

CREATE INDEX ON Transactions (id_ent, transactionStatusID) INCLUDE (trnTypeCurrencyID) WITH (ONLINE = ON)

答案 1 :(得分:1)

如果查看查询的执行计划,则会突出显示执行错误的位。它会告诉您它是在进行表扫描,索引扫描还是索引搜索。 所以这是开始寻找的最佳场所。

目前你有任何索引吗? JOIN和WHERE子句中涉及的字段是主要候选者 - 如果您没有索引,那么这将是一个主要因素。

答案 2 :(得分:0)

你试过吗

COUNT(1)

相反?

此外,是否需要加入 TransactionTypesCurrencies ,似乎您没有使用其中的任何内容?

答案 3 :(得分:0)

交易表上的聚集索引是什么?还有哪些其他指标?您可以尝试此查询以消除一个连接:

SELECT
    T.TransactionStatusID,
    TS.ShortName,
    qtyTransactions = COUNT(*)
FROM
    dbo.Transactions AS T
INNER JOIN
    dbo.TransactionStatusDef AS TS
    ON T.id_Ent = TS.ent_Ent
    AND T.transactionStatusID = TS.ID
WHERE EXISTS
(
    SELECT 1
        FROM do.TransactionTypeCurrencies AS TTC
        WHERE TTC.id_Ent = T.id_Ent
        AND TTC.trnTypeCurrencyID = T.trnTypeCurrencyID
)
AND T.id_Ent = @id_Ent
GROUP BY
    T.transactionStatusID,
    TS.shortName;

您也可以在发出查询之前尝试使用快照隔离,例如

SET TRANSACTION ISOLATION LEVEL SNAPSHOT;

要做到这一点,你必须:

ALTER DATABASE dbname SET ALLOW_SNAPSHOT_ISOLATION ON;

一般情况下,您希望确保正确编制索引。如果您无法对此查询的表应用正确的索引,因为它会损害其他查询,那么您可以考虑使用索引视图来维护此计数(以插入/更新性能为代价),或者如果您使用表分区是在企业版上,或偶尔在后台运行这些数据的汇总,以便您的应用程序不必等待它(假设实际计数有点陈旧)。