Select 基于组顺序的新列

Select new columns based on group order

我在这个网站上遇到了很多问题,但找不到解决方案。 我有一个 table:

Date    GroupID CHANNEL
24/02/2015  1   A
26/02/2015  1   B
27/02/2015  1   C
21/03/2015  2   D
20/02/2015  3   E
25/02/2015  3   D
28/02/2015  4   C
04/03/2015  5   B
05/03/2015  5   E
10/03/2015  5   D
11/03/2015  5   A
14/03/2015  5   C
23/03/2015  5   F
28/03/2015  6   E

频道仅限于“A”、“B”、“C”、“D”、“E”、“F”。里面有很多行,不同的GROUPID。

我需要得到这个 table:

Date    GroupID Channel isFirst isLast  Channelsingroup Daysbeforelast
24/02/2015  1       A   TRUE    FALSE   3                   3
26/02/2015  1       B   FALSE   FALSE   3                   1
27/02/2015  1       C   FALSE   TRUE    3                   0
21/03/2015  2       D   TRUE    TRUE    1                   0
20/02/2015  3       E   TRUE    FALSE   2                   5
25/02/2015  3       D   FALSE   FALSE   2                   0
28/02/2015  4       C   TRUE    TRUE    1                   0
04/03/2015  5       B   TRUE    FALSE   6                   19
05/03/2015  5       E   FALSE   FALSE   6                   18
10/03/2015  5       D   FALSE   FALSE   6                   13
11/03/2015  5       A   FALSE   FALSE   6                   12
14/03/2015  5       C   FALSE   FALSE   6                   9
23/03/2015  5       F   FALSE   FALSE   6                   0
28/03/2015  6       E   TRUE    TRUE    1                   0

IsFirst = TRUE当Channel在按Time排序的ID相同的行组中排在第一位时;否则为假。

IsLast = TRUE 当Channel是按时间排序的具有相同GroupID的行组中的最后一个时;否则为假。

Channelsingroup - 同一组中的行数(具有相同的 GroupID)

Daysbeforelast - 组中最新行与当前行之间的天数差异。

我无权创建或更新 table,只能创建或更新 select。

我希望以上数据有意义,有任何问题请告诉我。

您可以使用 ROW_NUMBER 获取 isFirstisLast 列,使用 COUNT(*) OVER() 获取 ChannelsInGroup。此外,您可以使用 CROSS APPLY 来计算 DaysBeforeLast:

SQL Fiddle

WITH Cte AS(
    SELECT *,
        RN = ROW_NUMBER() OVER(PARTITION BY GroupID ORDER BY [Date]),
        CC = COUNT(*) OVER(PARTITION BY GroupID)
    FROM TestData
)
SELECT
    c.[Date],
    c.GroupID,
    c.Channel,
    isFirst         = CASE WHEN c.RN = 1 THEN 'TRUE' ELSE 'FALSE' END,
    isLast          = CASE WHEN c.RN = c.CC THEN 'TRUE' ELSE 'FALSE' END,
    ChannelsInGroup = c.CC,
    DaysBeforeLast  = DATEDIFF(DAY, c.[Date], x.LastDate)
FROM Cte c
CROSS APPLY(
    SELECT TOP 1 [Date]
    FROM Cte
    WHERE 
        RN = CC
        AND GroupID = c.GroupID
    ORDER BY [Date] DESC
)x(LastDate)

结果

|       Date | GroupID | Channel | isFirst | isLast | ChannelsInGroup | DaysBeforeLast |
|------------|---------|---------|---------|--------|-----------------|----------------|
| 2015-02-24 |       1 |       A |    TRUE |  FALSE |               3 |              3 |
| 2015-02-26 |       1 |       B |   FALSE |  FALSE |               3 |              1 |
| 2015-02-27 |       1 |       C |   FALSE |   TRUE |               3 |              0 |
| 2015-03-21 |       2 |       D |    TRUE |   TRUE |               1 |              0 |
| 2015-02-20 |       3 |       E |    TRUE |  FALSE |               2 |              5 |
| 2015-02-25 |       3 |       D |   FALSE |   TRUE |               2 |              0 |
| 2015-02-28 |       4 |       C |    TRUE |   TRUE |               1 |              0 |
| 2015-03-04 |       5 |       B |    TRUE |  FALSE |               6 |             19 |
| 2015-03-05 |       5 |       E |   FALSE |  FALSE |               6 |             18 |
| 2015-03-10 |       5 |       D |   FALSE |  FALSE |               6 |             13 |
| 2015-03-11 |       5 |       A |   FALSE |  FALSE |               6 |             12 |
| 2015-03-14 |       5 |       C |   FALSE |  FALSE |               6 |              9 |
| 2015-03-23 |       5 |       F |   FALSE |   TRUE |               6 |              0 |
| 2015-03-28 |       6 |       E |    TRUE |   TRUE |               1 |              0 |

一种解决方案可能是使用窗口聚合函数:

select 
    *, 
    case when date = MIN(date) over (partition by groupid order by groupid) then 'TRUE' else 'FALSE' end isFirst,
    case when date = MAX(date) over (partition by groupid order by groupid) then 'TRUE' else 'FALSE' end isLast,
    count(*) over (partition by groupid order by groupid) Channelsingroup,
    datediff(day,date,MAX(date) over (partition by groupid order by groupid)) Daysbeforelast
from your_table

Sample SQL Fiddle

试试这个。

select   DATE_1 ,GroupID,CHANNEL
         , case (select top 1 test.CHANNEL 
                 from test 
                 where test.GroupID = outer1.GroupID
                    order by test.CHANNEL) when outer1.CHANNEL then 'true' else 'false' end as isFirst 
        , case (select top 1 test.CHANNEL 
                 from test 
                 where test.GroupID = outer1.GroupID 
                    order by test.CHANNEL desc) when CHANNEL then 'true' else 'false' end as isLast
        , (select count(*)
           from test 
             where test.GroupID = outer1.GroupID) Channelsingroup 
        , (select DATEDIFF(DAY,outer1.DATE_1,MAX(test.DATE_1))
           from test 
           where test.GroupID = outer1.GroupID)

from     test outer1
 with cte 
 as 
 (select Date,GroupID ,Channel,row_number() over (partition by groupid order         

 by groupid) as first
 from table1)
 select Date,GroupID, Channel, case when first=1 then 'true' else 'false' end as 
 isFirst,
 case when isfirst=b.Channelsingroup then 'true' else 'false' end as isLast
 b.Channelsingroup, datediff(dd,getdate(),Daysbeforelast) as Daysbeforelast
 from cte a
 inner join (select count(*) as Channelsingroup ,max(date) as Daysbeforelast 
 from table1 group by groupid) as  b 
 on a.groupid=b,groupid