SP运行速度很慢.当我查看执行计划时 – 我可以看到其成本的83%用于嵌套循环(内部联接)
有什么机会以某种方式替代它吗?
这是我的SP
ALTER PROCEDURE [dbo].[EarningPlazaCommercial]
@State varchar(50),
@StartDate datetime,
@EndDate datetime,
@AsOfDate datetime,
@ClassCode nvarchar(max),
@Coverage varchar(100)
AS
BEGIN
SET NOCOUNT ON;
CREATE TABLE #PolicyNumbers (PolicyNumber varchar(50))
INSERT INTO #PolicyNumbers SELECT PolicyNumber FROM tblClassCodesPlazaCommercial T1
WHERE NOT EXISTS (
SELECT 1 FROM tblClassCodesPlazaCommercial T2
WHERE T1.PolicyNumber = T2.PolicyNumber
AND ClassCode IN
(SELECT * FROM [dbo].[StringOfStringsToTable](@ClassCode,','))
)
CREATE CLUSTERED INDEX IDX_C_PolicyNumbers_PolicyNumber ON #PolicyNumbers(PolicyNumber)
; WITH Earned_to_date AS (
SELECT Cast(@AsOfDate AS DATE) AS Earned_to_date
), policy_data AS (
SELECT
PolicyNumber
, Cast(PolicyEffectiveDate AS DATE) AS PolicyEffectiveDate
, Cast(PolicyExpirationDate AS DATE) AS PolicyExpirationDate
, WrittenPremium
FROM PlazaInsuranceWPDataSet pid
WHERE NOT EXISTS (SELECT PolicyNumber FROM #PolicyNumbers pn WHERE pn.PolicyNumber = pid.PolicyNumber)
AND State IN (SELECT * FROM [dbo].[StringOfStringsToTable](@State,','))
AND Coverage IN (SELECT * FROM [dbo].[StringOfStringsToTable](@Coverage,','))
)
…
这里我要添加我对存储过程的完整查询:
ALTER PROCEDURE [dbo].[EarningPlazaCommercial]
@State varchar(50),
@StartDate datetime,
@EndDate datetime,
@AsOfDate datetime,
@ClassCode nvarchar(max),
@Coverage varchar(100)
AS
BEGIN
SET NOCOUNT ON;
CREATE TABLE #PolicyNumbers (PolicyNumber varchar(50))
INSERT INTO #PolicyNumbers SELECT PolicyNumber FROM tblClassCodesPlazaCommercial T1
WHERE NOT EXISTS (
SELECT 1 FROM tblClassCodesPlazaCommercial T2
WHERE T1.PolicyNumber = T2.PolicyNumber
AND ClassCode IN
(SELECT * FROM [dbo].[StringOfStringsToTable](@ClassCode,','))
)
CREATE CLUSTERED INDEX IDX_C_PolicyNumbers_PolicyNumber ON #PolicyNumbers(PolicyNumber)
; WITH Earned_to_date AS (
SELECT Cast(@AsOfDate AS DATE) AS Earned_to_date
--SELECT @AsOfDate AS Earned_to_date
), policy_data AS (
SELECT
PolicyNumber
, Cast(PolicyEffectiveDate AS DATE) AS PolicyEffectiveDate
, Cast(PolicyExpirationDate AS DATE) AS PolicyExpirationDate
, WrittenPremium
--, State
FROM PlazaInsuranceWPDataSet pid
WHERE NOT EXISTS (SELECT PolicyNumber FROM #PolicyNumbers pn WHERE pn.PolicyNumber = pid.PolicyNumber)
AND State IN (SELECT * FROM [dbo].[StringOfStringsToTable](@State,','))
AND Coverage IN (SELECT * FROM [dbo].[StringOfStringsToTable](@Coverage,','))
)
, digits AS (
SELECT digit
FROM (VALUES (0), (1), (2), (3), (4)
, (5), (6), (7), (8), (9)) AS z2 (digit)
), numbers AS (
SELECT 1000 * d4.digit + 100 * d3.digit + 10 * d2.digit + d1.digit AS number
FROM digits AS d1
CROSS JOIN digits AS d2
CROSS JOIN digits AS d3
CROSS JOIN digits AS d4
), calendar AS (
SELECT
DateAdd(month, number, '1753-01-01') AS month_of
, DateAdd(month, number, '1753-02-01') AS month_after
FROM numbers
), policy_dates AS (
SELECT
PolicyNumber
, CASE
WHEN month_of < PolicyEffectiveDate THEN PolicyEffectiveDate
ELSE month_of
END AS StartRiskMonth
, CASE
WHEN PolicyExpirationDate < month_after THEN PolicyExpirationDate
WHEN Earned_to_date.Earned_to_date < month_after THEN Earned_to_date
ELSE month_after
END AS EndRiskMonth
, DateDiff(day, PolicyEffectiveDate, PolicyExpirationDate) AS policy_days
, WrittenPremium
FROM policy_data
JOIN calendar
ON (policy_data.PolicyEffectiveDate < calendar.month_after
AND calendar.month_of < policy_data.PolicyExpirationDate)
CROSS JOIN Earned_to_date
WHERE month_of < Earned_to_date
)
SELECT --PolicyEffectiveDate,
--PolicyExpirationDate,
--PolicyNumber,
Year(StartRiskMonth) as YearStartRisk,
Month(StartRiskMonth) as MonthStartRisk,
c.YearNum,c.MonthNum,
convert(varchar(7), StartRiskMonth, 120) as RiskMonth,
sum(WrittenPremium * DateDiff(day, StartRiskMonth, EndRiskMonth) / policy_days) as EarnedPremium
FROM tblCalendar c
LEFT JOIN policy_dates l ON c.YearNum=Year(l.StartRiskMonth) and c.MonthNum = Month(l.StartRiskMonth) AND l.StartRiskMonth BETWEEN @StartDate AND @EndDate
WHERE c.YearNum Not IN (2017) --and PolicyNumber = 'PACA1000191-00'
GROUP BY convert(varchar(7), StartRiskMonth, 120),
Year(StartRiskMonth) , Month(StartRiskMonth),
c.YearNum,c.MonthNum--,PolicyNumber--,PolicyEffectiveDate,PolicyExpirationDate
ORDER BY c.YearNum,c.MonthNum
--convert(varchar(7), StartRiskMonth, 120)
DROP TABLE #PolicyNumbers
END
GO
来自生产链接的完整实际执行计划:
最佳答案 我认为问题出在你的“日历”子查询中.它返回10000行而没有任何索引.也许你的实际日期范围在1950年到2033年之间:
试试这个
ALTER PROCEDURE [dbo].[EarningPlazaCommercial]
@State varchar(50),
@StartDate datetime,
@EndDate datetime,
@AsOfDate datetime,
@ClassCode nvarchar(max),
@Coverage varchar(100)
AS
BEGIN
SET NOCOUNT ON;
CREATE TABLE #PolicyNumbers (PolicyNumber varchar(50))
INSERT INTO #PolicyNumbers
SELECT PolicyNumber
FROM tblClassCodesPlazaCommercial T1
WHERE NOT EXISTS (SELECT 1
FROM tblClassCodesPlazaCommercial T2
WHERE T1.PolicyNumber = T2.PolicyNumber
AND ClassCode IN (SELECT *
FROM [dbo].[StringOfStringsToTable](@ClassCode,','))
)
CREATE CLUSTERED INDEX IDX_C_PolicyNumbers_PolicyNumber
ON #PolicyNumbers(PolicyNumber)
DECLARE @Calendar TABLE (
month_of DATE,
month_after DATE,
PRIMARY KEY (month_of, month_after)
);
WITH digits AS
(
SELECT digit
FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)) AS z2 (digit)
), numbers AS (
SELECT 100 * d3.digit + 10 * d2.digit + d1.digit AS number
FROM digits AS d1
CROSS JOIN digits AS d2
CROSS JOIN digits AS d3
), calendar AS
(
SELECT
DateAdd(month, number, '1950-01-01') AS month_of,
DateAdd(month, number, '1950-02-01') AS month_after
FROM numbers
)
insert into @Calendar
select *
from calendar
; WITH policy_data AS
(
SELECT
PolicyNumber,
Cast(PolicyEffectiveDate AS DATE) AS PolicyEffectiveDate,
Cast(PolicyExpirationDate AS DATE) AS PolicyExpirationDate,
WrittenPremium
--, State
FROM
PlazaInsuranceWPDataSet pid
WHERE
NOT EXISTS (SELECT PolicyNumber FROM #PolicyNumbers pn
WHERE pn.PolicyNumber = pid.PolicyNumber)
AND State IN (SELECT * FROM [dbo].[StringOfStringsToTable](@State,','))
AND Coverage IN (SELECT * FROM [dbo].[StringOfStringsToTable](@Coverage,','))
), policy_dates AS
(
SELECT
PolicyNumber,
CASE
WHEN month_of < PolicyEffectiveDate THEN PolicyEffectiveDate
ELSE month_of
END AS StartRiskMonth,
CASE
WHEN PolicyExpirationDate < month_after THEN PolicyExpirationDate
WHEN Earned_to_date.Earned_to_date < month_after THEN Earned_to_date
ELSE month_after
END AS EndRiskMonth,
DateDiff(day, PolicyEffectiveDate, PolicyExpirationDate) AS policy_days,
WrittenPremium
FROM
policy_data
JOIN
@calendar calendar ON (policy_data.PolicyEffectiveDate < calendar.month_after
AND calendar.month_of < policy_data.PolicyExpirationDate)
WHERE
month_of < Cast(@AsOfDate AS DATE)
)
SELECT --PolicyEffectiveDate,
--PolicyExpirationDate,
--PolicyNumber,
Year(StartRiskMonth) as YearStartRisk,
Month(StartRiskMonth) as MonthStartRisk,
c.YearNum, c.MonthNum,
convert(varchar(7), StartRiskMonth, 120) as RiskMonth,
sum(WrittenPremium * DateDiff(day, StartRiskMonth, EndRiskMonth) / policy_days) as EarnedPremium
FROM
tblCalendar c
LEFT JOIN
policy_dates l ON c.YearNum = Year(l.StartRiskMonth)
AND c.MonthNum = Month(l.StartRiskMonth)
AND l.StartRiskMonth BETWEEN @StartDate AND @EndDate
WHERE
c.YearNum Not IN (2017) --and PolicyNumber = 'PACA1000191-00'
GROUP BY
convert(varchar(7), StartRiskMonth, 120),
Year(StartRiskMonth), Month(StartRiskMonth),
c.YearNum,
c.MonthNum --,PolicyNumber
--,PolicyEffectiveDate,PolicyExpirationDate
ORDER BY
c.YearNum,c.MonthNum
--convert(varchar(7), StartRiskMonth, 120)
DROP TABLE #PolicyNumbers
END
GO
如果它工作,问题确实在“日历”子查询中.
修复它的想法:
>返回表的TVP仅包含策略活动月份(我已更改了最后一行).我想这将是几行
SELECT
PolicyNumber,
CASE
WHEN month_of < PolicyEffectiveDate THEN PolicyEffectiveDate
ELSE month_of
END AS StartRiskMonth,
CASE
WHEN PolicyExpirationDate < month_after THEN PolicyExpirationDate
WHEN Earned_to_date.Earned_to_date < month_after THEN Earned_to_date
ELSE month_after
END AS EndRiskMonth,
DateDiff(day, PolicyEffectiveDate, PolicyExpirationDate) AS policy_days,
WrittenPremium
FROM
policy_data
OUTER APPLY
TableFunction_ListOfMonth (PolicyEffectiveDate, PolicyExpirationDate)
WHERE
month_of < CAST(@AsOfDate AS DATE)
>将子查询的结果放在具有聚簇索引的表变量中
DECLARE @Calendar TABLE (
month_of DATE,
month_after DATE,
PRIMARY KEY (month_of, month_after)
);
WITH digits AS (
SELECT digit
FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)) AS z2 (digit)
), numbers AS (
SELECT 100 * d3.digit 10 * d2.digit d1.digit AS编号
FROM数字AS d1
CROSS JOIN数字AS d2
CROSS JOIN数字AS d3
),日历AS(
选择
DateAdd(月,号码,’1950-01-01′)AS month_of
,DateAdd(月,号,’1950-02-01′)AS month_after
从数字
)
插入@Calendar
从日历中选择*