【问题标题】:Optimize query in sql server优化sql server中的查询
【发布时间】:2011-11-24 23:42:04
【问题描述】:

我在 Sql Server 中有一个要优化的查询。

SELECT user_type_name = CASE user_type_id 
                          WHEN 1 THEN 'Admin' 
                          WHEN 5 THEN 'Super Admin' 
                          WHEN 3 THEN 'Writer' 
                          WHEN 4 THEN 'Reader' 
                        END, 
       user_can_log = CASE user_inactive 
                        WHEN 1 THEN 'No' 
                        ELSE 'Yes' 
                      END, 
       (SELECT COUNT(*) 
        FROM   t_fthread 
        WHERE  fthread_creator_userid = user_id)         AS number_tickets, 
       (SELECT COUNT(*) 
        FROM   t_email 
        WHERE  email_to LIKE '%' + user_email + '%' 
                OR email_cc LIKE '%' + user_email + '%') AS number_emails, 
       * 
FROM   t_user 
       LEFT JOIN t_organisation 
         ON user_org_id = organisation_id 
WHERE  user_org_id = 42 
ORDER  BY user_last_name, 
          user_first_name 

查询运行时间过长。感谢查询分析器,我已经确定了查询中需要花费太多时间的部分, 就是这个部分:

(select count(*) from t_email where email_to like '%'+user_email+'%' or email_cc like '%'+user_email+'%') as number_emails.

我正在尝试重写查询以仍然获得 number_emails,但在每种情况下,它仍然很慢。

我已尝试创建索引,但无法在 user_email 和 user_cc 上创建索引。这两列都是 ntext 类型,在 sql server 2000 上,不可能在这些列上创建索引。 我已使用数据库引擎优化顾问运行分析查询,并运行工具提供的建议。

CREATE NONCLUSTERED INDEX [_dta_index_t_fthread_15_2073058421__K5] ON [dbo].[t_fthread]
(
[fthread_creator_userid] ASC
)

CREATE STATISTICS [_dta_stat_1365579903_4_3] ON [dbo].[t_user]([user_last_name], [user_first_name])

CREATE STATISTICS [_dta_stat_1365579903_1_5] ON [dbo].[t_user]([user_id], [user_org_id])

CREATE NONCLUSTERED INDEX [_dta_index_t_user_15_1365579903__K5_K1] ON [dbo].[t_user]
(
[user_org_id] ASC,
[user_id] ASC
)

但是查询仍然需要很长时间才能完成执行。

【问题讨论】:

  • 如果你有LIKE '%foo',也不能在SQL Server中使用索引,所以你是否可以索引列也没关系。

标签: sql-server optimization indexing


【解决方案1】:

您的查询表明设计不佳。你应该要么

  • 如果 to 和 cc 的所有用户都在您的数据库中,则规范您的数据库,或者
  • 跟踪发送的电子邮件数量

规范化你的数据库

要求:to 和 cc 的所有用户都在您的数据库中(没有电子邮件发送到组织外的电子邮件地址)

不要将电子邮件存储在 tocc 中,而是创建新表并存储电子邮件 ID 以及来自 to 和 cc 的 user_id。

跟踪发送的电子邮件数量

t_user 表中添加两列(Number_To, Number_CC)并根据需要增加它们(发送电子邮件时,将其存储到t_email 表中,...)。如果您决定采用这种方式,请注意并发,最好使用UPDATE t_user SET Number_To = Number_To + 1,而不是选择当前的Number_To 值然后更新为新值。

