SQL:存储过程需要 20/25 秒才能完成 运行,而完全相同的 select 在查询 window 中几乎是即时的 运行
SQL: Stored procedure takes 20/25 seconds to run while the exact same select is almost instant running in a query window
我已经阅读了 SP taking 15 minutes, but the same query when executed returns results in 1-2 minutes,但是 none 那里发布的建议似乎对我有用。
我有下一个 SQL SP:
ALTER PROCEDURE [dbo].[BL_GET_OW_AND_CATALOGUES_BY_SITE_FOR_ACTUAL_POSITION]
@PFK_ENTERPRISE int,
@FK_SITE int,
@PK_USER int
WITH RECOMPILE
AS
SET ARITHABORT ON;
DECLARE @PFK_ENTERPRISE_2 int = @PFK_ENTERPRISE
DECLARE @FK_SITE_2 int = @FK_SITE
DECLARE @PK_USER_2 int = @PK_USER
SELECT * INTO #markets_catalogues_tmp1 FROM markets_catalogues WHERE pfk_enterprise = @PFK_ENTERPRISE_2 and is_Active = 1 and FK_CATALOGUE_SETUP > 0
SELECT ow.PK_ORDER_WINDOW, ow.OW_DESCRIPTION
FROM ORDERS_WINDOW ow
inner join ORDERS_WINDOW_CATALOGUES owc on ow.PFK_ENTERPRISE = owc.PFK_ENTERPRISE
and ow.PK_ORDER_WINDOW = owc.PFK_ORDER_WINDOW
inner join CATALOGUES c on owc.PFK_ENTERPRISE = c.PFK_ENTERPRISE
and owc.PFK_CATALOGUE = c.PK_CATALOGUE
inner join #markets_catalogues_tmp1 mc on
owc.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and owc.PFK_CATALOGUE = mc.PFK_CATALOGUE
inner join MARKET m on
m.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and m.PK_MARKET = mc.PFK_MARKET
inner join USER_ACCESS_MARKETS uam on
m.PFK_ENTERPRISE = uam.PFK_ENTERPRISE AND m.PK_MARKET = uam.PFK_MARKET
inner join USERS u ON
uam.PFK_ENTERPRISE = u.PFK_ENTERPRISE AND uam.PFK_USER = u.PK_USER
WHERE (ow.PFK_ENTERPRISE = @PFK_ENTERPRISE_2) AND (ow.FK_ORDER_WINDOW_STATUS IN (1,2,3,5,6))
AND (IS_MAIN_CATALOG_RELATED = 1 OR FK_CATALOG_RELATED = 0)
AND (uam.PFK_USER = @PK_USER_2 OR @PK_USER_2 IS NULL)
AND c.FK_SITE = @FK_SITE_2
AND (c.IS_HISTORIC <> 1)
group by ow.PK_ORDER_WINDOW, ow.OW_DESCRIPTION
ORDER BY ow.OW_DESCRIPTION ASC
而且执行需要 20 多秒,但奇怪的是,如果我只是 运行 在新查询 window 中完全相同 select 结果几乎即时.
(原来存储的有点长,有3个select,但是一个就可以重现问题了)
我试过直接用tmp变量代替参数,我也试过
WITH RECOMPILE AS SET ARITHABORT ON;
无济于事,起初尝试 CTRL-L 检查显示瓶颈的执行计划我已经修复了,但现在我不能再尝试执行计划了,因为我正在将 select 存储到一个#tmp 变量。
我需要找出为什么这个查询用 exec 执行 SP 比 运行 单独 select 本身花费的时间长得多,换句话说,优化这个查询。
编辑 1:
如果我在单独的 window 中启动下一个查询,它会立即运行,并且 returns 与 运行 从 SP:
得到的结果完全相同
SELECT * INTO #markets_catalogues_tmp1 FROM markets_catalogues WHERE pfk_enterprise = 41 and is_Active = 1 and FK_CATALOGUE_SETUP > 0
SELECT ow.PK_ORDER_WINDOW, ow.OW_DESCRIPTION
FROM ORDERS_WINDOW ow
inner join ORDERS_WINDOW_CATALOGUES owc on ow.PFK_ENTERPRISE = owc.PFK_ENTERPRISE
and ow.PK_ORDER_WINDOW = owc.PFK_ORDER_WINDOW
inner join CATALOGUES c on owc.PFK_ENTERPRISE = c.PFK_ENTERPRISE
and owc.PFK_CATALOGUE = c.PK_CATALOGUE
inner join #markets_catalogues_tmp1 mc on
owc.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and owc.PFK_CATALOGUE = mc.PFK_CATALOGUE
inner join MARKET m on
m.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and m.PK_MARKET = mc.PFK_MARKET
inner join USER_ACCESS_MARKETS uam on
m.PFK_ENTERPRISE = uam.PFK_ENTERPRISE AND m.PK_MARKET = uam.PFK_MARKET
inner join USERS u ON
uam.PFK_ENTERPRISE = u.PFK_ENTERPRISE AND uam.PFK_USER = u.PK_USER
WHERE (ow.PFK_ENTERPRISE = 41) AND (ow.FK_ORDER_WINDOW_STATUS IN (1,2,3,5,6))
--and MARKETS_CATALOGUES.IS_ACTIVE = 1
--and MARKETS_CATALOGUES.FK_CATALOGUE_SETUP > 0
AND (IS_MAIN_CATALOG_RELATED = 1 OR FK_CATALOG_RELATED = 0)
AND (uam.PFK_USER = 14118 OR 14118 IS NULL)
AND c.FK_SITE = 1
AND (c.IS_HISTORIC <> 1)
group by ow.PK_ORDER_WINDOW, ow.OW_DESCRIPTION
ORDER BY ow.OW_DESCRIPTION ASC
编辑 2:
我已经将 where 子句中的所有元素移动到连接中的相应 ON - 正如@CarlosSR 所建议的那样 - 所以查询现在看起来像这样:
SELECT ow.PK_ORDER_WINDOW, ow.OW_DESCRIPTION
FROM ORDERS_WINDOW ow
inner join ORDERS_WINDOW_CATALOGUES owc on ow.PFK_ENTERPRISE = owc.PFK_ENTERPRISE
and ow.PFK_ENTERPRISE = @PFK_ENTERPRISE_2
and ow.FK_ORDER_WINDOW_STATUS IN (1,2,3,5,6)
and ow.PK_ORDER_WINDOW = owc.PFK_ORDER_WINDOW
inner join CATALOGUES c on owc.PFK_ENTERPRISE = c.PFK_ENTERPRISE
and owc.PFK_CATALOGUE = c.PK_CATALOGUE
and c.FK_SITE = @FK_SITE_2
and (c.IS_MAIN_CATALOG_RELATED = 1 or c.FK_CATALOG_RELATED = 0)
and c.IS_HISTORIC <> 1
inner join #markets_catalogues_tmp1 mc on
owc.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and owc.PFK_CATALOGUE = mc.PFK_CATALOGUE
inner join MARKET m on
m.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and m.PK_MARKET = mc.PFK_MARKET
inner join USER_ACCESS_MARKETS uam on
m.PFK_ENTERPRISE = uam.PFK_ENTERPRISE and m.PK_MARKET = uam.PFK_MARKET
and (uam.PFK_USER = @PK_USER_2 OR @PK_USER_2 IS NULL)
inner join USERS u ON
uam.PFK_ENTERPRISE = u.PFK_ENTERPRISE AND uam.PFK_USER = u.PK_USER
group by ow.PK_ORDER_WINDOW, ow.OW_DESCRIPTION
ORDER BY ow.OW_DESCRIPTION ASC
但结果完全一样,20 秒 运行 查询。
编辑 3: 回答@Andrew Sayer
执行计划 - 起初 - 显示与内部 select
的连接存在瓶颈
inner join (SELECT * FROM markets_catalogues.....
(查询看起来像下一个)。
SELECT ow.PK_ORDER_WINDOW, ow.OW_DESCRIPTION
FROM ORDERS_WINDOW ow
inner join ORDERS_WINDOW_CATALOGUES owc on ow.PFK_ENTERPRISE = owc.PFK_ENTERPRISE
and ow.PK_ORDER_WINDOW = owc.PFK_ORDER_WINDOW
inner join CATALOGUES c on owc.PFK_ENTERPRISE = c.PFK_ENTERPRISE
and owc.PFK_CATALOGUE = c.PK_CATALOGUE
inner join (SELECT * FROM markets_catalogues WHERE pfk_enterprise = @PFK_ENTERPRISE_2 and is_Active = 1 and FK_CATALOGUE_SETUP > 0) mc on
owc.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and owc.PFK_CATALOGUE = mc.PFK_CATALOGUE
inner join MARKET m on
m.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and m.PK_MARKET = mc.PFK_MARKET
inner join USER_ACCESS_MARKETS uam on
m.PFK_ENTERPRISE = uam.PFK_ENTERPRISE AND m.PK_MARKET = uam.PFK_MARKET
inner join USERS u ON
uam.PFK_ENTERPRISE = u.PFK_ENTERPRISE AND uam.PFK_USER = u.PK_USER
WHERE (ow.PFK_ENTERPRISE = @PFK_ENTERPRISE_2) AND (ow.FK_ORDER_WINDOW_STATUS IN (1,2,3,5,6))
AND (IS_MAIN_CATALOG_RELATED = 1 OR FK_CATALOG_RELATED = 0)
AND (uam.PFK_USER = @PK_USER_2 OR @PK_USER_2 IS NULL)
AND c.FK_SITE = @FK_SITE_2
AND (c.IS_HISTORIC <> 1)
group by ow.PK_ORDER_WINDOW, ow.OW_DESCRIPTION
ORDER BY ow.OW_DESCRIPTION ASC
我所做的是将连接中的 SELECT 移动到一个单独的 #tmp 变量,实际上现在它的 运行ning 更快(因为同样的 select 是在一个 select 中使用,为简单起见我省略了)。起初三个selects的整个查询是1:20分钟,现在完整的查询是40秒,但与运行宁[=]时的即时结果相比仍然很慢81=]s 在查询中 window.
编辑4:估计的执行计划。
https://www.brentozar.com/pastetheplan/?id=HJGXXfsG9
编辑5:实际执行计划。
https://www.brentozar.com/pastetheplan/?id=SkF2Nzszc
编辑 6: 实际执行计划建议我为 #tmp table 创建一个索引。我可以这样创建索引:
SELECT * INTO #markets_catalogues_tmp1 FROM markets_catalogues WHERE pfk_enterprise = @PFK_ENTERPRISE_2 and is_Active = 1 and FK_CATALOGUE_SETUP > 0
CREATE NONCLUSTERED INDEX markets_catalogues_tmp1 ON [dbo].[#markets_catalogues_tmp1] ([pfk_enterprise]) INCLUDE ([is_active], [FK_CATALOGUE_SETUP])
但由于我对此不是很有经验,我不确定索引应该包含在 SP 本身还是外部,因为如果我把它放在里面,每次启动 SP 时都会创建索引?但是outside就不行了,因为是tmptable,所以有点懵
感谢更新。
您可以随时尝试索引建议 - 基准测试是否有帮助应该非常简单。我的直接想法是,两者都无济于事,但练习如何以可控的方式进行基准测试将是有益的。阅读这个问题,我们可以知道您已经尝试了很多不同的事情,并且不太清楚哪些更改有助于程序的行为更像独立程序,哪些更改改进了存储过程和独立程序,哪些没有区别但无论如何保留。
您还没有提供您希望其表现的实际执行计划,但让我们看看我们可以改进多少。
您的时间主要用于 USER_ACCESS_MARKETS
索引查找的嵌套循环。这需要很长时间,因为它运行了 539K 次,而它认为它只会执行 55 次。这个错误的估计可能是从 ORDERS_WINDOW_CATALOGUES
和 CATALOGUES
之间的连接开始的,它认为这将导致 3 行但实际上是 returns 119 行(这种差异随着连接的增加而滚雪球)。这个连接有很多过滤器在起作用,两个在主要连接条件中看起来是聚集索引(应该没问题),然后是 c.IS_HISTORIC <> 1
和 c.FK_SITE = @FK_SITE_2
过滤器,后者两个可能是低基数的原因。我的猜测是 c.FK_SITE = @FK_SITE_2
在这里是最重要的,我建议你看看这个连接在你的独立版本中是什么样的 - 其中嗅探 FK_SITE_2
变量会有所不同。
要解决这个基数问题,您可以为 CATALOGUES
创建一个临时 table,就像您已经对 markets_catalogues
所做的一样。如果有一种有效的方法可以找到与这些其他过滤器匹配的行,那么这将起作用。这将使 SQL 服务器有机会在填充此温度 table 后获得估计 - 更正它对 FK_SITE_2
过滤器的估计。
SELECT * INTO #CATALOGUES_tmp FROM CATALOGUES WHERE FK_SITE = @FK_SITE_2 and IS_HISTORIC <> 1
而不是解决基数估计(这里看起来有很多问题来源,您将需要解决很多问题),您可以通过使用临时 table 所以它必须被散列连接:
SELECT * INTO #USER_ACCESS_MARKETS_tmp FROM USER_ACCESS_MARKETS WHERE PFK_ENTERPRISE = @PFK_ENTERPRISE_2 and (PFK_USER = @PK_USER_2 OR @PK_USER_2 IS NULL)
或提示连接选项 - 告诉 SQL 服务器它应该散列连接到 USER_ACCESS_MARKETS
inner hash join USER_ACCESS_MARKETS uam on
m.PFK_ENTERPRISE = uam.PFK_ENTERPRISE AND m.PK_MARKET = uam.PFK_MARKET
那些临时 tables 可能会导致其他基数错误估计开始更多地影响计划,因此您需要对每次修改的实际执行计划进行评估 - 查看现在的时间,请参阅估计的来源。
进一步的优化可能来自注意到它花费了大约一秒钟的时间将 540K 行聚合回 119 - 这表明将连接更改为 semi-joins 或强制更早地进行区分以减少不必要的结果的数量通过是可能的。如果您可以这样做,以便仅在一小部分时间内完成与 USER_ACCESS_MARKETS
的缓慢嵌套循环连接,那么这也会带来巨大的好处(并且仍然可以是嵌套循环)。
我已经对语句的一部分进行了子查询,并在加入 USER_ACCESS_MARKETS
之前放置了一个 distinct
,很难预测这会产生多大的影响,因为我不知道关于 market_catalogues
中的 PFK_MARKET
列的任何信息(此列是您从此连接中唯一关心的附加列)但有可能
ALTER PROCEDURE [dbo].[BL_GET_OW_AND_CATALOGUES_BY_SITE_FOR_ACTUAL_POSITION]
@PFK_ENTERPRISE int,
@FK_SITE int,
@PK_USER int
WITH RECOMPILE
AS
SET ARITHABORT ON;
DECLARE @PFK_ENTERPRISE_2 int = @PFK_ENTERPRISE
DECLARE @FK_SITE_2 int = @FK_SITE
DECLARE @PK_USER_2 int = @PK_USER
SELECT sq.PK_ORDER_WINDOW, sq.OW_DESCRIPTION
FROM (
SELECT DISTINCT ow.PK_ORDER_WINDOW, ow.OW_DESCRIPTION, m.PFK_ENTERPRISE, m.PK_MARKET
FROM ORDERS_WINDOW ow
inner join ORDERS_WINDOW_CATALOGUES owc on ow.PFK_ENTERPRISE = owc.PFK_ENTERPRISE
and ow.PK_ORDER_WINDOW = owc.PFK_ORDER_WINDOW
inner join CATALOGUES c on owc.PFK_ENTERPRISE = c.PFK_ENTERPRISE
and owc.PFK_CATALOGUE = c.PK_CATALOGUE
inner join (SELECT * FROM markets_catalogues WHERE pfk_enterprise = @PFK_ENTERPRISE_2 and is_Active = 1 and FK_CATALOGUE_SETUP > 0) mc on
owc.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and owc.PFK_CATALOGUE = mc.PFK_CATALOGUE
inner join MARKET m on
m.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and m.PK_MARKET = mc.PFK_MARKET
WHERE (ow.PFK_ENTERPRISE = @PFK_ENTERPRISE_2) AND (ow.FK_ORDER_WINDOW_STATUS IN (1,2,3,5,6))
AND (c.IS_MAIN_CATALOG_RELATED = 1 OR c.FK_CATALOG_RELATED = 0)
AND c.FK_SITE = @FK_SITE_2
AND (c.IS_HISTORIC <> 1)
) sq
inner join USER_ACCESS_MARKETS uam on
sq.PFK_ENTERPRISE = uam.PFK_ENTERPRISE AND sq.PK_MARKET = uam.PFK_MARKET
AND (uam.PFK_USER = @PK_USER_2 OR @PK_USER_2 IS NULL)
inner join USERS u ON
uam.PFK_ENTERPRISE = u.PFK_ENTERPRISE AND uam.PFK_USER = u.PK_USER
group by sq.PK_ORDER_WINDOW, sq.OW_DESCRIPTION
ORDER BY sq.OW_DESCRIPTION ASC
我还回到了 markets_catalogues
没有临时 table 的查询版本,你应该先尝试将数据放入临时 table .
我想知道这些计划是否与您通过独立声明获得的计划相似。
我已经阅读了 SP taking 15 minutes, but the same query when executed returns results in 1-2 minutes,但是 none 那里发布的建议似乎对我有用。 我有下一个 SQL SP:
ALTER PROCEDURE [dbo].[BL_GET_OW_AND_CATALOGUES_BY_SITE_FOR_ACTUAL_POSITION]
@PFK_ENTERPRISE int,
@FK_SITE int,
@PK_USER int
WITH RECOMPILE
AS
SET ARITHABORT ON;
DECLARE @PFK_ENTERPRISE_2 int = @PFK_ENTERPRISE
DECLARE @FK_SITE_2 int = @FK_SITE
DECLARE @PK_USER_2 int = @PK_USER
SELECT * INTO #markets_catalogues_tmp1 FROM markets_catalogues WHERE pfk_enterprise = @PFK_ENTERPRISE_2 and is_Active = 1 and FK_CATALOGUE_SETUP > 0
SELECT ow.PK_ORDER_WINDOW, ow.OW_DESCRIPTION
FROM ORDERS_WINDOW ow
inner join ORDERS_WINDOW_CATALOGUES owc on ow.PFK_ENTERPRISE = owc.PFK_ENTERPRISE
and ow.PK_ORDER_WINDOW = owc.PFK_ORDER_WINDOW
inner join CATALOGUES c on owc.PFK_ENTERPRISE = c.PFK_ENTERPRISE
and owc.PFK_CATALOGUE = c.PK_CATALOGUE
inner join #markets_catalogues_tmp1 mc on
owc.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and owc.PFK_CATALOGUE = mc.PFK_CATALOGUE
inner join MARKET m on
m.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and m.PK_MARKET = mc.PFK_MARKET
inner join USER_ACCESS_MARKETS uam on
m.PFK_ENTERPRISE = uam.PFK_ENTERPRISE AND m.PK_MARKET = uam.PFK_MARKET
inner join USERS u ON
uam.PFK_ENTERPRISE = u.PFK_ENTERPRISE AND uam.PFK_USER = u.PK_USER
WHERE (ow.PFK_ENTERPRISE = @PFK_ENTERPRISE_2) AND (ow.FK_ORDER_WINDOW_STATUS IN (1,2,3,5,6))
AND (IS_MAIN_CATALOG_RELATED = 1 OR FK_CATALOG_RELATED = 0)
AND (uam.PFK_USER = @PK_USER_2 OR @PK_USER_2 IS NULL)
AND c.FK_SITE = @FK_SITE_2
AND (c.IS_HISTORIC <> 1)
group by ow.PK_ORDER_WINDOW, ow.OW_DESCRIPTION
ORDER BY ow.OW_DESCRIPTION ASC
而且执行需要 20 多秒,但奇怪的是,如果我只是 运行 在新查询 window 中完全相同 select 结果几乎即时.
(原来存储的有点长,有3个select,但是一个就可以重现问题了)
我试过直接用tmp变量代替参数,我也试过
WITH RECOMPILE AS SET ARITHABORT ON;
无济于事,起初尝试 CTRL-L 检查显示瓶颈的执行计划我已经修复了,但现在我不能再尝试执行计划了,因为我正在将 select 存储到一个#tmp 变量。
我需要找出为什么这个查询用 exec 执行 SP 比 运行 单独 select 本身花费的时间长得多,换句话说,优化这个查询。
编辑 1:
如果我在单独的 window 中启动下一个查询,它会立即运行,并且 returns 与 运行 从 SP:
得到的结果完全相同SELECT * INTO #markets_catalogues_tmp1 FROM markets_catalogues WHERE pfk_enterprise = 41 and is_Active = 1 and FK_CATALOGUE_SETUP > 0
SELECT ow.PK_ORDER_WINDOW, ow.OW_DESCRIPTION
FROM ORDERS_WINDOW ow
inner join ORDERS_WINDOW_CATALOGUES owc on ow.PFK_ENTERPRISE = owc.PFK_ENTERPRISE
and ow.PK_ORDER_WINDOW = owc.PFK_ORDER_WINDOW
inner join CATALOGUES c on owc.PFK_ENTERPRISE = c.PFK_ENTERPRISE
and owc.PFK_CATALOGUE = c.PK_CATALOGUE
inner join #markets_catalogues_tmp1 mc on
owc.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and owc.PFK_CATALOGUE = mc.PFK_CATALOGUE
inner join MARKET m on
m.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and m.PK_MARKET = mc.PFK_MARKET
inner join USER_ACCESS_MARKETS uam on
m.PFK_ENTERPRISE = uam.PFK_ENTERPRISE AND m.PK_MARKET = uam.PFK_MARKET
inner join USERS u ON
uam.PFK_ENTERPRISE = u.PFK_ENTERPRISE AND uam.PFK_USER = u.PK_USER
WHERE (ow.PFK_ENTERPRISE = 41) AND (ow.FK_ORDER_WINDOW_STATUS IN (1,2,3,5,6))
--and MARKETS_CATALOGUES.IS_ACTIVE = 1
--and MARKETS_CATALOGUES.FK_CATALOGUE_SETUP > 0
AND (IS_MAIN_CATALOG_RELATED = 1 OR FK_CATALOG_RELATED = 0)
AND (uam.PFK_USER = 14118 OR 14118 IS NULL)
AND c.FK_SITE = 1
AND (c.IS_HISTORIC <> 1)
group by ow.PK_ORDER_WINDOW, ow.OW_DESCRIPTION
ORDER BY ow.OW_DESCRIPTION ASC
编辑 2:
我已经将 where 子句中的所有元素移动到连接中的相应 ON - 正如@CarlosSR 所建议的那样 - 所以查询现在看起来像这样:
SELECT ow.PK_ORDER_WINDOW, ow.OW_DESCRIPTION
FROM ORDERS_WINDOW ow
inner join ORDERS_WINDOW_CATALOGUES owc on ow.PFK_ENTERPRISE = owc.PFK_ENTERPRISE
and ow.PFK_ENTERPRISE = @PFK_ENTERPRISE_2
and ow.FK_ORDER_WINDOW_STATUS IN (1,2,3,5,6)
and ow.PK_ORDER_WINDOW = owc.PFK_ORDER_WINDOW
inner join CATALOGUES c on owc.PFK_ENTERPRISE = c.PFK_ENTERPRISE
and owc.PFK_CATALOGUE = c.PK_CATALOGUE
and c.FK_SITE = @FK_SITE_2
and (c.IS_MAIN_CATALOG_RELATED = 1 or c.FK_CATALOG_RELATED = 0)
and c.IS_HISTORIC <> 1
inner join #markets_catalogues_tmp1 mc on
owc.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and owc.PFK_CATALOGUE = mc.PFK_CATALOGUE
inner join MARKET m on
m.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and m.PK_MARKET = mc.PFK_MARKET
inner join USER_ACCESS_MARKETS uam on
m.PFK_ENTERPRISE = uam.PFK_ENTERPRISE and m.PK_MARKET = uam.PFK_MARKET
and (uam.PFK_USER = @PK_USER_2 OR @PK_USER_2 IS NULL)
inner join USERS u ON
uam.PFK_ENTERPRISE = u.PFK_ENTERPRISE AND uam.PFK_USER = u.PK_USER
group by ow.PK_ORDER_WINDOW, ow.OW_DESCRIPTION
ORDER BY ow.OW_DESCRIPTION ASC
但结果完全一样,20 秒 运行 查询。
编辑 3: 回答@Andrew Sayer
执行计划 - 起初 - 显示与内部 select
的连接存在瓶颈inner join (SELECT * FROM markets_catalogues.....
(查询看起来像下一个)。
SELECT ow.PK_ORDER_WINDOW, ow.OW_DESCRIPTION
FROM ORDERS_WINDOW ow
inner join ORDERS_WINDOW_CATALOGUES owc on ow.PFK_ENTERPRISE = owc.PFK_ENTERPRISE
and ow.PK_ORDER_WINDOW = owc.PFK_ORDER_WINDOW
inner join CATALOGUES c on owc.PFK_ENTERPRISE = c.PFK_ENTERPRISE
and owc.PFK_CATALOGUE = c.PK_CATALOGUE
inner join (SELECT * FROM markets_catalogues WHERE pfk_enterprise = @PFK_ENTERPRISE_2 and is_Active = 1 and FK_CATALOGUE_SETUP > 0) mc on
owc.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and owc.PFK_CATALOGUE = mc.PFK_CATALOGUE
inner join MARKET m on
m.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and m.PK_MARKET = mc.PFK_MARKET
inner join USER_ACCESS_MARKETS uam on
m.PFK_ENTERPRISE = uam.PFK_ENTERPRISE AND m.PK_MARKET = uam.PFK_MARKET
inner join USERS u ON
uam.PFK_ENTERPRISE = u.PFK_ENTERPRISE AND uam.PFK_USER = u.PK_USER
WHERE (ow.PFK_ENTERPRISE = @PFK_ENTERPRISE_2) AND (ow.FK_ORDER_WINDOW_STATUS IN (1,2,3,5,6))
AND (IS_MAIN_CATALOG_RELATED = 1 OR FK_CATALOG_RELATED = 0)
AND (uam.PFK_USER = @PK_USER_2 OR @PK_USER_2 IS NULL)
AND c.FK_SITE = @FK_SITE_2
AND (c.IS_HISTORIC <> 1)
group by ow.PK_ORDER_WINDOW, ow.OW_DESCRIPTION
ORDER BY ow.OW_DESCRIPTION ASC
我所做的是将连接中的 SELECT 移动到一个单独的 #tmp 变量,实际上现在它的 运行ning 更快(因为同样的 select 是在一个 select 中使用,为简单起见我省略了)。起初三个selects的整个查询是1:20分钟,现在完整的查询是40秒,但与运行宁[=]时的即时结果相比仍然很慢81=]s 在查询中 window.
编辑4:估计的执行计划。
https://www.brentozar.com/pastetheplan/?id=HJGXXfsG9
编辑5:实际执行计划。
https://www.brentozar.com/pastetheplan/?id=SkF2Nzszc
编辑 6: 实际执行计划建议我为 #tmp table 创建一个索引。我可以这样创建索引:
SELECT * INTO #markets_catalogues_tmp1 FROM markets_catalogues WHERE pfk_enterprise = @PFK_ENTERPRISE_2 and is_Active = 1 and FK_CATALOGUE_SETUP > 0
CREATE NONCLUSTERED INDEX markets_catalogues_tmp1 ON [dbo].[#markets_catalogues_tmp1] ([pfk_enterprise]) INCLUDE ([is_active], [FK_CATALOGUE_SETUP])
但由于我对此不是很有经验,我不确定索引应该包含在 SP 本身还是外部,因为如果我把它放在里面,每次启动 SP 时都会创建索引?但是outside就不行了,因为是tmptable,所以有点懵
感谢更新。
您可以随时尝试索引建议 - 基准测试是否有帮助应该非常简单。我的直接想法是,两者都无济于事,但练习如何以可控的方式进行基准测试将是有益的。阅读这个问题,我们可以知道您已经尝试了很多不同的事情,并且不太清楚哪些更改有助于程序的行为更像独立程序,哪些更改改进了存储过程和独立程序,哪些没有区别但无论如何保留。
您还没有提供您希望其表现的实际执行计划,但让我们看看我们可以改进多少。
您的时间主要用于 USER_ACCESS_MARKETS
索引查找的嵌套循环。这需要很长时间,因为它运行了 539K 次,而它认为它只会执行 55 次。这个错误的估计可能是从 ORDERS_WINDOW_CATALOGUES
和 CATALOGUES
之间的连接开始的,它认为这将导致 3 行但实际上是 returns 119 行(这种差异随着连接的增加而滚雪球)。这个连接有很多过滤器在起作用,两个在主要连接条件中看起来是聚集索引(应该没问题),然后是 c.IS_HISTORIC <> 1
和 c.FK_SITE = @FK_SITE_2
过滤器,后者两个可能是低基数的原因。我的猜测是 c.FK_SITE = @FK_SITE_2
在这里是最重要的,我建议你看看这个连接在你的独立版本中是什么样的 - 其中嗅探 FK_SITE_2
变量会有所不同。
要解决这个基数问题,您可以为 CATALOGUES
创建一个临时 table,就像您已经对 markets_catalogues
所做的一样。如果有一种有效的方法可以找到与这些其他过滤器匹配的行,那么这将起作用。这将使 SQL 服务器有机会在填充此温度 table 后获得估计 - 更正它对 FK_SITE_2
过滤器的估计。
SELECT * INTO #CATALOGUES_tmp FROM CATALOGUES WHERE FK_SITE = @FK_SITE_2 and IS_HISTORIC <> 1
而不是解决基数估计(这里看起来有很多问题来源,您将需要解决很多问题),您可以通过使用临时 table 所以它必须被散列连接:
SELECT * INTO #USER_ACCESS_MARKETS_tmp FROM USER_ACCESS_MARKETS WHERE PFK_ENTERPRISE = @PFK_ENTERPRISE_2 and (PFK_USER = @PK_USER_2 OR @PK_USER_2 IS NULL)
或提示连接选项 - 告诉 SQL 服务器它应该散列连接到 USER_ACCESS_MARKETS
inner hash join USER_ACCESS_MARKETS uam on
m.PFK_ENTERPRISE = uam.PFK_ENTERPRISE AND m.PK_MARKET = uam.PFK_MARKET
那些临时 tables 可能会导致其他基数错误估计开始更多地影响计划,因此您需要对每次修改的实际执行计划进行评估 - 查看现在的时间,请参阅估计的来源。
进一步的优化可能来自注意到它花费了大约一秒钟的时间将 540K 行聚合回 119 - 这表明将连接更改为 semi-joins 或强制更早地进行区分以减少不必要的结果的数量通过是可能的。如果您可以这样做,以便仅在一小部分时间内完成与 USER_ACCESS_MARKETS
的缓慢嵌套循环连接,那么这也会带来巨大的好处(并且仍然可以是嵌套循环)。
我已经对语句的一部分进行了子查询,并在加入 USER_ACCESS_MARKETS
之前放置了一个 distinct
,很难预测这会产生多大的影响,因为我不知道关于 market_catalogues
中的 PFK_MARKET
列的任何信息(此列是您从此连接中唯一关心的附加列)但有可能
ALTER PROCEDURE [dbo].[BL_GET_OW_AND_CATALOGUES_BY_SITE_FOR_ACTUAL_POSITION]
@PFK_ENTERPRISE int,
@FK_SITE int,
@PK_USER int
WITH RECOMPILE
AS
SET ARITHABORT ON;
DECLARE @PFK_ENTERPRISE_2 int = @PFK_ENTERPRISE
DECLARE @FK_SITE_2 int = @FK_SITE
DECLARE @PK_USER_2 int = @PK_USER
SELECT sq.PK_ORDER_WINDOW, sq.OW_DESCRIPTION
FROM (
SELECT DISTINCT ow.PK_ORDER_WINDOW, ow.OW_DESCRIPTION, m.PFK_ENTERPRISE, m.PK_MARKET
FROM ORDERS_WINDOW ow
inner join ORDERS_WINDOW_CATALOGUES owc on ow.PFK_ENTERPRISE = owc.PFK_ENTERPRISE
and ow.PK_ORDER_WINDOW = owc.PFK_ORDER_WINDOW
inner join CATALOGUES c on owc.PFK_ENTERPRISE = c.PFK_ENTERPRISE
and owc.PFK_CATALOGUE = c.PK_CATALOGUE
inner join (SELECT * FROM markets_catalogues WHERE pfk_enterprise = @PFK_ENTERPRISE_2 and is_Active = 1 and FK_CATALOGUE_SETUP > 0) mc on
owc.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and owc.PFK_CATALOGUE = mc.PFK_CATALOGUE
inner join MARKET m on
m.PFK_ENTERPRISE = mc.PFK_ENTERPRISE and m.PK_MARKET = mc.PFK_MARKET
WHERE (ow.PFK_ENTERPRISE = @PFK_ENTERPRISE_2) AND (ow.FK_ORDER_WINDOW_STATUS IN (1,2,3,5,6))
AND (c.IS_MAIN_CATALOG_RELATED = 1 OR c.FK_CATALOG_RELATED = 0)
AND c.FK_SITE = @FK_SITE_2
AND (c.IS_HISTORIC <> 1)
) sq
inner join USER_ACCESS_MARKETS uam on
sq.PFK_ENTERPRISE = uam.PFK_ENTERPRISE AND sq.PK_MARKET = uam.PFK_MARKET
AND (uam.PFK_USER = @PK_USER_2 OR @PK_USER_2 IS NULL)
inner join USERS u ON
uam.PFK_ENTERPRISE = u.PFK_ENTERPRISE AND uam.PFK_USER = u.PK_USER
group by sq.PK_ORDER_WINDOW, sq.OW_DESCRIPTION
ORDER BY sq.OW_DESCRIPTION ASC
我还回到了 markets_catalogues
没有临时 table 的查询版本,你应该先尝试将数据放入临时 table .
我想知道这些计划是否与您通过独立声明获得的计划相似。