SQL Server查询使用另一个查询的结果加入表

时间:2014-04-14 20:54:59

标签: sql sql-server relational-database common-table-expression

我正在使用包含事件票据的数据表(第一个变量声明中引用的dbo.IncidentDimvw

我正在尝试生成一个查询,该查询将枚举从最早的票证到现在的每个日期,以及每天“打开”和“关闭”的票数(可能是0),以及每天的票数“打开”(与“已打开”不同)。

为了实现这一点,我需要一个独立于我的票证数据表的日期列表(即日历)。我能够使用一些CTE来完成这项工作,在查询中动态构建日期列表。

这部分完美无瑕,你可以测试下面的代码,如果你想看到它的实际效果(为@SSOReportDateRangeBegin提供一个恒定的值)。

我的当前查询如下所示,带有描述性注释。它返回一个完整的日历,每个日期从2012年12月27日到当前日期(可扩展到十年)。

我遇到的问题是,我无法弄清楚如何将dbo.IncidentDimvw加入此“日历”,以便拉开已开票,已关闭且打开的票数在每个日期。
CTE和DISTINCT选择使我很难理解这些连接是如何构建的。

我真正想要的是将日历CTE的最终结果选为普通旧数据集,我可以从中比较故障单 - 从而将日历创建的逻辑与逻辑分开选票。

我已尝试LEFT JOIN - dbo.IncidentDimvw到日历查询,但是当我选择
COUNT(OpenedInc.ID) AS 'Incidents Opened'结果计数太大 - 这表明日历逻辑正在干扰票证选择逻辑 - 或者在这种情况下我可能不理解如何正确GROUP

/* Begin our date range with the earliest
   incident ticket creation date on record. (currently 12/27/2012)
   CAST is necessary because the CreatedDate column is a datetime value,
   and we specifically want to truncate the "time" portion
   for the purposes of this query.*/
DECLARE @SSOReportDateRangeBegin AS DATE =
    (SELECT MIN(CAST(inc.CreatedDate AS DATE)) FROM dbo.IncidentDimvw inc);

/* State how many years to include in our date range.
   Currently our range includes 10 years. (12/27/2012 - 12/26/2022)
   Later on, our selection will limit results to only dates <=CAST(GETDATE() AS DATE) */
DECLARE @SSOReportDateRangeYears AS INT = 10;
/* @SSOReportDateRangeYears cannot be greater than 31.
   For larger time periods, add CTEs for decades, centuries, millenia, etc. */

/* Calendar calculater based on a row counter with 31 rows.
   Calculates day 1 through day 31 for all 12 months, for 10 years.

   Since some months don't have 31 days, some DATEADD calculations
   will rollover to the next month, causing duplicate date values.
   (e.g. Feb 31st will be interpreted as Mar 3rd in non-leap year,
   Mar 2nd in leap-year, duplicating the date values for March 2nd or 3rd)

   SELECT DISTINCT is used to eliminate duplicate date values,
   giving us a clean and complete calendar to work with. */
WITH [counter](N) AS
(SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1),
SSOReportDays(N) AS (SELECT row_number() OVER (ORDER BY (SELECT NULL)) FROM [counter]),
SSOReportMonths(N) AS (SELECT N - 1 FROM SSOReportDays WHERE N <= 12),
SSOReportYears(N) AS (SELECT N - 1 FROM SSOReportDays WHERE N <= @SSOReportDateRangeYears)

SELECT DISTINCT

CAST(DATEADD(DAY, SSOReportDays.n,
     DATEADD(MONTH, SSOReportMonths.n,
     DATEADD(YEAR, SSOReportYears.n,
     DATEADD(DAY, -1, @SSOReportDateRangeBegin)))) AS DATE) AS CalendarDate
     /* Subtract 1 day from @SSOReportDateRangeBegin,
        because the counter will begin with the following day. */

/* CROSS JOIN to compute every possible combination
   of day(1-31) month(1-12) and year(1-10) */
FROM SSOReportYears CROSS JOIN SSOReportMonths CROSS JOIN SSOReportDays

/* Reduce calendar to <= the current date,
   since incident tickets cannot be created with future dates. */
WHERE CAST(DATEADD(DAY, SSOReportDays.n,
           DATEADD(MONTH, SSOReportMonths.n,
           DATEADD(YEAR, SSOReportYears.n,
           DATEADD(DAY, -1, @SSOReportDateRangeBegin)))) AS DATE) <= CAST(GETDATE() AS DATE)
           /* Subtract 1 day from @SSOReportDateRangeBegin,
              because the counter will begin with the following day. */

1 个答案:

答案 0 :(得分:1)

没有什么能阻止您在查询中添加更多CTE。您已经有4个CTE,为日历日期添加第五个CTE,为表中的Ticket Counts添加第六个CTE,然后左键加入Calendar CTE并显示票证计数:

WITH [counter](N) AS
(SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
 SELECT 1),
SSOReportDays(N) AS (SELECT row_number() OVER (ORDER BY (SELECT NULL)) FROM [counter]),
SSOReportMonths(N) AS (SELECT N - 1 FROM SSOReportDays WHERE N <= 12),
SSOReportYears(N) AS (SELECT N - 1 FROM SSOReportDays WHERE N <= @SSOReportDateRangeYears),
Calendar AS(
SELECT DISTINCT
CAST(DATEADD(DAY, SSOReportDays.n,
     DATEADD(MONTH, SSOReportMonths.n,
     DATEADD(YEAR, SSOReportYears.n,
     DATEADD(DAY, -1, @SSOReportDateRangeBegin)))) AS DATE) AS CalendarDate
     /* Subtract 1 day from @SSOReportDateRangeBegin,
        because the counter will begin with the following day. */

/* CROSS JOIN to compute every possible combination
   of day(1-31) month(1-12) and year(1-10) */
FROM SSOReportYears CROSS JOIN SSOReportMonths CROSS JOIN SSOReportDays

/* Reduce calendar to <= the current date,
   since incident tickets cannot be created with future dates. */
WHERE CAST(DATEADD(DAY, SSOReportDays.n,
           DATEADD(MONTH, SSOReportMonths.n,
           DATEADD(YEAR, SSOReportYears.n,
           DATEADD(DAY, -1, @SSOReportDateRangeBegin)))) AS DATE) <= CAST(GETDATE() AS DATE)),
TicketCount AS (
SELECT TicketDate, Status, Count(*) cnt -- These are just fake columns. Use your columns
FROM dbo.IncidentDimvw
GROUP BY TicketDate, Status)
SELECT c.CalendarDate, O.cnt, C.cnt, E.cnt
FROM Calendar c
LEFT JOIN TicketCount O ON c.CalendarDate = O.TicketDate AND O.Status = 'Open'
LEFT JOIN TicketCount C ON c.CalendarDate = C.TicketDate AND C.Status = 'Close'
LEFT JOIN TicketCount E ON c.CalendarDate = E.TicketDate AND E.Status = 'etc.' -- keep adding until you get all required statuses
ORDER BY 1