【问题标题】:First value in DATE minus 30 days SQLDATE 中的第一个值减去 30 天 SQL
【发布时间】:2018-12-19 13:47:06
【问题描述】:

我有一堆数据,其中显示了 ID、最大日期及其对应的值(用户 ID、类型、...)。然后我需要为每个 ID 取 MAX 日期,减去 30 天并显示第一个日期及其在此日期期间的对应值。

例子:

ID 日期 姓名
1 01.05.2018 AAA
1 21.04.2018 CCC
1 05.04.2018 BBB
1 28.03.2018 AAA

预期:

ID max_date max_name previous_date previous_name
1 01.05.2018 AAA 05.04.2018 BBB

我有使用子选择的有效解决方案,但由于我有相当大的 WHERE 部分,刷新需要很长时间。

SUBSELECT 看起来像这样: (SELECT MIN(N.name) 从 t1 N 其中 N.ID = T.ID AND (N.date = (MAX(T.date)-30)) AND (...)) 作为上一个名称

你是怎么写选择的?

我正在使用 TSQL

谢谢

【问题讨论】:

  • 把你的代码贴在这里,我们看看能不能变得更好
  • 样本数据,预期输出也始终是这里的标准要求 :) 否则伪代码可能只会绘制 更多 个问题
  • 一对 APPLY 操作可能会有所帮助,但如果不看看你现在在做什么就很难知道。
  • ID 日期名称 1 01.05.2018 AAA 1 21.04.2018 CCC 1 05.04.2018 BBB 1 28.03.2018 AAA 预期:ID max_date max_name previous_date previous_name 1 01.05.2018 AAA 05.04.2018 BBB
  • 之前(减去 30 天)我使用的东西如下: (SELECT MIN(N.name) FROM t1 N WHERE N.ID = T.ID AND (N.date = (MAX(T.date)-30)) AND (...)) AS PreviousName

标签: sql sql-server date max min


【解决方案1】:

我可以用 2 个 CTE 来建立日期和名称。

SQL Fiddle

MS SQL Server 2017 架构设置

CREATE TABLE t1 (ID int, theDate date, theName varchar(10)) ;

INSERT INTO t1 (ID, theDate, theName)
VALUES
    ( 1,'2018-05-01','AAA' )
  , ( 1,'2018-04-21','CCC' )
  , ( 1,'2018-04-05','BBB' )
  , ( 1,'2018-03-27','AAA' )
  , ( 2,'2018-05-02','AAA' )
  , ( 2,'2018-05-21','CCC' )
  , ( 2,'2018-03-03','BBB' )
  , ( 2,'2018-01-20','AAA' )
;

主查询

;WITH cte1 AS (
  SELECT t1.ID, t1.theDate, t1.theName
    , DATEADD(day,-30,t1.theDate) AS dMinus30
    , ROW_NUMBER() OVER (PARTITION BY t1.ID ORDER BY t1.theDate DESC) AS rn
  FROM t1
)
, cte2 AS (
  SELECT c2.ID, c2.theDate, c2.theName
    , ROW_NUMBER() OVER (PARTITION BY c2.ID ORDER BY c2.theDate) AS rn
    , COUNT(*) OVER (PARTITION BY c2.ID) AS theCount
  FROM cte1
  INNER JOIN cte1 c2 ON cte1.ID = c2.ID
    AND c2.theDate >= cte1.dMinus30
  WHERE cte1.rn = 1
  GROUP BY c2.ID, c2.theDate, c2.theName
)
SELECT cte1.ID, cte1.theDate AS max_date, cte1.theName AS max_name
  , cte2.theDate AS previous_date, cte2.theName AS previous_name 
  , cte2.theCount
FROM cte1 
INNER JOIN cte2 ON cte1.ID = cte2.ID 
  AND cte2.rn=1
WHERE cte1.rn = 1

Results

| ID |   max_date | max_name | previous_date | previous_name |
|----|------------|----------|---------------|---------------|
|  1 | 2018-05-01 |      AAA |    2018-04-05 |           BBB |
|  2 | 2018-05-21 |      CCC |    2018-05-02 |           AAA |

cte1 构建由ID 分组的max_datemax_name 列表,然后使用ROW_NUMBER() 窗口函数按日期对组进行排序以获得最近的日期。 cte2 重新加入此列表以获取在 cte1 的最大日期的最后 30 天内的所有日期。然后它基本上做同样的事情来获取最后一个日期。然后外部查询将这两个结果连接在一起以获得所需的列,同时仅分别从中选择最近和最近的行。

我不确定它与您的数据的扩展效果如何,但使用 CTE 应该可以很好地优化。

编辑:对于附加要求,我刚刚在cte2 中添加了另一个COUNT() 窗口函数。

【讨论】:

  • 也许还有一个问题。我还可以添加新列以查看这 30 天内我们有多少个 ID。谢谢
【解决方案2】:

我愿意:

select id,
       max(case when seqnum = 1 then date end) as max_date,
       max(case when seqnum = 1 then name end) as max_name,
       max(case when seqnum = 2 then date end) as prev_date,
       max(case when seqnum = 2 then name end) as prev_name,
from (select e.*, row_number() over (partition by id order by date desc) as seqnum
      from example e
     ) e
group by id;

【讨论】:

  • 这比我的执行计划更清晰,但它不包括“最大 30 天内的最小日期”要求。可以将其修改为仅选择 ID 的最近 30 天吗?也许修改子选择和WHERE 子句。我喜欢这种方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多