【问题标题】:Select Count(*) over large amount of data在大量数据上选择 Count(*)
【发布时间】:2010-12-24 21:32:17
【问题描述】:

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

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,有什么帮助吗?

编辑:

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

【问题讨论】:

  • “仅一天”是什么意思?事务表上是否有任何 datetime/smalldatetime 列?这些列是否被索引?索引是否聚集?正如其他人所问的,按计划执行的大部分工作是什么?您在上面说它会导致表扫描,但在下面您说它使用索引。让我们更好地了解实际存在的表结构和实际的执行计划属性,将使我们有更好的机会指出需要改进的地方。如果没有,我们只是在猜测。
  • 该表有一个字段名 TransactionDate 是一个 SmallDateTime 字段 表的主要工作是存储互联网进行的交易,一旦我读到使用 Count(*) 比使用 Count(1) 更好,我必须为我的表创建索引,但要注意插入,因为用户会插入很多事务并且它必须非常快。谢谢(对不起我的英语)
  • @jmpena:在大多数现代数据库系统中(肯定是在SQL Server 2005COUNT(*)COUNT(1) 的行为完全相同。对日期列的额外过滤需要更改索引,因此发布整个查询将帮助您更好地为您提供帮助。

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


【解决方案1】:

据我所知,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)

【讨论】:

  • 这个答案帮助它节省了很多时间,但我有一些其他查询,我无法创建多个索引,因为它是一个事务表
  • 如果你有足够的空间,你可以添加WITH (ONLINE = ON)选项。这将使表在创建索引时保持运行状态。
  • 请注意,在线索引重建只能在企业版中进行。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    事务表上的聚集索引是什么?还存在哪些其他索引?您可以尝试使用此查询来消除一个连接:

    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;
    

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

    【讨论】:

    • 你的权利,我不能在任何字段中创建索引,因为它是一个事务表,它会损害查询,我放在这里的查询只是 5 个不同报告之一(状态、渠道、月/日、等...)我会尝试你的回应..谢谢
    • 我不能影响插入,这个表永远不会只更新插入,知道这一点有什么帮助吗?我对视图了解不多
    【解决方案4】:

    你试过了吗

    COUNT(1)
    

    相反?

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

    【讨论】:

    • 问题在于扫描,我还没有看到 COUNT(*) 优于 COUNT(1) 的情况。 SELECT * 可以胜过 SELECT ,具体取决于索引,但即使这样也只是微不足道的。如果有表扫描,那么问题肯定与不正确的索引有关。并非总是可以解决而不在其他地方造成痛苦。
    • 是的,没错,索引是这里的一个重要因素,不需要删除连接。
    • count(*) 使用当前索引
    猜你喜欢
    • 2010-10-12
    • 1970-01-01
    • 1970-01-01
    • 2015-05-01
    • 2016-01-16
    • 1970-01-01
    • 2011-06-29
    • 2018-04-24
    • 1970-01-01
    相关资源
    最近更新 更多