SQL:使用冗余联合代码优化嵌套查询?

时间:2018-08-16 20:04:47

标签: sql tsql subquery query-optimization union

尽管以下查询适合我的使用,但我试图了解是否有一种优化方法,因为它在UNION的两个语句中使用相同的嵌套子查询。我的直觉说,应该有一种方法可以查询一次子查询,并将结果用于UNION的两个部分,但是当尝试在UNION中使用联接时,我遇到了语法问题。

SELECT ClientLocalName, ClientLocalID, ClientMasterID, 1 HierarchyType
    FROM dbo.ClientLocal
    WHERE clientLocalID = 
        ANY (SELECT HierarchyItem FROM dbo.ClientPhone WHERE PhoneNumber LIKE '%0123456789')
UNION
SELECT ClientMasterName, '0', clientMasterID, 2
    FROM dbo.ClientMaster
    WHERE clientMasterID = 
        ANY (SELECT HierarchyItem FROM dbo.ClientPhone WHERE PhoneNumber LIKE '%0123456789')
ORDER BY HierarchyType ASC;

为帮助解释为什么需要原始查询的原因,请设想“本地”客户端和“主”客户端的概念-本地客户端与主客户端绑定,但是主客户端可能有也可能没有本地客户。同样,与每个客户端关联的电话号码仅应绑定到本地客户端或主客户端(因此,在第二条语句中,本地客户端ID的硬编码为“ 0”-因为没有关联)。

这将针对Microsoft SQL Server 2008 R2运行。

4 个答案:

答案 0 :(得分:3)

如果您不喜欢重复代码,则可以使用公用表表达式:

WITH cte AS (
  SELECT HierarchyItem FROM dbo.ClientPhone WHERE PhoneNumber LIKE '%0123456789'
)
SELECT ClientLocalName, ClientLocalID, ClientMasterID, 1 HierarchyType
FROM dbo.ClientLocal
WHERE clientLocalID IN (SELECT HierarchyItem FROM cte)
UNION
SELECT ClientMasterName, '0', clientMasterID, 2
FROM dbo.ClientMaster
WHERE clientMasterID IN (SELECT HierarchyItem FROM cte)
ORDER BY HierarchyType ASC;

答案 1 :(得分:1)

由于似乎没有人提及它:减少代码并不一定意味着它也将更快地执行。人们似乎常常认为,首先执行CTE(如果有多个,则“按顺序”执行),然后应用最终查询。他们不是。实际上,服务器将在很大程度上处理cte,就好像它是FROM或JOIN子句中某个位置的派生表一样,并选择完全相同的查询计划来执行它们。为了避免系统不得不扫描ClientPhone表两次(LIKE '%0123...'是非常“烦人的”操作),您必须先做一次,将结果存储在临时表中,然后然后在您的实际查询中使用所述临时表。

为此,我随意创建了一些示例数据(请参见下面的代码)。我的测试结果是:

  • 原始查询耗时71ms
  • CTE版本耗时71ms,并使用完全相同的查询计划
  • 创建临时表需要26ms,使用它的查询需要23ms

因此,即使服务器需要创建临时表并填充它,合并后的临时表仍只需49毫秒,而不是原来的71毫秒!

当然,根据所涉及的记录数和重复查询的复杂程度,您的里程可能会有所不同,就像我说的那样,LIKE '%blah'是一件令人讨厌的事情,因为它需要对表进行全面扫描(或(索引)有点运气。如果是WHERE pk_field = @value,效果可能会大不相同)

查询愉快...

IF DB_ID('test') IS NULL CREATE DATABASE test
GO
USE test
GO

-- setup
IF OBJECT_ID('ClientPhone') IS NOT NULL DROP TABLE ClientPhone 
IF OBJECT_ID('ClientLocal') IS NOT NULL DROP TABLE ClientLocal
IF OBJECT_ID('ClientMaster') IS NOT NULL DROP TABLE ClientMaster
GO
SELECT TOP 50000 
       HierarchyItem = IDENTITY(int, 1, 1),
       PhoneNumber   = Convert(varchar(100), NewID())
  INTO dbo.ClientPhone 
  FROM sys.objects x1, sys.columns x2, sys.objects x3, sys.columns x4

SELECT ClientLocalName = 'client dummy', 
       ClientLocalID   = Convert(int, Rand(HierarchyItem * 37) * 50000), 
       ClientMasterID   = Convert(int, Rand(HierarchyItem * 47) * 50000), 
       HierarchyType   = 1
  INTO dbo.ClientLocal
  FROM dbo.ClientPhone 