【讨论】:

    【解决方案2】:

    是否可以使用以下方法进行搜索:

     email_to like user_email+'%' 
    

    ,甚至编码所有替代方案? 或者,更好的是,将用户电子邮件(稍后将被搜索)以“已清理”的形式存储在数据库中?

    user_email 前面的通配符是邪恶的:以 '%' 开头的文本搜索总是很慢,因为它们是不可预测的,而且你永远不知道在字符串中的哪个位置可以找到搜索文本。

    例如,考虑文本:

    kakaka@mailblahblah.youknowka@this.text
    

    虽然这是一个相对较小的字符串,但对文本“ka@this.text”的搜索将如下所示

    • 在位置一发现“k”
    • 仍然在位置二匹配
    • 位置三不匹配

    这持续了两次,第三次找到了''@''。然后在位置四不匹配。

    只有在大约 36 次比较操作之后,SQL Server 才知道字符串匹配。这只是一行。所以尽量避免在字符串比较的开头使用通配符。

    【讨论】:

    • 不,我们不能只用 email_to 像 user_email+'%' 进行搜索,因为字段 email_to 可以包含许多电子邮件,所以如果删除第一个通配符,查询将不会返回准确的结果
    • 然后将邮件地址存储在另一个表中(已清理,每行一个),邮件地址表与当前表之间存在多对一关系。在邮件地址表中执行搜索,最终通过连接。
    • 然后回去告诉设计数据库的人完全不了解数据库设计指南,他负责。它不应该是一个 ntext(电子邮件不是那么长)并且应该只包含一封电子邮件。它被称为关系数据库是有原因的,设计者忽略了这一点。结果,你的表现很糟糕。
    • @TomTom:不完全正确。我们不知道该表的用途是什么——也许唯一的功能是记录电子邮件?或者这是后来在数据库中引入的功能?但我同意你的看法:在每种情况下,重新连接电子邮件的存储并拥有标准化的 DB 方案都是有益的。 (此外,关系数据库不一定是规范化数据库)
    • @TomTom,设计数据库的人把 ntext 因为这些字段假设包含邮件列表(您无法提前定义该邮件列表中有多少邮件)......现在,我无法重新编写电子邮件的存储,但这可能是为了将来......不过感谢您的帮助
    【解决方案3】:

    好吧,您可以对电子邮件执行以下操作:

        SELECT COUNT(*) 
        FROM   t_email 
        WHERE  
           (
                PATINDEX('%' + user_email + '%', email_to) != 0
                OR PATINDEX('%' + user_email + '%', email_cc) != 0
           )
    

    【讨论】:

    • 嗨,Greco,感谢您的帮助。Patindex 提示已固定我的查询。性能提高了 15%。
    【解决方案4】:

    尝试此查询一次。另外,尝试= 运算符而不是LIKE

    SELECT tu.*,
    
    (case when  user_type_id = 1 then 'Admin' 
           when user_type_id = 5 then 'Super Admin' 
           when user_type_id = 3 then 'Writer' 
           when user_type_id = 4 then  'Reader'
           else 'somethingelse' 
           end) as user_type_name, 
    
        (case when user_inactive = 1 then 'No'
         else 'Yes' end) as user_can_log,
    
        (select count(*) as number_tickets from  t_fthread where
        fthread_creator_userid=user_id()),
    
        (select count(*) as number_emails from  t_email where email_to like '%abc@xyz.com%'
      or email_cc like '%abc@xyz.com%') 
    
       FROM t_user tu left join t_organisation torg on tu.user_org_id=
      torg.organisation_id   where tu.user_org_id = 42  order by tu.user_last_name, 
      tu.user_first_name 
    

    【讨论】:

    • 您好 Rahul,您的提议出现了这个错误:“文本、ntext 和图像数据类型无法进行比较或排序,除非使用 IS NULL 或 LIKE 运算符。”
    • 那是因为你的 'email_to' 和 'email_cc' 列要么被定义为 text/ntext ... 被相应地编辑,但实际上它应该是 varchar 类型列。
    • 嗨 Rahul,实际上,email_to 和 email_cc 列包含邮件列表,由于不确定邮件列表的长度,这就是定义为 ntext 的原因......不过,您的建议已经加快了我的查询。在分析了我获得的查询后,大约性能提高了 20%。谢谢。
    • 不幸的是,这并没有返回正确的答案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-11
    • 1970-01-01
    • 1970-01-01
    • 2014-11-04
    • 1970-01-01
    • 2013-10-04
    相关资源
    最近更新 更多