【问题标题】:TSQL optimize multiple IntersectTSQL优化多个Intersect
【发布时间】:2013-06-05 16:37:00
【问题描述】:

问题查询使用了多个 Intersect。

通过 count() = 6 将其更改为 in ('alpha','beta','gamma','delta','epsilon','phi') 组不是一个选项,因为应用程序支持如下带有通配符(例如 alpha%)。但是 count() = 6 查询运行时间不到 1 秒。

使用通配符可以使用多个连接,这就是它过去的结构。在 4 或更少时,Intersect 的性能优于多个连接,但不幸的是在 5 或更多时没有测试。

查询在任何 4 个字词中都表现出色 - 不到 1 秒。
从字面上看,任何 4 - 前 4、后 4 或中间 4。
在 5 或更多然后它死了 - 我在 2 分钟时终止了查询。
让它运行 6 个术语 - 5 分钟返回 795 行。

查询计划将循环与合并连接混合使用最多 4 个术语。
在 5 个或更多术语时,查询计划是所有循环连接。
是否有将查询提示应用于相交的语法?

使用 () () 尝试了两组 3,但这并没有改变查询计划。

  ( -- start term
      select [ftsIndexWordOnce].[sID] 
      from [ftsIndexWordOnce] with (nolock)
      where [ftsIndexWordOnce].[wordID] in ( 
             select [id] from [FTSwordDef] with (nolock) 
                                     where [word] like 'alpha')
  ) -- end term
INTERSECT
  ( -- start term
      select [ftsIndexWordOnce].[sID] 
      from [ftsIndexWordOnce] with (nolock)
      where [ftsIndexWordOnce].[wordID] in ( 
             select [id] from [FTSwordDef] with (nolock) 
                                     where [word] like 'beta')
  ) -- end term
INTERSECT
  ( -- start term
      select [ftsIndexWordOnce].[sID] 
      from [ftsIndexWordOnce] with (nolock)
      where [ftsIndexWordOnce].[wordID] in ( 
             select [id] from [FTSwordDef] with (nolock) 
                                     where [word] like 'gamma')
  ) -- end term
INTERSECT 
  ( -- start term
      select [ftsIndexWordOnce].[sID] 
      from [ftsIndexWordOnce] with (nolock)
      where [ftsIndexWordOnce].[wordID] in ( 
             select [id] from [FTSwordDef] with (nolock) 
                                     where [word] like 'delta')
  ) -- end term
INTERSECT
  ( -- start term
      select [ftsIndexWordOnce].[sID] 
      from [ftsIndexWordOnce] with (nolock)
      where [ftsIndexWordOnce].[wordID] in ( 
             select [id] from [FTSwordDef] with (nolock) 
                                     where [word] like 'epsilon')
  ) -- end term
INTERSECT
  ( -- start term
      select [ftsIndexWordOnce].[sID] 
      from [ftsIndexWordOnce] with (nolock)
      where [ftsIndexWordOnce].[wordID] in ( 
             select [id] from [FTSwordDef] with (nolock) 
                                     where [word] like 'phi')
  ) -- end term

我想我已经解决了

     select distinct [ftsIndexWordOnce].[sID] 
      from [ftsIndexWordOnce] with (nolock)
      Inner Merge Join [FTSwordDef] with (nolock) 
        On [FTSwordDef].[ID] = [ftsIndexWordOnce].[wordID] 
       And [FTSwordDef].[word] like 'alpha' 
  INTERSECT
     select distinct [ftsIndexWordOnce].[sID] 
      from [ftsIndexWordOnce] with (nolock)
      Inner Merge Join [FTSwordDef] with (nolock) 
        On [FTSwordDef].[ID] = [ftsIndexWordOnce].[wordID] 
       And [FTSwordDef].[word] like 'beta'

查询优化器在 5 或更多时仍然变得愚蠢,但这会强制第一个连接成为合并并保存它。

