获取上次和下一次约会

Get Last and Next Appointments

我在 SQL 服务器中有以下 table,我想获取每个客户的上次和下一次约会。

注意:如果第一次约会是在未来,最后一次约会应该是N/A。同样,如果上次约会是过去,则下一次约会将为 N/A。如果最后一次约会早于 30 天,则不应显示(如果没有未来的约会 - 被视为不活跃的客户)。

CustomerId (int) | Date (date) | Time (time)
1                | 20210801    | 11:00
1                | 20210802    | 13:00
1                | 20210805    | 10:00
1                | 20210811    | 16:00
1                | 20210821    | 17:00
2                | 20210801    | 11:00
2                | 20210802    | 11:00
2                | 20210803    | 11:00
2                | 20210804    | 11:00
3                | 20210831    | 11:00
4                | 20210526    | 10:00

在这种情况下,结果应该是(假设日期是今天 2021 年 8 月 7 日):

CustomerId (int) | LastAppointment (varchar) | NextAppointment (varchar)
1                | 05 Aug 2021 - 10:00       | 11 Aug 2021 - 16:00
2                | 04 Aug 2021 - 11:00       | N/A
3                | N/A                       | 31 Aug 2021 - 11:00

有人能帮帮我吗?一个例子将不胜感激。

注意 :此解决方案有效但在性能方面非常糟糕,请检查 以获得更好的方法


像这样

SELECT DISTINCT customerid,
                Isnull(CONVERT(VARCHAR,
                                 (SELECT TOP 1 Concat(date, ' ', TIME)
                                  FROM appointments B
                                  WHERE b.customerid = a.customerid
                                    AND ([date] < CONVERT(DATE, Getdate())
                                         OR ([date] = CONVERT(DATE, Getdate())
                                             AND [time] <= CONVERT(TIME, Getdate())))
                                    ORDER  BY [date] DESC)), 'N/A') AS lastappointment,
                Isnull(CONVERT(VARCHAR,
                                 (SELECT TOP 1 Concat(date, ' ', TIME)
                                  FROM appointments B
                                  WHERE b.customerid = a.customerid
                                    AND ([date] > CONVERT(DATE, Getdate())
                                         OR ([date] = CONVERT(DATE, Getdate())
                                             AND [time] > CONVERT (TIME, Getdate())))
                                    ORDER  BY [date])), 'N/A') AS nextappointment
FROM appointments A
WHERE Datediff(DAY,
                 (SELECT TOP 1 date
                  FROM appointments B
                  WHERE b.customerid = a.customerid
                    AND [date] <= CONVERT(DATE, Getdate())
                    ORDER  BY [date] DESC), CONVERT(DATE, Getdate())) <= 30
  OR (((
          (SELECT TOP 1 date
           FROM appointments B
           WHERE b.customerid = a.customerid
             AND [date] > CONVERT(DATE, Getdate())
             ORDER  BY [date]) > CONVERT(DATE, Getdate())))
      OR ((
             (SELECT TOP 1 date
              FROM appointments B
              WHERE b.customerid = a.customerid
                AND [date] > CONVERT(DATE, Getdate())
                ORDER  BY [date]) = CONVERT(DATE, Getdate()))
          AND (
                 (SELECT TOP 1 [time]
                  FROM appointments B
                  WHERE b.customerid = a.customerid
                    AND [date] > CONVERT(DATE, Getdate())
                    ORDER  BY [date]) > CONVERT(TIME, Getdate()))))

我给你的 table appointments 打了电话,条件是 select 过去 30 天内最后一次预约的客户或未来预约的客户。 我测试了列类型 Date 的日期和 Time(7) 的时间。

您需要条件聚合:

SELECT CustomerId,
       COALESCE(
         MAX(CASE 
               WHEN CAST(Date AS DATETIME) + CAST(Time AS DATETIME) < GETDATE() 
                 THEN FORMAT(CAST(Date AS DATETIME) + CAST(Time AS DATETIME), 'dd MMM yyyy - HH:mm')
             END
         ), 'N/A'    
       ) LastAppointment,
       COALESCE(
         MIN(CASE 
               WHEN CAST(Date AS DATETIME) + CAST(Time AS DATETIME) > GETDATE() 
                 THEN FORMAT(CAST(Date AS DATETIME) + CAST(Time AS DATETIME), 'dd MMM yyyy - HH:mm')
             END
         ), 'N/A'    
       ) NextAppointment
