合并数据透视表查询中的行

时间:2013-04-22 15:02:38

标签: sql sql-server sql-server-2008

以下SQL查询应该显示教师可用性。有4种可能的预订类型 - 上午,下午,全天和每小时。如果有AM预订,单元格中的文本应显示PM,如果有PM预订,则应显示AM,如果有全天预订,或者AM和PM预订,则应显示'xxx'。一切正常。

现在,我继续进行每小时预订。可以安全地假设每天只有2小时预订,AM为1(开始时间<= 12pm),PM为1(结束时间> 12pm),这意味着我们应该显示'xxx' 。但是我真的很难让这个显示出来。

WITH Bookings AS
(   SELECT  TeacherID,
            [WeekDay] = DATENAME(WEEKDAY, BookingDate),
            [Status] = CASE 
                            WHEN [3] > 1 THEN 'XXX'
                            WHEN ([0] > 0 AND [1] > 0) THEN 'XXX'
                            WHEN [2] > 0 THEN 'XXX'                            
                            WHEN [0] > 0 THEN 'PM'
                            WHEN [1] > 0 THEN 'AM'
                            WHEN [3] > 0 AND CONVERT(time(0), EndTime) <= CONVERT(time(0), '12:00:00') THEN 'PM'
                            WHEN [3] > 0 AND CONVERT(time(0), StartTime) >= CONVERT(time(0), '12:00:00') THEN 'AM'
                            WHEN [3] > 0 AND CONVERT(time(0), StartTime) <= CONVERT(time(0), '12:00:00') AND CONVERT(time(0), EndTime) >= CONVERT(time(0), '12:00:00') THEN 'XXX'                           
                        END
    FROM    (   SELECT  TeacherID, BookingDate, BookingDuration, StartTime, EndTime, [x] = 1
                FROM    BookingDays where (Status = 0 or Status IS NULL)
            ) BookingDays
            PIVOT
            (   SUM(x)
                FOR BookingDuration IN ([0], [1], [2], [3])
            ) pvt

             WHERE BookingDate >= DATEADD(ww, DATEDIFF(ww,0,'04/22/2013'), 0) AND BookingDate <= DATEADD(ww, DATEDIFF(ww,0,'04/22/2013'), 6)

), PivotedBookings AS
(   SELECT  *
    FROM    Bookings
            PIVOT
            (   MAX([Status])
                FOR [WeekDay] IN ([Monday], [Tuesday], [Wednesday], [Thursday], [Friday])
            ) pvt

)
SELECT ID,Firstname,Surname,Band,'£' + CONVERT(varchar(50),DefaultChargeRateDaily) + '/' + '£' + CONVERT(varchar(50), DefaultPayRateDaily) as 'BandRates',Telephone,Mobile,Teacher,TeacherAssistant,KeyStage,MAX(Monday) Monday,MAX(Tuesday) Tuesday,MAX(Wednesday) Wednesday,MAX(Thursday) Thursday,MAX(Friday) Friday, Notes
  FROM (
SELECT  t.ID, 
        t.Firstname, 
        t.Surname, 
        tb.Band,
        t.DefaultChargeRateDaily,
        t.DefaultPayRateDaily,
        t.Telephone,
        t.Mobile,
        t.Teacher,
        t.TeacherAssistant,
        CASE WHEN t.Nursery > 0 THEN 'NUR' WHEN t.Reception > 0 THEN 'REC' WHEN t.Year1 > 0 THEN 'Y1' WHEN t.Year2 > 0 THEN 'Y2' WHEN t.Year3 > 0 THEN 'Y3' WHEN t.Year4 > 0 THEN 'Y4' WHEN t.Year5 > 0 THEN 'Y5' WHEN t.Year6 > 0 THEN 'Y6' WHEN t.Year7 > 0 THEN 'Y7' WHEN t.Year8 > 0 THEN 'Y8' WHEN t.Year9 > 0 THEN 'Y9' WHEN t.Year10 > 0 THEN 'Y10' WHEN t.Year11 > 0 THEN 'Y11' WHEN t.ALevel > 0 THEN 'ALevel' END + ' - ' + CASE WHEN t.ALevel > 0 THEN 'ALevel' WHEN t.Year11 > 0 THEN 'Y11' WHEN t.Year10 > 0 THEN 'Y10' WHEN t.Year9 > 0 THEN 'Y9' WHEN t.Year8 > 0 THEN 'Y7' WHEN t.Year6 > 0 THEN 'Y6' WHEN t.Year5 > 0 THEN 'Y6' WHEN t.Year4 > 0 THEN 'Y4' WHEN t.Year3 > 0 THEN 'Y3' WHEN t.Year2 > 0 THEN 'Y2' WHEN t.Year1 > 0 THEN 'Y1' WHEN t.Reception > 0 THEN 'REC' WHEN t.Nursery > 0 THEN 'NUR' ELSE '' END as 'KeyStage',

        Monday = CASE WHEN an.Date = DATEADD(ww, DATEDIFF(ww,0,'04/22/2013'), 0) AND an.TeacherID = t.ID THEN an.Text WHEN t.Status = 0 THEN 'XXX'  ELSE COALESCE(pb.Monday, '') END,
        Tuesday = CASE WHEN an.Date = DATEADD(ww, DATEDIFF(ww,0,'04/22/2013'), 1) AND an.TeacherID = t.ID THEN an.Text WHEN t.Status = 0 THEN 'XXX'  ELSE COALESCE(pb.Tuesday, '') END,
        Wednesday = CASE WHEN an.Date = DATEADD(ww, DATEDIFF(ww,0,'04/22/2013'), 2) AND an.TeacherID = t.ID THEN an.Text WHEN t.Status = 0 THEN 'XXX'  ELSE COALESCE(pb.Wednesday, '') END,
        Thursday = CASE WHEN an.Date = DATEADD(ww, DATEDIFF(ww,0,'04/22/2013'), 3) AND an.TeacherID = t.ID THEN an.Text WHEN t.Status = 0 THEN 'XXX'  ELSE COALESCE(pb.Thursday, '') END,
        Friday = CASE WHEN an.Date = DATEADD(ww, DATEDIFF(ww,0,'04/22/2013'), 4) AND an.TeacherID = t.ID THEN an.Text WHEN t.Status = 0 THEN 'XXX'  ELSE COALESCE(pb.Friday, '') END,
        Notes
FROM    Teachers t

        LEFT JOIN PivotedBookings pb
            ON pb.TeacherID = t.ID
        LEFT JOIN TeacherBands tb
            ON tb.ID = t.Band
        LEFT JOIN AvailabilityNotes an 
            ON t.ID = an.TeacherID
            WHERE t.Active = 0 and (t.Status = 1 or t.Status = 0) and t.PrimarySchool = 1
            ) T1
 GROUP BY ID,Firstname,Surname,Telephone,Mobile,Teacher,TeacherAssistant,KeyStage,Notes,DefaultChargeRateDaily,DefaultPayRateDaily,Band
 ORDER BY Surname,Firstname asc