【问题讨论】:

    标签: sql-server-2008 tsql intersect query-hints


    【解决方案1】:

    可能想试试“EXISTS”。 “IN”可能会变得昂贵,尤其是对于大型列表。 "EXISTS" 只查找第一个匹配项,而"IN" 试图找到所有匹配项。如果 sID 在 ftsIndexWordOnce 中是唯一的,那么下面的代码应该可以工作。如果没有,您可以在其上添加 distinct 或 group。

    编辑:第一个脚本有逻辑错误。见 cmets。

    SELECT
        [ftsIndexWordOnce].[sID]
    FROM
        [ftsIndexWordOnce] WITH (NOLOCK)
    WHERE
        EXISTS
        (
            SELECT
                NULL
            FROM
                [FTSwordDef] WITH (NOLOCK)
            WHERE
                [FTSwordDef].[word] LIKE 'alpha'
                AND
                [FTSwordDef].id = [ftsIndexWordOnce].wordid
        )
    INTERSECT
    SELECT
        [ftsIndexWordOnce].[sID]
    FROM
        [ftsIndexWordOnce] WITH (NOLOCK)
    WHERE
        EXISTS
        (
            SELECT
                NULL
            FROM
                [FTSwordDef] WITH (NOLOCK)
            WHERE
                [FTSwordDef].[word] LIKE 'beta'
                AND
                [FTSwordDef].id = [ftsIndexWordOnce].wordid
        )
    INTERSECT
    SELECT
        [ftsIndexWordOnce].[sID]
    FROM
        [ftsIndexWordOnce] WITH (NOLOCK)
    WHERE
        EXISTS
        (
            SELECT
                NULL
            FROM
                [FTSwordDef] WITH (NOLOCK)
            WHERE
                [FTSwordDef].[word] LIKE 'gamma'
                AND
                [FTSwordDef].id = [ftsIndexWordOnce].wordid
        )
    INTERSECT
    SELECT
        [ftsIndexWordOnce].[sID]
    FROM
        [ftsIndexWordOnce] WITH (NOLOCK)
    WHERE
        EXISTS
        (
            SELECT
                NULL
            FROM
                [FTSwordDef] WITH (NOLOCK)
            WHERE
                [FTSwordDef].[word] LIKE 'delta'
                AND
                [FTSwordDef].id = [ftsIndexWordOnce].wordid
        )
    INTERSECT
    SELECT
        [ftsIndexWordOnce].[sID]
    FROM
        [ftsIndexWordOnce] WITH (NOLOCK)
    WHERE
        EXISTS
        (
            SELECT
                NULL
            FROM
                [FTSwordDef] WITH (NOLOCK)
            WHERE
                [FTSwordDef].[word] LIKE 'epsilon'
                AND
                [FTSwordDef].id = [ftsIndexWordOnce].wordid
        )
    INTERSECT
    SELECT
        [ftsIndexWordOnce].[sID]
    FROM
        [ftsIndexWordOnce] WITH (NOLOCK)
    WHERE
        EXISTS
        (
            SELECT
                NULL
            FROM
                [FTSwordDef] WITH (NOLOCK)
            WHERE
                [FTSwordDef].[word] LIKE 'phi'
                AND
                [FTSwordDef].id = [ftsIndexWordOnce].wordid
        )
    

    【讨论】:

    • 好主意,但 AND 返回 0。很确定它一次评估单个 wordID,并且单个 wordID 不能既像“alpha”又像“beta”。你怎么看?
    • 哦,对了。所以回到相交,但使用 EXISTS 而不是 IN。将相交位连接为子查询也可以。
    • 用 INTERSECT 尝试 EXISTS 但仍然没有解决。在 5 或更多时,它会进行所有循环连接并死亡。但是,内部合并连接可能会解决问题。
    • +1 因为你让我考虑从另一个角度看待它。
    【解决方案2】:

    我遇到了和你非常相似的问题。

    我想知道未定义数量的相交的共同值是什么。

    由于性能损失,我无法使用动态 SQL。

    于是我想出了一个模式如下:

    WITH CTE AS (
        SELECT [FTSwordDef].[ID], -- Unique identifier of the referenced entity
               [ftsIndexWordOnce].[sID] -- The field that should be common for the intersect
        FROM [FTSwordDef]
        INNER JOIN [ftsIndexWordOnce]
            ON [FTSwordDef].[ID] = [ftsIndexWordOnce].[wordID]
    
        -- All your intersects becomes a query that returns all the participants of interest    
        WHERE [FTSwordDef].[word] IN ('alpha','beta','gamma','delta','epsilon','phi')   
    
        -- Optional GROUP BY if you don't trust the integrity of 
        -- your data and fear duplicate data may be present
        GROUP BY [FTSwordDef].[ID],[ftsIndexWordOnce].[sID]
    )
    
    SELECT [MAIN].[sID] -- The common value you wanted with the intersect
    FROM CTE [MAIN]
    
    -- We count the amount of participating entities (N Amount of intersect)
    CROSS APPLY(
        SELECT COUNT(DISTINCT [A].[ID]) C FROM CTE [A]
    ) [A]
    
    -- We count the occurrences of the common value
    CROSS APPLY(
        SELECT COUNT([B].[sID]) C FROM CTE [B] WHERE [B].[sID] = [MAIN].[sID]
    ) [B]
    
    -- If the value we want in common has equal occurrences as the amount of 
    -- participating referenced entities, we can say the value is common for all
    WHERE [A].[C] = [B].[C]
    GROUP BY [MAIN].[sID]
    

    这是一个小提琴http://sqlfiddle.com/#!18/2f1d9/55

    【讨论】:

      猜你喜欢
      • 2011-01-09
      • 1970-01-01
      • 2015-11-19
      • 2017-11-16
      • 1970-01-01
      • 2010-12-06
      • 1970-01-01
      • 2014-10-14
      • 1970-01-01
      相关资源
      最近更新 更多