【问题标题】:SQL Query - 20mil records - Best practice to return informationSQL 查询 - 2000 万条记录 - 返回信息的最佳实践
【发布时间】:2010-04-02 09:01:50
【问题描述】:

我有一个包含下表的 SQL 数据库:

表:电话记录 -------------- ID(身份种子) 名 姓 电话号码 邮政编码

非常简单直接的表格。该表有超过 2000 万条记录。我正在寻找进行查询的最佳方法,以从表中提取基于区号的记录。例如,这是我完成的示例查询。

SELECT phonenumber, firstname
FROM [PhoneRecords]
WHERE (phone LIKE '2012042%') OR
      (phone LIKE '2012046%') OR
      (phone LIKE '2012047%') OR
      (phone LIKE '2012083%') OR
      (phone LIKE '2012088%') OR
      (phone LIKE '2012841%')

如您所见,这是一个丑陋的查询,但它可以完成工作(如果我没有遇到超时问题)

谁能告诉我执行上述查询以显示结果的速度/优化的最佳方式?目前,在 9gb 1600mhz ram、i7 930 四核超频 4.01ghz 上完成上述查询大约需要 2 小时。我显然拥有执行此类查询所需的计算机能力,但查询仍然需要很长时间。

【问题讨论】:

  • 能否提供表上索引的详细信息?

标签: sql-server


【解决方案1】:

您可能在 phonenumber 列上缺少索引。

CREATE INDEX IX_PHONERECORDS_PHONENUMBER_FIRSTNAME 
  ON dbo.PhoneRecords (PhoneNumber) INCLUDE (FirstName)

如果这没有帮助,请发布execution plan (CTRL+M)。

【讨论】:

  • 感谢大家的快速回复。电话没有被索引,但我现在正在设置它以尝试运行查询(使用 Union All 而不是 OR)以查看它是否更快。我将发布我的发现,希望遇到此问题的其他人也能够解决此问题。
  • @eqiz:当你发表你的帖子时,我也很想知道单独添加索引是否足够并且优化器可以处理你的 OR,或者查询重写是否帮助优化器选择了最好的计划。
  • 如果使用union all 会比or 子句执行得更好(或更差),我会感到惊讶
【解决方案2】:

首先,您需要在列phone 上建立索引。如果您没有,请添加它。

如果它仍然运行缓慢,您可以尝试使用UNION ALL 而不是OR,因为这对优化器来说更容易使用。这是有效的,因为您构建条件的方式保证了结果将是不同的。所以你的查询可以重写为:

SELECT phonenumber, firstname FROM [PhoneRecords] WHERE phone LIKE '2012042%'
UNION ALL
SELECT phonenumber, firstname FROM [PhoneRecords] WHERE phone LIKE '2012046%'
UNION ALL
SELECT phonenumber, firstname FROM [PhoneRecords] WHERE phone LIKE '2012047%'
UNION ALL
SELECT phonenumber, firstname FROM [PhoneRecords] WHERE phone LIKE '2012083%'
UNION ALL
SELECT phonenumber, firstname FROM [PhoneRecords] WHERE phone LIKE '2012088%'
UNION ALL
SELECT phonenumber, firstname FROM [PhoneRecords] WHERE phone LIKE '2012041%'

这个查询应该能够使用索引来高效运行。

您应该在运行实际查询之前查看执行计划,并确保没有 TABLE SCAN 或 INDEX SCAN。

【讨论】:

  • Union 相当于使用 OR 语句。 SQL 查询优化器不会以任何方式使用相同的计划吗?
  • 解释一下?你不会对 MySQL 感到困惑吗?
  • 抱歉,是的……搞混了!你是对的 - SQL Server 没有这个功能。对于 SQL Server,查看计划的最简单方法是在 SSMS 中。有一个“显示预计执行计划”按钮。
【解决方案3】:

你有索引吗?第一步是在 PhoneNumber 列上放置一个索引。如果这还不够(我不知道在索引列中搜索部分字符串的确切细节),我建议添加另一个名为“AreaCode”的列,该列可以从 PhoneNumber 列自动计算。然后您可以在 AreaCode 列上添加索引。

【讨论】:

    【解决方案4】:

    第一个非常明显的问题是你有索引吗?如果要对其进行查询,则至少需要在电话号码上创建索引。您可能应该创建一个覆盖索引,其中包括您想要的字段和 where 子句中的字段,这样计算机就不必浪费时间在索引中找到该行以获取您想要的信息后获取该行。显然,另一方面是索引越大,查询越慢。

    【讨论】:

      【解决方案5】:

      您可以拆分您的电话号码栏:[区号]、[电话号码]

      然后,如果此查询是您的应用程序中对该表“最重要”的查询,并且返回的行数/总行数的比率很高,则在 [Area Code] 上添加 CLUSTERED 索引,否则,添加标准索引。

      您也可以保留电话号码列并直接对其进行索引,这取决于您的应用。

      【讨论】:

        【解决方案6】:

        首先,我将电话列拆分为“区号”和“电话号码”。

        另外,我会将这些数字转换为 int;索引的执行速度会更快。

        AreaCode = 2012042
        

        应该会快得多

        PhoneNumber LIKE '2012042%'
        

        【讨论】:

          【解决方案7】:

          即使您正在执行表扫描(即使您有索引,如果选择性很低,也可能发生这种情况)您的查询执行速度应该快于 2 小时。如果与其他查询扫描的其他表没有竞争并且 sqlserver 最大内存足够大,则您的表足够小以完全适合 sql server 缓冲池。因此,虽然您可以执行一些技巧,例如在区域 + 电话中添加索引或拆分电话号码,但您应该调查 sql 服务器配置以及您的系统配置。

          【讨论】:

            【解决方案8】:

            http://igoro.com/archive/precomputed-view-a-cool-and-useful-sql-pattern

            创建一个物化视图,其中包括电话号码的前 n 个数字作为它自己的列。然后您可以查询区号列并包含名称。预先计算区号,因此不必在每次选择时都进行。如果可以,请不要使用 or 运算符。使用 union 帮助查询计划使用索引。

            实际上,您正在运行的查询将执行 20,000,000 次 x 比较,其中 x 是您每次执行选择时要搜索的区号数。通过查询精确的索引列,您根本不需要转到表,并且可以以有效的方式搜索索引 O(log n) 我认为。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2018-08-13
              • 1970-01-01
              • 2013-01-06
              • 2020-01-11
              • 2013-09-25
              • 1970-01-01
              • 1970-01-01
              • 2012-09-26
              相关资源
              最近更新 更多