【问题标题】:TSQL not using indexesTSQL 不使用索引
【发布时间】:2011-10-03 15:07:50
【问题描述】:

这是一个测试场景,使用临时表来说明问题。 假装表 @userdata 在用户 ID 上有索引 并且表 @users 在 id 上有索引

为什么第一个选择不能使用索引,我认为它在 1 个子选择中比在 2 个子选择中表现更好?

版本 - Microsoft SQL Server 2008 R2 (RTM) 兼容级别 - SQL Server 2000。

-- test tables
DECLARE @userdata TABLE(info VARCHAR(50), userid INT)
DECLARE @users    TABLE(id INT, username VARCHAR(20), superuser BIT)

-- test data
INSERT @users    VALUES(1, 'superuser', 1)
INSERT @users    VALUES(2, 'testuser1', 0)
INSERT @users    VALUES(3, 'testuser2', 0)
INSERT @userdata VALUES('secret information', 1)
INSERT @userdata VALUES('testuser1''s data', 2)
INSERT @userdata VALUES('testuser2''s data', 3)
INSERT @userdata VALUES('testuser2''s data',3)

DECLARE @username VARCHAR(50)
SET @username = 'superuser'
--SET @username = 'testuser1'


--The superuser can read all data
--The testusers can only read own data

-- This sql can't use indexes and is very slow
SELECT *
FROM @userdata d
WHERE EXISTS 
(SELECT 1 FROM @users u
WHERE u.username = @username AND u.superuser = 1 OR 
u.id = d.userid AND u.username = @username)

-- This sql uses indexes and performs well
SELECT *
FROM @userdata d
WHERE EXISTS 
(SELECT 1 FROM @users u
WHERE u.username = @username AND u.superuser = 1)
OR EXISTS (SELECT 1 FROM @users u
WHERE u.ID = d.userid 
AND u.username = @username)

【问题讨论】:

  • 你用的是什么版本的sql server?
  • 我在问题中包含了版本
  • 表中有多少行(以及每个用户的行),SELECT * FROM @userdata d WHERE EXISTS (SELECT * FROM @users u WHERE u.username = @username AND (u.superuser = 1 OR u.id = d.userid)) 相对于其他两个查询的执行情况如何?
  • 第一个 WHERE EXISTS 中的选择看起来不合适。那里不应该有一些额外的括号,也许是:WHERE (u.username = @username AND u.superuser = 1) OR (u.id = d.userid AND u.username = @username)?
  • @rsbarro - AND 的优先级高于 OR - 您显示的括号是服务器如何解释它。

标签: tsql sql-server-2008 indexing


【解决方案1】:

我认为or 可能会给查询分析器带来一些麻烦,无法提出一个好的查询计划。 这并不是您问题的真正答案,而是执行此查询的另一种方法。除了你已经拥有的索引之外,我建议在@users.username 上建立一个索引。

if exists(select * from @users where username = @username and superuser = 1)
begin
  select *
  from @userdata
end
else
begin
  select d.*
  from @userdata as d
    inner join @users as u
      on d.userid = u.id
  where u.username = @username
end

【讨论】:

  • 最后一个 sql 不到 1 秒,第一个 sql 大约需要 5 秒。所以为用户名添加索引不是必要的,至少现在还没有。如果我稍作改动,向我们出售程序/数据库的公司将撤销所有支持。
【解决方案2】:

当您使用局部变量(在您的情况下为@username)时,SQL Server 不会总是产生最佳计划。

有关 SQL Server 由于使用了局部变量而不使用索引的示例,请参见以下链接:http://www.sqlbadpractices.com/using-local-variables-in-t-sql-queries/

【讨论】:

    【解决方案3】:

    问题可能是 SQL 不会使用索引,而不是不能。他们有多种原因。

    您可以尝试force it to use an index。您可能会发现这样查询速度较慢。

    您可以尝试ALTER INDEX ixFoo REBUILD 重建索引。该索引可能不会被使用,因为它过于碎片化。

    你也可以试试UPDATE STATISTICS

    【讨论】:

    • 我认为索引不是问题。正如我所提到的,当我拆分“存在”时,sql 使用索引就好了。
    猜你喜欢
    • 2014-06-11
    • 2015-01-24
    • 2011-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-09
    • 2020-11-20
    • 1970-01-01
    相关资源
    最近更新 更多