FROM tablename
GROUP BY CustomerId
HAVING COALESCE(DATEDIFF(
         d, 
         MAX(CASE 
               WHEN CAST(Date AS DATETIME) + CAST(Time AS DATETIME) < GETDATE() 
                 THEN CAST(Date AS DATETIME) + CAST(Time AS DATETIME)
             END
         ),
         GETDATE()
       ), 0) < 30

参见demo
结果:

CustomerId LastAppointment NextAppointment
1 05 Aug 2021 - 10:00 11 Aug 2021 - 16:00
2 04 Aug 2021 - 11:00 N/A
3 N/A 31 Aug 2021 - 11:00

您只需使用 datetime 值,然后使用条件聚合来 select 每个客户所需的日期。首先使用 CTE 尽可能简化日期转换,如下所示:

with ap as (
    select CustomerId, Convert(datetime,Left(Concat([date], ' ', [time]),15)) app
    from t
), groups as (
    select CustomerId, 
    Max(case when app <= GetDate() then app end) LastAppointment,
    Min(case when app > GetDate() then app end) NextAppointment
    from ap
    group by customerId
)
select CustomerID, 
    IsNull(Format(LastAppointment, 'dd MMM yyyy - hh:mm'), 'N/A') LastAppointment, 
    IsNull(Format(NextAppointment, 'dd MMM yyyy - hh:mm'), 'N/A') NextAppointment
from groups
where DateAdd(day,-30,GetDate()) < isnull(lastappointment,GetDate())

DB<>Fiddle

另请注意,此查询仅触及 table 一次并执行一次逻辑读取。

Base table 出于优化目的,仅使用一次。使用 LAG() 函数和其他必要条件来选择实际数据集。

-- SQL SERVER

SELECT p.CustomerId
     , CASE WHEN p.chk_condition = 1
               THEN CONVERT(varchar(13), p.prev_Date, 113) + ' - ' + LEFT(p.prev_time, 5)
            WHEN p.chk_condition = 2
               THEN CONVERT(varchar(13), p.Date, 113) + ' - ' + LEFT(p.time, 5)
            ELSE 'N/A'
       END "LastAppointment"
     , CASE WHEN p.chk_condition != 2
               THEN CONVERT(varchar(13), p.Date, 113) + ' - ' + LEFT(p.time, 5)
            ELSE 'N/A'
       END "NextAppointment"
FROM ( SELECT t.*
            , CASE WHEN  t.prev_Date < GETDATE() AND t.Date >= GETDATE()
                      THEN 1
                   WHEN t.prev_Date < GETDATE() AND t.Date <= GETDATE()
                      THEN 2
                   ELSE 0
              END chk_condition
            , ROW_NUMBER() OVER (PARTITION BY CustomerId ORDER BY t.Date DESC, t.prev_Date DESC) row_num
       FROM (SELECT CustomerId, Date, Time
                  , LAG(Date) OVER (PARTITION BY CustomerId ORDER BY "Date", "Time") "prev_Date"
                  , LAG(Time) OVER (PARTITION BY CustomerId ORDER BY "Date", "Time") "prev_Time"
             FROM appointment) t
       WHERE  CASE WHEN t.prev_Date < GETDATE() AND t.Date >= GETDATE() 
                      THEN 1
                   WHEN t.prev_Date IS NULL 
                      THEN CASE WHEN DATEDIFF(day, t.Date, GETDATE()) >= 30
                                   THEN 0
                                ELSE 1
                           END
                   WHEN t.prev_Date < GETDATE() AND t.Date <= GETDATE()
                      THEN 1
               END = 1 ) p
WHERE p.row_num = 1
ORDER BY p.CustomerId;

请检查这个urlhttps://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=3813d09cf25ed14d249970654995b085