0

I hope my description is clear. I have a single view that contains entries based on Level and Status. I wrote a query that counts each Level by its type and Status and then calculates the percent of the total of each Level within a given year using multiple Select statements in a single query. I based my query on this logic: Multiple select statements in a Single query.

My logic:

SELECT Table1.[Level Description]
     , Table1.[Level Status]
     , Table1.[Level Count]
     , Table2.[Level Total]
     , CAST(Table1.[Level Count] / Table2.[Level Total] AS DECIMAL(3, 2)) * 100 AS 'LVL %'
FROM
(
    SELECT LEVEL_DESC AS 'Level Description'
         , LEVEL_STATUS 'Level Status'
         , (CASE
                WHEN LEVEL_DESC IN ('LEVEL 1', 'LEVEL 2', 'LEVEL 3', 'LEVEL 4')
                     AND LEVEL_STATUS IN ('A', 'I', 'R') THEN
                    COUNT(LEVEL_STATUS) * 1.00
                ELSE
                    0
            END
           ) AS 'Level Count'
    FROM dbo.My_Level_View
    WHERE (DUE_DATE BETWEEN '2019-01-01' AND '2019-12-31')
          AND LEVEL_SUB = 'I'
    GROUP BY LEVEL_DESC
           , LEVEL_STATUS
) AS [Table1]
, (
      SELECT LEVEL_DESC AS 'Total Description'
           , (CASE
                  WHEN LEVEL_DESC IN ('LEVEL 1', 'LEVEL 2', 'LEVEL 3', 'LEVEL 4') THEN
                      COUNT(LEVEL_STATUS) * 1.00
                  ELSE
                      0
              END
             ) AS 'Level Total'
      FROM dbo.My_Level_View
      WHERE (DUE_DATE BETWEEN '2019-01-01' AND '2019-12-31')
            AND LEVEL_SUB = 'I'
      GROUP BY LEVEL_DESC
  ) AS [Table2]
WHERE Table1.[Level Description] = Table2.[Total Description];

The query works well and gives me the result I need:

Level Results

However, I would like to extend it to work with any record within a two year window from today's date and group the results by year.

I have tried using

WHERE DATEDIFF(YEAR, DUE_DATE, GETDATE()) < 2

in place of the hard coded date range

WHERE (DUE_DATE BETWEEN '2019-01-01' AND '2019-12-31')

When I do this it counts each Level by it's Type within the the last two years and then performs the calculations. I would like to separate the results by year, e.g., 2019 and 2020 as shown in the image.

What I have scripted may not be the best solution. How can I make my query separate the results by year?

3
  • I wasn't very clear, sorry. I have tried to clarify my question. Commented Aug 13, 2020 at 21:03
  • There is no year indicator in image. But why not simply add Year(DUE_DATE) in GROUP BY clause? Commented Aug 13, 2020 at 22:14
  • Thank you. I hadn't considered it. This gets me closer to where I want to be. I am also studying your answer below. The concept is a little over my head. Commented Aug 13, 2020 at 22:45

1 Answer 1

0

Consider using DATEADD(YEAR, -2, GETDATE()) for more precise date range since DATEDIFF(YEAR, DUE_DATE, GETDATE()) < 2 returns dates by year rounding. Plus, you should set an upper range to avoid dates after which DATEDIFF will return as negative. Also, use a single aggregate query with window function for total.

SELECT agg.[Year]
     , agg.[Level Description]
     , agg.[Level Status]
     , agg.[Level Count]
     , SUM(agg.[Level Count])  OVER (PARTITION BY agg.[Year]
                                                  , agg.[Level Description]
                                                  , agg.[Level Status]) AS [Level Total]
     , CAST(agg.[Level Count] / (SUM(agg.[Level Count]) 
                                 OVER (PARTITION BY agg.[Year]
                                                    , agg.[Level Description],
                                                    , agg.[Level Status]))
             AS DECIMAL(3, 2)) * 100 AS 'LVL %'
FROM
(
    SELECT YEAR(DUE_DATE) AS [Year]
         , LEVEL_DESC AS [Level Description]
         , LEVEL_STATUS AS [Level Status]
         , SUM(CASE
                   WHEN LEVEL_DESC IN ('LEVEL 1', 'LEVEL 2', 'LEVEL 3', 'LEVEL 4')
                        AND LEVEL_STATUS IN ('A', 'I', 'R') 
                   THEN 1.00
                   ELSE 0.00
               END) AS [Level Count]
    FROM dbo.My_Level_View
    WHERE DUE_DATE BETWEEN DATEADD(YEAR, -2, GET_DATE()) AND GET_DATE()
      AND LEVEL_SUB = 'I'
    GROUP BY YEAR(DUE_DATE)
           , LEVEL_DESC
           , LEVEL_STATUS
) AS agg

Should you need to supply any dynamic date, declare or pass parameter with a stored procedure:

DECLARE @paramDate datetime = '2020-01-01';
...    
     WHERE DUE_DATE BETWEEN DATEADD(YEAR, -2, @paramDate) AND @paramDate
...

Aside - best practice in SQL is to use explicit joins with multiple subqueries or tables and heed Bad Habits to Kick : Using table aliases like (a, b, c) or (t1, t2, t3).

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you, @Parfait. I was able to apply your answer with one tweak and make this work.
Excellent to hear and glad to help! Happy coding!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.