尽管以下查询适合我的使用,但我试图了解是否有一种优化方法,因为它在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运行。
答案 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...'
是非常“烦人的”操作),您必须先做一次,将结果存储在临时表中,然后然后在您的实际查询中使用所述临时表。
为此,我随意创建了一些示例数据(请参见下面的代码)。我的测试结果是:
因此,即使服务器需要创建临时表并填充它,合并后的临时表仍只需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
模式在数字的开头有一个通配符,使其难以优化。