6

The SP runs very slow. When I look at execution plan - I can see that 83% of its cost goes to Nested Loops (Inner Join) enter image description here

Is any chance to substitute it somehow?

Here is my 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,','))        
) 

...

--Part of the Execution Plan enter image description here

enter image description here

Here I am adding my full query for Stored Procedure:

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

Full actual execution plan from production link:

https://aligngeneral-my.sharepoint.com/personal/oserdyuk_aligngeneral_com/_layouts/15/guestaccess.aspx?guestaccesstoken=VuiFBK6zMim%2fyIh%2bNrQaOcgrg%2fpIJNKDTStt765cBfQ%3d&docid=1abc31e385da14574a930e99e22f00c7b&rev=1&expiration=2017-01-06T22%3a20%3a34.000Z

And this is how my TempDB configured: enter image description here

Serdia
  • 4,242
  • 22
  • 86
  • 159
  • post the full query and full execution plan. We can change the `Join strategy` by using `Join Hints` but it is not advisable – Pரதீப் Dec 07 '16 at 16:25
  • Not sure it is problem with nested loop join... If you are updating the whole execution plan then we can look for appropriate performance solution... – Kannan Kandasamy Dec 07 '16 at 16:32
  • what would be the best way to post execution plan on stackoverflow.com? Picture snip? Thanks – Serdia Dec 07 '16 at 16:44
  • @Oleg - Post it as image – Pரதீப் Dec 07 '16 at 16:58
  • Added one part of it – Serdia Dec 07 '16 at 17:03
  • @Oleg when you move your mouse over the nested loop part what does the hint tell you? as in predicate, object etc... – Fuzzy Dec 07 '16 at 17:05
  • Using table valued functions are generally expensive I would avoid them if I could – Fuzzy Dec 07 '16 at 17:18
  • The nested loop is as a result of the NOT EXISTS have a look at this article https://sqlperformance.com/2012/12/t-sql-queries/left-anti-semi-join – Fuzzy Dec 07 '16 at 17:22
  • there is also plenty of room to improve performance after that for instance try avoiding using YEAR(), MONTH() functions in the where clause because if the columns referenced are indexed it renders the index useless – Fuzzy Dec 07 '16 at 17:23
  • try using OUTER APPLY instead of NOT EXISTS – Fuzzy Dec 07 '16 at 17:26
  • Instead of image can you save the execution plan as .sqlplan or xml file and attach it here in SO? – Kannan Kandasamy Dec 07 '16 at 19:59
  • https://aligngeneral-my.sharepoint.com/personal/oserdyuk_aligngeneral_com/_layouts/15/guestaccess.aspx?guestaccesstoken=yIc%2bFQZ%2bPJ%2bhLa0%2bb%2f8P3QRaO%2fP02bxrpagNjdWorLI%3d&docid=1dbe3aa8c6d75461cbe7d6accd6e0dfff&rev=1&expiration=2017-01-06T20%3a41%3a00.000Z – Serdia Dec 07 '16 at 20:41
  • I added a link. Let me know if it wont work. Thanks – Serdia Dec 07 '16 at 20:41
  • This is an estimated execution plan... Can you provide the actual execution plan? – Kannan Kandasamy Dec 07 '16 at 21:26
  • Actual bottleneck is due to the table spool of 3000 records each of 879k records with 293 iterations... How tempdb is configured? – Kannan Kandasamy Dec 07 '16 at 21:28
  • #Kannan Kandasamy I have added actual execution plan from production (Link) and also snip of TempDB configuration. Let me know if anything else? Thank you – Serdia Dec 07 '16 at 22:25
  • #Kannan, could you please tell me your opinion about how my TempDB configured? Thanks – Serdia Dec 08 '16 at 17:57

2 Answers2

1

I think problem is in your "calendar" subquery. It returns 10000 rows without any index. Maybe your actual date range between 1950 and 2033:

Try this

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

If it works, problem indeed is in "calendar" subquery.

Ideas to fix it:

  1. TVP that returns a table contains only policy active months (I've changed last rows). I think it will be few rows

     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)
    
  2. put results of your subquery in table variable with clustered index

     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
    
Thiago Baldim
  • 7,362
  • 3
  • 29
  • 51
