如何使用交叉连接优化 SQL 查询
Posted
技术标签:
【中文标题】如何使用交叉连接优化 SQL 查询【英文标题】:How to optimizing SQL Query with Cross Join 【发布时间】:2013-10-08 20:51:02 【问题描述】:如何让这个 SQL 查询更高效?下面显示的 CteFinal 代码是我的查询的一部分,我的查询最多需要 6 分钟。 cteMonth 如下所示。 cteDetail 是另一个直接从数据库中提取信息的 cte,运行时间不到一秒。
CteFinal 所做的是创建缺少的会计期间行,同时包含 f.FiscalPeriod=0 行中的一些列数据。
我不能添加、删除或更改表上的任何索引,因为这是一个 ERP 数据库,我不能进行此类更改。
CteFinal:
SELECT Account,Month, CONVERT(DATETIME, CAST(@Year as varchar(4)) + '-' + CAST(Month as VARCHAR(2)) + '-' + '01', 102) JEDate
,accountdesc,'' Description,'' JournalCode,NULL JournalNum,NULL JournalLine
,'' LegalNumber,'' CurrencyCode,0.00 DebitAmount,0.00 CreditAmount,fiscalcalendarid,company,bookid,SegValue2,SegValue1,SegValue3,SegValue4
FROM cteDetail f
CROSS JOIN cteMonths m
WHERE f.FiscalPeriod=0 and not exists(select * from cteDetailADDCreatedZero x where x.Account=f.Account and x.FiscalPeriod=Month)
CteMonth:
cteMonths (Month) AS(
select 0 as Month
UNION select 1 as Month
UNION select 2 as Month
UNION select 3 as Month
UNION select 4 as Month
UNION select 5 as Month
UNION select 6 as Month
UNION select 7 as Month
UNION select 8 as Month
UNION select 9 as Month
UNION select 10 as Month
UNION select 11 as Month
UNION select 12 as Month)
谢谢!
【问题讨论】:
右加入 cteMonth 怎么样? 您可以使用SET NOCOUNT ON
来减少一些网络开销,在表名前加上dbo.
以节省一点安全开销,如果可能的话,将WITH (NOLOCK)
提示添加到表中。在您的AND NOT EXISTS
中,您可以只从您的cteDetailADDCreatedZero
表中选择主键。
@Darth WITH (NOLOCK)
将有助于防止阻塞,但以潜在的脏数据为代价 - 并且不会自行加速此查询(仅在更新阻塞读取的情况下)。您对NOT EXISTS
提出的更改将对任何事情产生绝对零影响。
为什么会有第 0 个月和第 12 个月?你真的要代表 13 个月吗?为什么要使用交叉连接?您真的想要源中的每一行都有 13 行吗?否则你能告诉我们如何找出cteDetail
中的什么可以告诉我们该数据属于哪个月份吗?
@Aaron 第 0 个月 = 本年度的期初余额,该行将始终存在。它用于填充其他创建的行中的一些列。匹配很简单,对这个查询没有影响,但是很好的问题。
【参考方案1】:
这里有一种更有效的方法来生成给定年份的 12 个月(如果您有 your own Numbers table,效率会更高):
DECLARE @year INT = 2013;
;WITH cteMonths([Month],AsDate) AS
(
SELECT n-1,DATEADD(YEAR, @Year-1900, DATEADD(MONTH,n-1,0)) FROM (
SELECT TOP (13) RANK() OVER (ORDER BY [object_id]) FROM sys.all_objects
) AS c(n)
)
SELECT [Month], AsDate FROM cteMonths;
所以现在,你可以说:
;WITH cteMonths([Month],AsDate) AS
(
SELECT n,DATEADD(YEAR, @Year-1900, DATEADD(MONTH,n-1,0)) FROM (
SELECT TOP (13) RANK() OVER (ORDER BY [object_id]) FROM sys.all_objects
) AS c(n)
),
cteDetail AS
(
...no idea what is here...
),
cteDetailADDCreatedZero AS
(
...no idea what is here...
)
SELECT f.Account, m.[Month], JEDate = m.AsDate, f.accountdesc, Description = '',
JournalCode = '', JournalNum = NULL, JournalLine = NULL, LegalNumber = '',
CurrencyCode = '', DebitAmount = 0.00, CreditAmount = 0.00, f.fiscalcalendarid,
f.company, f.bookid, f.SegValue2, f.SegValue1, f.SegValue3, f.SegValue4
FROM cteMonths AS m
LEFT OUTER JOIN cteDetail AS f
ON ... some clause I am not clear on ...
WHERE f.FiscalPeriod = 0
AND NOT EXISTS
(
SELECT 1 FROM cteDetailADDCreatedZero AS x
WHERE x.Account = f.Account
AND x.FiscalPeriod = m.[Month]
);
我怀疑这不会解决您的问题:这可能会强制对cteDetail
或cteDetailADDCreatedZero
中提到的任何表或两者都进行整个表扫描。您应该检查此查询的实际执行计划,看看是否有任何扫描或其他昂贵的操作可以指导您更好地建立索引。也可能只是您将一堆低效的 CTE 堆叠在一起 - 除非您展示所有内容,否则我们无法真正提供帮助。 CTE 就像视图 - 如果您开始将它们堆叠在一起,您实际上会限制优化器为您生成有效计划的能力。到了某个时候,它就会把手举到空中……
【讨论】:
【参考方案2】:一种可能性是物理化 SQL 视图(如果查询是视图)。有时具有复杂查询的视图很慢。
【讨论】:
以上是关于如何使用交叉连接优化 SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章