第一部分生成以下内容 -

SELECT  TeacherID,
            [WeekDay] = DATENAME(WEEKDAY, BookingDate),
            [Status] = CASE 
                            WHEN [3] > 1 THEN 'XXX'
                            WHEN ([0] > 0 AND [1] > 0) THEN 'XXX'
                            WHEN [2] > 0 THEN 'XXX'                            
                            WHEN [0] > 0 THEN 'PM'
                            WHEN [1] > 0 THEN 'AM'
                            WHEN [3] > 0 AND CONVERT(time(0), EndTime) <= CONVERT(time(0), '12:00:00') THEN 'PM'
                            WHEN [3] > 0 AND CONVERT(time(0), StartTime) >= CONVERT(time(0), '12:00:00') THEN 'AM'
                            WHEN [3] > 0 AND CONVERT(time(0), StartTime) <= CONVERT(time(0), '12:00:00') AND CONVERT(time(0), EndTime) >= CONVERT(time(0), '12:00:00') THEN 'XXX'                           
                        END
    FROM    (   SELECT  TeacherID, BookingDate, BookingDuration, StartTime, EndTime, [x] = 1
                FROM    BookingDays where (Status = 0 or Status IS NULL)
            ) BookingDays
            PIVOT
            (   SUM(x)
                FOR BookingDuration IN ([0], [1], [2], [3])
            ) pvt

             WHERE BookingDate >= DATEADD(ww, DATEDIFF(ww,0,'04/22/2013'), 0) AND BookingDate <= DATEADD(ww, DATEDIFF(ww,0,'04/22/2013'), 6)

表格列:

TeacherID | WeekDay   | Status
9386    Monday      PM
9386    Tuesday     AM
9386    Wednesday   XXX
9763    Monday      PM
9763    Tuesday     AM
9763    Wednesday   XXX
9927    Monday      PM
9927    Tuesday     AM
9927    Wednesday   XXX
10358   Monday      PM
10358   Monday      AM

我们可以在这里看到,最后两行需要合并并在状态列中显示为XXX。

下面的屏幕截图显示了一个示例。突出显示黄色,显示PM。但是,有2小时预订(BookingDuration ID 3),一个上午10:00 - 上午11:00,另一个14:00 - 15:00。因此,这应该显示XXX,而不是PM / AM。

Screenshot

我希望这是有道理的!

1 个答案:

答案 0 :(得分:1)

您的问题可以追溯到第一个查询:

FROM    (   SELECT  TeacherID, BookingDate, BookingDuration, StartTime, EndTime, [x] = 1
            FROM    BookingDays 
            WHERE   (Status = 0 OR Status IS NULL)
        ) BookingDays
        PIVOT
        (   SUM(x)
            FOR BookingDuration IN ([0], [1], [2], [3])
        ) pvt

这样做的想法是获得一个输出,即每个教师每天有一行,在子查询中添加StartTimeEndTime,不能保证你会得到一行每位教师每天,因为两个记录可能在同一日期有不同的开始/结束时间,为了解决这个问题,您可以使用:

