2016-11-18 127 views
0

查询的第一部分获取PolicyPremiums,EffectiveExpiration日期。 第二部分创建Calendar,第三部分(最终SELECT声明)返回的收益细分为MonthYear 一切正常,只需3秒即可显示结果。 但后来我需要过滤什么PolicyNumber的工作,基本上我需要摆脱PolicyNumber的没有@ClassCode。因此,对于在查询的第一部分,我把WHERE条款:如何提高使用子查询时的查询性能

WHERE State IN ('CA','NV','AZ')   
     AND PolicyNumber IN (
          SELECT PolicyNumber 
          FROM tblClassCodesPlazaCommercial 
          GROUP BY PolicyNumber 
          HAVING COUNT (CASE WHEN ClassCode NOT IN (@ClassCode)                  
THEN 1 END)=0 
          ) 

Thanksful到@Prdp用户我有这样的说法:Case语句将产生1对存在于列表中其他NULL将是ClassCode产生。现在,每个PolicyNumber的计数聚合将计为1。通过设置= 0,我们可以确保PolicyNumber在给定列表中没有任何ClassCode。

查询永久性后,在SSRS报告中可以有多于的200 ClassCodes

有趣的是,这两个语句单独工作正常。但是,当我使用它们在一起(这是摆在cte policy_dataWHERE条款则执行需要永远。 是没有办法告诉引擎进行查询,这是

; WITH Earned_to_date AS (
    SELECT Cast(EOMONTH (GETDATE(), -1) AS DATE) 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 
     WHERE State IN ('CA','NV','AZ') 
    /* -------This statement gives me trouble ----------------------*/ 
     AND PolicyNumber IN (
          SELECT PolicyNumber 
          FROM tblClassCodesPlazaCommercial 
          GROUP BY PolicyNumber 
          HAVING COUNT (CASE WHEN ClassCode NOT IN (5151)                  
THEN 1 END)=0 
          ) 
) 

,然后计算和突破的第一部分下来收益只有那些已被过滤政策

我的整个代码如下:

; WITH Earned_to_date AS (
    SELECT Cast(EOMONTH (GETDATE(), -1) AS DATE) 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 
     WHERE State IN ('CA','NV','AZ') 
    /* -------This statement gives me trouble ----------------------*/ 
     AND PolicyNumber IN (
          SELECT PolicyNumber 
          FROM tblClassCodesPlazaCommercial 
          GROUP BY PolicyNumber 
          HAVING COUNT (CASE WHEN ClassCode NOT IN (@ClassCode)                  
THEN 1 END)=0 
          ) 
) 
, 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  
      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 '01-01-2015' AND '12-31-2016' 
WHERE  c.YearNum Not IN (2017) 
GROUP BY convert(varchar(7), StartRiskMonth, 120), 
      Year(StartRiskMonth) , Month(StartRiskMonth), 
      c.YearNum,c.MonthNum 
ORDER BY c.YearNum,c.MonthNum 

什么将是提高性能的最佳方式? 10我在两张表上创建了non-clustered索引PolicyNumber。但仍然没有。 就像我说的,在我看来,如果SQL引擎会处理需要3秒的第一部分(PolicyNumber筛选),然后执行另外3秒的部分(计算这些PolicyNumber的部分) - 这太棒了。 但我是DBA的新手,所以我不确定它有可能。 有什么建议吗? 感谢

执行计划::

enter image description here 最终结果:

enter image description here

+0

Codereview.stackexchange.com是这个 – scsimon

回答

0

你必须使你的代码更易读。通过临时表而不是cte将它分成更小的块。

你的麻烦:

HAVING COUNT (CASE WHEN ClassCode NOT IN (5151) THEN 1 END)=0 

如果ClassCode是5000在您具有含COUNT(TRUE - > 1)。

如果ClassCode在您有HAVING COUNT(FALSE - > NULL)时为5151。

应组之前进行过滤:

WHERE ClassCode IN (5151) -- and check index 
+0

表一个更好的地方'tblClassCodesPlazaCommercial'可是没有'NULL ClassCode'。 所以会表现会得到更好的,如果我用临时表打破呢? 谢谢 – Oleg

+0

当你分割你的代码时,它会更具可读性,并且你快速改变执行计划 - 你将更容易得到你的性能问题或者你的qry没有返回正确数据的地方。 – Deadsheep39