【问题标题】:How to replace an INNER JOIN with an IN?如何用 IN 替换 INNER JOIN?
【发布时间】:2013-10-04 18:27:05
【问题描述】:

我有一个存储过程,它在 WHILE 循环中运行几个查询,尤其是这些查询比 SP 的其余部分花费的时间呈指数增长,大大降低了我的性能。

我在一些地方读到,如果您只从第一个表中提取列,则使用 ININNER JOIN 可以提高性能,这正是我正在做的。

然而,问题是我没有第一个线索来了解如何用IN 子句替换我的INNER JOIN。我知道IN 的语法与INNER JOIN 非常不同,但我不知道从哪里开始。

作为脚注,SQL 不是我的强项。非常感谢所有建议。

这是其中的一个查询。它们在逻辑上几乎相同。

SELECT @CurrentKey = riv.Key
FROM   ResourceInfo_VW riv
       INNER JOIN (SELECT Track,
                          Code,
                          [Language]
                   FROM   ResourceInfo_VW
                   WHERE  Key = @CurrentKey) AS riv2
         ON riv.Track = riv2.Track
            AND riv.Code = riv2.Code
            AND riv.[Language] = riv2.[Language]
       INNER JOIN UserGroupCourseCatalog_VW ugcc
         ON riv.Key = ugcc.Key
WHERE  riv.[Type] = 'a'
       AND ugcc.UserGroupKey = @UserGroupKey 

【问题讨论】:

  • 如果您指定列,SQL Server 将知道您只是从第一个表中提取列。如果您正在使用 SELECT *,请停止这样做。 IN 不会神奇地改变连接的性能。
  • 可能只是 WHILE 循环本身在减慢您的查询速度。
  • 我喜欢@Richard 的想法。我会采用基于集合的方法来解决这个问题。
  • 您是否介意在这里更深入地了解您想要实现的整体目标。它可能会帮助某人提供更完整的解决方案。
  • 对派生表的联接有什么作用?另外,这里是否有对@Key 的循环引用?

标签: sql sql-server sql-server-2008 join


【解决方案1】:

是的,格式非常不同,而且它们在技术上做了不同的事情: 这是连接的完成方式:

SELECT * FROM TABLEA a JOIN TABLEB on a.commonfield = b.commonfield

这就是你如何使用 IN 语句

SELECT * FROM TABLEA WHERE commonfield in (SELECT commonfield from tableb)

【讨论】:

  • 这更像是 IN 与 JOIN 的一般答案。现在您已经发布了您的代码,还有其他一些事情浮现在脑海中。保持这一点,以防这对其他人有帮助。
【解决方案2】:

一种选择是创建一个表变量来存储您的中间结果。

首先将@UserGroupKeys 插入while 循环中。然后针对您的真实数据表运行单个update ... select。这将是手术中最昂贵的部分。

table 变量现在将查找 UserGroupKeyKey 并且应该比搜索原始表快得多。

declare @t table (UserGroupKey nvarchar(255), Key nvarchar(255))
while (...)
begin

    insert into @t(UserGroupKey) values (@UserGroupKey)

end

-- this is the expensive part, but it only happens once
update @t
set Key = riv.KEY
FROM   ResourceInfo_VW riv
       INNER JOIN (SELECT Track,
                          Code,
                          [Language]
                   FROM   ResourceInfo_VW
                   WHERE  KEY = @Key) AS riv2
         ON riv.Track = riv2.Track
            AND riv.Code = riv2.Code
            AND riv.[Language] = riv2.[Language]
       INNER JOIN UserGroupCourseCatalog_VW ugcc
         ON riv.KEY = ugcc.KEY
WHERE  riv.[Type] = 'a'
       AND ugcc.UserGroupKey = @t.UserGroupKey 

while (...)
begin

    select @Key = Key
    from @t
    where @t.UserGroupKey = @UserGroupKey

end

【讨论】:

  • 表变量是加速的魔杖。任何时候我可以用这种方式预处理昂贵的连接,我都会这样做。
【解决方案3】:

我注意到的第一件事是有一个“相关子查询”,这实际上不是必需的。 您已选择变量,这意味着即使存在多个 KEY,最后也只会选择一个值。 在这种情况下,将在变量中选择什么键取决于 SQL Server,因为没有 order by 子句。

考虑添加 Top 和 order by,这样您就知道在多行满足 WHERE 条件的情况下,将得到什么结果。 每次运行相同的查询时,结果至少应该是一致的。

我将重写查询如下。如果表上有适当的索引,性能应该不会差。

请注意,添加 TOP 1 是因为在变量中您无论如何都不能存储多个值。因此,找出您要存储的 ONE 值。 MIN、MAX、TOP1等……

另外,我认为不需要对“ResourceInfo_VW”表进行 SELF JOIN。如果我在这里错了,你可以纠正我。

SELECT 
TOP 1 
@Key= riv.KEY
FROM ResourceInfo_VW riv
INNER JOIN UserGroupCourseCatalog_VW ugcc
ON riv.KEY = ugcc.KEY AND riv.[Type] = 'a'
WHERE  ugcc.UserGroupKey = @UserGroupKey

只是为了说明,如果您尝试了解为什么以当前方式编写此查询的逻辑,那么只有您可以进入下一步,在您的情况下,使用不同的运算符重写查询。

【讨论】:

    【解决方案4】:

    花了点心思,但我通过从相关语句中删除所有INNER JOINs 大大优化了这个性能。

    相对于运行所需的时间,此查询会根据正在处理的记录数变得越来越快。

    这就是我最终的结果。您会注意到一些额外的变量。这些是在每次 WHILE 迭代开始时设置的。

    SELECT  @CurrentTrack = Track,
            @CurrentCode = Code,
            @CurrentLanguage = [Language]
    FROM    ResourceInfo_VW riv
    WHERE   riv.Key = @CurrentKey
    
    SELECT      riv.Key
    FROM        ResourceInfo_VW riv
    WHERE       riv.[Type] = 'a'
            AND riv.Track = @CurrentTrack
            AND riv.Code = @CurrentCode
            AND riv.[Language] = @CurrentLanguage
            AND riv.CourseKey IN (SELECT CourseKey 
                                  FROM  UserGroupCourseCatalog_VW 
                                  WHERE UserGroupKey = @UserGroupKey)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-24
      • 1970-01-01
      • 2021-11-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多