| TeacherID | BookingDate | BookingDuration | StartTime | EndTime |
|-----------+-------------+-----------------+-----------+---------|
|    1      |  20130422   |        3        |   10:00   |  11:00  |
|    1      |  20130422   |        3        |   13:00   |  14:00  |

在预订CTE中,这将返回(如果您为选择列表添加了开始/结束时间,但删除它们不会使其成为一行):

| TeacherID | WeekDay | Status | StartTime | EndTime |
|-----------+---------|--------+-----------+---------|
|    1      |  Monday |   'AM' |   10:00   |  11:00  |
|    1      |  Monday |   'PM' |   13:00   |  14:00  |

如果您使用以下

FROM    (   SELECT  TeacherID, 
                    BookingDate, 
                    BookingDuration, 
                    StartTime = MIN(StartTime) OVER(PARTITION BY TeacherID, BookingDate),
                    EndTime = MIN(StartTime) OVER(PARTITION BY TeacherID, BookingDate)
                    [x] = 1
            FROM    BookingDays 
            WHERE   (Status = 0 OR Status IS NULL)
        ) BookingDays
        PIVOT
        (   SUM(x)
            FOR BookingDuration IN ([0], [1], [2], [3])
        ) pvt

使用窗口函数,您可以确保每个教师/天组合只返回相同的StartTime和EndTime:

| TeacherID | WeekDay | Status | StartTime | EndTime |
|-----------+---------|--------+-----------+---------|
|    1      |  Monday |  'XXX' |   10:00   |  14:00  |

您稍后不需要在查询中合并行。

您需要做的另一项更改是稍微更改Status的CASE语句,以便获得完整查询:

WITH Bookings AS
(   SELECT  TeacherID,
            [WeekDay] = DATENAME(WEEKDAY, BookingDate),
            [Status] = CASE 
                            WHEN ([0] > 0 AND [1] > 0) THEN 'XXX'
                            WHEN [2] > 0 THEN 'XXX'                            
                            WHEN [0] > 0 THEN 'PM'
                            WHEN [1] > 0 THEN 'AM'
                            WHEN [3] > 0 AND StartTime <= CONVERT(TIME, '12:00:00') AND EndTime >= CONVERT(TIME, '12:00:00') THEN 'XXX'   
                            WHEN [3] > 0 AND EndTime <= CONVERT(TIME, '12:00:00') THEN 'PM'
                            WHEN [3] > 0 AND StartTime >= CONVERT(TIME, '12:00:00') THEN 'AM'                        
                        END
    FROM    (   SELECT  TeacherID, 
                        BookingDate, 
                        BookingDuration, 
                        StartTime = CAST(MIN(StartTime) OVER(PARTITION BY TeacherID, BookingDate, BookingDuration) AS TIME),
                        EndTime = CAST(MAX(EndTime) OVER(PARTITION BY TeacherID, BookingDate, BookingDuration) AS TIME),
                        [x] = 1
                FROM    BookingDays 
                WHERE   (Status = 0 OR Status IS NULL)
            ) BookingDays
            PIVOT
            (   SUM(x)
                FOR BookingDuration IN ([0], [1], [2], [3])
            ) pvt
)

我在这里所做的就是删除行WHEN [3] > 1,以防万一发生在AM中,或两者都发生在PM中(我知道你已经说过可以安全地假设这一点不会发生,但为了完整性,我已经添加了它),并且我已经将以下行更高了:

WHEN [3] > 0 AND StartTime <= CONVERT(TIME, '12:00:00') AND EndTime >= CONVERT(TIME, '12:00:00') THEN 'XXX'   

这应该是以您需要的格式获取数据所需的所有更改。

Example on SQL Fiddle(请注意教师5)

修改

正如你所说,如果早上和下午的时段有一个小时的时段,或者反过来,上述情况就会失败。我认为如果你将你的案例陈述改为下面,这应该解决问题。

        [Status] = CASE 
                        WHEN ([0] > 0 AND [1] > 0) THEN 'XXX'
                        WHEN [2] > 0 THEN 'XXX'    
                        WHEN [3] > 0 AND StartTime <= CONVERT(TIME, '12:00:00') AND EndTime >= CONVERT(TIME, '12:00:00') THEN 'XXX' 
                        WHEN [3] > 0 AND StartTime <= CONVERT(TIME, '12:00:00') AND [1] > 0 THEN 'XXX'          
                        WHEN [3] > 0 AND StartTime >= CONVERT(TIME, '12:00:00') AND [0] > 0 THEN 'XXX'
                        WHEN [0] > 0 THEN 'PM'
                        WHEN [1] > 0 THEN 'AM'  
                        WHEN [3] > 0 AND EndTime <= CONVERT(TIME, '12:00:00') THEN 'PM'
                        WHEN [3] > 0 AND StartTime >= CONVERT(TIME, '12:00:00') THEN 'AM'                        
                    END