SELECT ClientMasterName = 'master dummy', 
       ClientLocalID    = Convert(int, Rand(HierarchyItem * 51) * 50000), 
       ClientMasterID   = Convert(int, Rand(HierarchyItem * 53) * 50000), 
       HierarchyType    = 2
  INTO dbo.ClientMaster
  FROM dbo.ClientPhone 

GO
-- original 
SELECT ClientLocalName, ClientLocalID, ClientMasterID, 1 HierarchyType
  FROM dbo.ClientLocal
 WHERE ClientLocalID = ANY (SELECT HierarchyItem FROM dbo.ClientPhone WHERE PhoneNumber LIKE '%01')
UNION
SELECT ClientMasterName, '0', ClientMasterID, 2
  FROM dbo.ClientMaster
 WHERE ClientMasterID = ANY (SELECT HierarchyItem FROM dbo.ClientPhone WHERE PhoneNumber LIKE '%01')
ORDER BY HierarchyType ASC;

-- CTE
;WITH cte
   AS (SELECT HierarchyItem FROM dbo.ClientPhone WHERE PhoneNumber LIKE '%01')

SELECT ClientLocalName, ClientLocalID, ClientMasterID, 1 HierarchyType
  FROM dbo.ClientLocal
 WHERE ClientLocalID = ANY (SELECT HierarchyItem FROM cte)
UNION
SELECT ClientMasterName, '0', ClientMasterID, 2
  FROM dbo.ClientMaster
 WHERE ClientMasterID = ANY (SELECT HierarchyItem FROM cte)
ORDER BY HierarchyType ASC;

-- temp table
IF OBJECT_ID('tempdb..#ClientPhone') IS NOT NULL DROP TABLE #ClientPhone

SELECT HierarchyItem INTO #ClientPhone FROM dbo.ClientPhone WHERE PhoneNumber LIKE '%01'

SELECT ClientLocalName, ClientLocalID, ClientMasterID, 1 HierarchyType
  FROM dbo.ClientLocal
 WHERE ClientLocalID = ANY (SELECT HierarchyItem FROM #ClientPhone)
UNION
SELECT ClientMasterName, '0', ClientMasterID, 2
  FROM dbo.ClientMaster
 WHERE ClientMasterID = ANY (SELECT HierarchyItem FROM #ClientPhone)
ORDER BY HierarchyType ASC;

答案 2 :(得分:0)

;with subQry as (
    SELECT ClientLocalName, ClientLocalID, ClientMasterID
        FROM dbo.ClientLocal
        WHERE clientLocalID = 
            ANY (SELECT HierarchyItem FROM dbo.ClientPhone WHERE PhoneNumber LIKE '%0123456789')
)
SELECT ClientLocalName, ClientLocalID, ClientMasterID, 1 HierarchyType
    FROM subQry
UNION
SELECT ClientMasterName, '0', clientMasterID, 2
    FROM subQry
ORDER BY HierarchyType ASC;

答案 3 :(得分:0)

我可以看到的唯一明显的优化是使用union all而不是union

SELECT ClientLocalName, ClientLocalID, ClientMasterID, 1 HierarchyType
FROM dbo.ClientLocal
WHERE clientLocalID = ANY (SELECT HierarchyItem FROM dbo.ClientPhone WHERE PhoneNumber LIKE '%0123456789')
UNION ALL
SELECT ClientMasterName, '0', clientMasterID, 2
FROM dbo.ClientMaster
WHERE clientMasterID = ANY (SELECT HierarchyItem FROM dbo.ClientPhone WHERE PhoneNumber LIKE '%0123456789')
ORDER BY HierarchyType ASC;

显然,子选择之间没有重复项(由于最后一列)。假设每个子选择中都没有重复项。

我不知道SQL Server对= ANY的处理方式,但是我认为它与IN基本上相同。 = ANY子查询将运行一次(对于每个子查询),但是第二次将结果缓存。您可以将其简化为:

SELECT ClientLocalName, ClientLocalID, ClientMasterID, 1 HierarchyType
FROM ((SELECT ClientLocalName, ClientLocalID, ClientMasterID
       FROM dbo.ClientLocal
      ) UNION ALL
      (SELECT ClientLocalName, '0', ClientMasterID
       FROM dbo.ClientMasterName
      ) 
     ) c        
WHERE c.clientLocalID = ANY (SELECT HierarchyItem FROM dbo.ClientPhone WHERE PhoneNumber LIKE '%0123456789')
ORDER BY HierarchyType ASC;

我不知道这是否会有很大的改善。

LIKE模式在数字的开头有一个通配符,使其难以优化。