获取上次和下一次约会
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())
另请注意,此查询仅触及 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
我在 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())
另请注意,此查询仅触及 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