SQL选择数据并按日期范围分组数据

Posted

技术标签:

【中文标题】SQL选择数据并按日期范围分组数据【英文标题】:SQL select data and grouping data by date range 【发布时间】:2021-06-10 05:30:39 【问题描述】:

我有这样的数据表

price date type
1000 2021-03-13 A
1000 2021-03-14 A
1000 2021-03-15 A
1300 2021-03-16 A
1000 2021-03-17 A
1300 2021-03-18 A
1000 2021-03-19 A
1500 2021-03-20 A
1500 2021-03-21 A
1500 2021-03-22 A
2000 2021-03-13 B
2000 2021-03-14 B
2000 2021-03-15 B
2000 2021-03-16 B
2000 2021-03-17 B

我想查询这样的数据

price startDate endDate type
1000 2021-03-13 2021-03-15 A
1300 2021-03-16 2021-03-16 A
1000 2021-03-17 2021-03-17 A
1300 2021-03-18 2021-03-18 A
1000 2021-03-19 2021-03-19 A
1500 2021-03-20 2021-03-22 A
2000 2021-03-13 2021-03-17 B

下面是我当前的sql,但它根本不正确

SELECT MIN(date) as startDate, MAX(date) as endDate, price, type                                      
                            FROM prices 
                            GROUP BY type, price
                            ORDER BY type, MIN(date)

【问题讨论】:

你的mysql版本是多少? 您如何确保数据按此顺序显示? 什么是空白没有被另一条记录填补,即当记录 (1300,2021-03-16,A) 不存在时? 【参考方案1】:

这是一个间隙和孤岛问题,处理它的一种简单方法是使用行数差异法。假设您使用的是 MySQL 8+,以下应该可以工作:

WITH cte AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY type ORDER BY date) rn1,
              ROW_NUMBER() OVER (PARTITION BY type, price ORDER BY date) rn2
    FROM prices
)

SELECT price, MIN(date) AS startDate, MAX(date) AS endDate, type
FROM cte
GROUP BY price, type, rn1 - rn2
ORDER BY type, startDate;

Demo

【讨论】:

它不会检测到没有用其他价格填补的空白,即如果您删除价格为 1300 的记录。这是故意的吗? @Turo 您会看到一些边缘情况,这些情况在 OP 中对我来说并非全部可见。在进行编辑之前,我将等待 OP 的反馈。 我希望你能发布一个解决方案 ;-) 我还没有找到它...... @Turo 。 . .日期在问题中没有空白。 OP 没有解释在这种情况下该怎么做。在我看来,这回答了这个问题,并且是解决这个问题的最佳方法之一。 (我能想到一种简化,但不值得回答。)【参考方案2】:
SELECT MIN(date) as startDate, MAX(date) as endDate, price, type    
FROM prices
GROUP BY type, price
ORDER BY MIN(date) ASC

此查询可能适用于按日期升序排列数据

【讨论】:

太简单了,不会产生预期的结果【参考方案3】:

如果不使用 CTE,您可以使用以下查询:

SELECT w1.price, w1.date, w2.date, w1.type FROM
(
  SELECT * FROM mytable t1
  WHERE NOT EXISTS (
    SELECT 1 FROM mytable t2
    WHERE 
      t1.price = t2.price AND
      t1.type = t2.type AND
      DATEDIFF(t2.date, t1.date) = -1
  )
) w1
INNER JOIN
(
  SELECT * FROM mytable t1
  WHERE NOT EXISTS (
    SELECT 1 FROM mytable t2
    WHERE 
      t1.price = t2.price AND
      t1.type = t2.type AND
      DATEDIFF(t2.date, t1.date) = +1
  )
) w2
ON
w1.price = w2.price AND
w1.type = w2.type AND
w1.date <= w2.date AND
NOT EXISTS (
  SELECT * FROM mytable t1
  WHERE NOT EXISTS (
    SELECT 1 FROM mytable t2
    WHERE 
      t1.price = t2.price AND
      t1.type = t2.type AND
      DATEDIFF(t2.date, t1.date) = +1
  )
  AND
  w1.price = t1.price AND
  w1.type = t1.type AND
  w1.date <= t1.date AND t1.date < w2.date
)
    获取每个时期的更小和更大的日期。 加入这些表。 获取较小日期和较大日期之间的行。

DB Fiddle

【讨论】:

@banku。 . .这绝对不是解决问题的最简单方法。

以上是关于SQL选择数据并按日期范围分组数据的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SQL 中按多列分组并按日期排序?

核心数据按年份分组并按日期排序

SQL:选择具有最大值的行并按单列分组

SQL - 使用分组方式选择,从具有最大值(日期)的寄存器中获取数据

SQL - 显示给定范围内的所有日期,并使用数据库中的时间戳计算该日期有多少帖子

如何从日期范围中获取所有天数并按这些天数分组?