Mikhail Lobanov
  • 2,976
  • 9
  • 24
  • Mikhail, I've changed `DateAdd(month, number, '2010-01-01') AS month_of,` `DateAdd(month, number, '2010-02-01') AS month_after` And it went down from spinning forever to 12 sec. Hvent try to put it into `@calendar` yet. But should I assume that this is the issue? – Serdia Dec 07 '16 at 22:45
  • I edit my code to increase date range from 2010-2020 to 1950-2033 – Mikhail Lobanov Dec 08 '16 at 05:44
  • My first idea was to decrease number of rows in calendar subquery. – Mikhail Lobanov Dec 08 '16 at 05:46
  • You should try reduce calendar date range from 1753-2500 to your actual values (just for test). – Mikhail Lobanov Dec 08 '16 at 05:49
  • My source table only containing dates from 2012 till now. So I guess I don't need huge calendar. I am getting the result in 4 seconds now! Which is good. I got a little confused with `@Calendar`. After I insert the `month_of` and `month_after` dates? Should I just `JOIN` it later in my query? Mikhail, I'll be honest, this part of the query wasn't written by me. It was written by someone who has loooots of experience. – Serdia Dec 08 '16 at 16:29
  • I mean the calendar part of the query. – Serdia Dec 08 '16 at 16:38
  • You should try to use @Calendar instead of "calendar" subquery (yes, join it) – Mikhail Lobanov Dec 08 '16 at 19:00
  • But where can I populate `@Calendar`? I cannot break the sequence of `CTE`'s, correct? It gives me an error if I do this after `calendar`. Sorry, I am confused a little – Serdia Dec 08 '16 at 20:37
  • I've edited my first example. Now it includes entire procedure. – Mikhail Lobanov Dec 08 '16 at 21:04
  • You need 2 CTE's. First one populates @Calendar, second do all work – Mikhail Lobanov Dec 08 '16 at 21:06
  • Thank you very much. I commented out `WHEN Earned_to_date.Earned_to_date < month_after THEN Earned_to_date` because it was giving me an error. And I dont need it, correct? Using @Calendar unfortunately didnt improve performance, but still much faster than originally. Interesting thing that if I will not use SP and just `SELECT` statement than it gives me result faster. I always thought that SP have faster performance – Serdia Dec 09 '16 at 00:22
  • Try 'WHEN CAST(@AsOfDate AS DATE) < month_after THEN CAST(@AsOfDate AS DATE)', but it does not affets perfomance. – Mikhail Lobanov Dec 09 '16 at 05:54
0

Try this:

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
                          OUTER APPLY
                                   (SELECT PolicyNumber
                                    FROM   #PolicyNumbers pn
                                    WHERE  pn.PolicyNumber = pid.PolicyNumber) AS pn
        WHERE pn.PolicyNumber IS NULL
            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;

I essentially changed:

policy_data
    AS (SELECT PolicyNumber
            , CAST(PolicyEffectiveDate AS DATE) AS PolicyEffectiveDate
            , CAST(PolicyExpirationDate AS DATE) AS PolicyExpirationDate
            , WrittenPremium
        --,         State
        FROM   PlazaInsuranceWPDataSet pid OUTER APPLY
        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, ','))),

To:

policy_data
AS (SELECT                 PolicyNumber
                    , CAST(PolicyEffectiveDate AS DATE) AS PolicyEffectiveDate
                    , CAST(PolicyExpirationDate AS DATE) AS PolicyExpirationDate
                    , WrittenPremium
    --,         State
    FROM                   PlazaInsuranceWPDataSet pid
                      OUTER APPLY
                               (SELECT PolicyNumber
                                FROM   #PolicyNumbers pn
                                WHERE  pn.PolicyNumber = pid.PolicyNumber) AS pn
    WHERE pn.PolicyNumber IS NULL
        AND State IN
                  (SELECT *
                   FROM   [dbo].[StringOfStringsToTable]
                       (@State, ','))
        AND Coverage IN
                    (SELECT *
                     FROM   [dbo].[StringOfStringsToTable]
                         (@Coverage, ','))),
Fuzzy
  • 3,810
  • 2
  • 15
  • 33
  • Thanks. Tried. But unfortunately it still spinning. Nested Loops (Inner Join) went down to 80%, instead of 83. – Serdia Dec 07 '16 at 17:42