【问题标题】:Performance problem on a query查询的性能问题
【发布时间】:2010-04-16 01:37:27
【问题描述】:

我在查询时遇到性能问题。

第一个表是一个 Customer 表,其中包含数百万条记录。客户表有一列电子邮件地址和一些关于客户的其他信息。

第二个表是一个只包含电子邮件地址的 CommunicationInfo 表。

而我在这里想要的是; CommunicationInfo 表中的电子邮件地址在 Customers 表中重复了多少次。表现最好的查询可能是什么。

我可以解释这种情况的基本查询是;

Select ci.Email, count(*) from Customer c left join 
CommunicationInfo ci on c.Email1 = ci.Email or c.Email2 = ci.Email 
Group by ci.Email

但可以肯定的是,执行过程大约需要 5、6 分钟。

提前致谢。

【问题讨论】:

  • CommunicationInfo 中有多少条记录?
  • CustomerCommunicationInfo是什么关系?我在想每个Customer 行有很多CommunicationInfo 行,是这样吗?
  • @KM:第二个表是一个只包含电子邮件地址的 CommunicationInfo 表。我相信这是一个查找表,每封电子邮件只添加一次。
  • @Quassnoi,但每次为客户发送通信时,它是一行,还是类似的东西。如果是这样,它会比客户表大。
  • @KM:你可能是对的。那么我们需要等待答案。

标签: sql-server tsql


【解决方案1】:

如果您在 Customer.Email 上有一个索引,在 CommunicationInfo.Email 上有另一个索引,那么这个查询就差不多了

Select
    c.Email, count(*)
    from Customer c
        left join CommunicationInfo  ci on c.Email1 = ci.Email
        left join CommunicationInfo ci2 on c.Email2 = ci2.Email
    Group by c.Email

【讨论】:

  • 键是索引。确保正确索引电子邮件列。
  • 如果ci.EmailPRIMARY KEY,则此查询是SELECT email, COUNT(*) FROM customer 的同义词。如果不是,则此查询是一个杂乱的笛卡尔连接。
【解决方案2】:

你提到:

而我在这里想要的是;多少 将电子邮件地址乘以 CommunicationInfo 表在 客户表。可能是什么 表现最好的查询。

对我来说,这听起来像是您可以轻松使用 INNER JOIN - 这很可能会快很多,因为它会将搜索范围限制为那些真正拥有电子邮件的客户 - 任何没有电子邮件的客户'根本没有电子邮件(因此甚至不会查看 count(*) = 0) - 即使只是 SQL Server 必须计算和分组的行数,这也可能会产生很大的不同。

所以试试这个:

SELECT 
   ci.Email, COUNT(*) 
FROM 
   dbo.Customer c 
INNER JOIN dbo.CommunicationInfo ci 
   ON c.Email1 = ci.Email OR c.Email2 = ci.Email  
GROUP BY
   ci.Email

在你的情况下表现如何?

【讨论】:

  • 执行起来真的要花这么多时间。
【解决方案3】:

使用OR 条件会剥夺优化器使用HASH JOINMERGE JOIN 的机会。

使用这个:

SELECT  ci.Email, SUM(cnt)
FROM    (
        SELECT  ci.Email, COUNT(c.Email) AS cnt
        FROM    CommunicationInfo ci
        LEFT JOIN
                Customer c
        ON      c.Email1 = ci.Email
        GROUP BY
                ci.Email
        UNION ALL
        SELECT  ci.Email, COUNT(c.Email) AS cnt
        FROM    CommunicationInfo ci
        LEFT JOIN
                Customer c
        ON      c.Email2 = ci.Email
        GROUP BY
                ci.Email
        ) q2
GROUP BY
        ci.Email

或者这个:

SELECT  ci.Email, COUNT(*)
FROM    CommunicationInfo ci
LEFT JOIN
        (
        SELECT  Email1 AS email
        FROM    Customer c
        UNION ALL
        SELECT  Email2
        FROM    Customer
        ) q
ON      q.Email = ci.Email
GROUP BY
        ci.Email

确保您在 Customer(Email)Customer(Email2) 上有索引

如果您的电子邮件大部分未填写,第一个查询会更有效,第二个查询 - 如果大多数电子邮件已填写。

【讨论】:

  • 我有一种感觉,既然 Customer 表中有数百万条记录,两次 IO 并不一定会让它运行得更快,但我也想到了这个想法。
  • @Jeremy:只要对EmailEmail2 进行索引,这只会以顺序方式从两个索引中获取所有键。如果所有或几乎所有字段都已填充并且匹配,则需要执行相同的操作,只是以效率较低的嵌套循环(随机读取)方式。
  • 啊,是的,您提到索引需要在列上。我的错。
【解决方案4】:

根据您的环境,您可能无法做太多优化。

几个问题:

  1. CommunicationInfo 中有多少条记录?
  2. 您真正需要多久运行一次此查询?是一次性分析,还是每隔 10 分钟会有多人运行一次?
  3. 字段是否已编入索引?我会猜测 Email1 和 Email2 字段都没有被索引。但是,我不建议在不考虑整个系统平衡的情况下添加索引。
  4. 为什么要使用左连接?您真的需要 Customer 表中的所有内容吗?您正在数数,所以进行 INNER JOIN 并没有什么坏处。

建议:

  1. 通过查询优化向导运行查询,看看是否有任何 SQL Server 建议。
  2. 一个极端的建议是将 Email1 和 Email2 列转储到临时表中并加入其中。我已经看到查询运行缓慢,因为对特定表的压力很大,所以有时将记录复制到临时表中会更快,但这种技术非常依赖于有多少内存,IO 有多快,以及特定桌子上的压力量。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-11-05
    • 1970-01-01
    • 1970-01-01
    • 2013-02-18
    • 2013-01-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多