【问题标题】:MySQL get list of dates and join with another tabMySQL 获取日期列表并加入另一个选项卡
【发布时间】:2021-12-07 23:33:49
【问题描述】:

我正在尝试为过去 7 天内的每个日期生成 7 行,并加入来自transactions 表的查询。目的是有一个包含每个日期的表格,以及从第一个条目到该日期的 transactions 中的 quantity 列的累积总数:

|      date     |  stockOnDate  |
|---------------|---------------|
| 2021-10-15    | 10            |
| 2021-10-16    | 3             |
| 2021-10-17    | 0             |
| 2021-10-18    | 9             |
| 2021-10-19    | 15            |
| 2021-10-20    | 15            |
| 2021-10-21    | 15            |

我可以获取日期列表,并且可以加入,但无法过滤嵌套查询:

SELECT v.*, t.* 
FROM ( SELECT DATE(ADDDATE(DATE_SUB(NOW(),INTERVAL 7 DAY), t3*1000 + t2*100 + t1*10 + t0)) AS `date` 
       FROM (SELECT 0 t0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t0,
            (SELECT 0 t1 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1,
            (SELECT 0 t2 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2,
            (SELECT 0 t3 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3 ) AS v
LEFT JOIN (SELECT SUM(quantity) AS stockOnDate, DATE(timestamp) as tDate 
           FROM `transactions` 
           WHERE tDate <= v.`date`) AS t ON t.tDate = v.`date`
WHERE v.`date` >= DATE_SUB(NOW(),INTERVAL 7 DAY) AND v.`date` <= DATE(NOW())

但我收到以下错误:

#1054 - Unknown column 'tDate' in 'where clause'

如果我将 WHERE tDate &lt;= v.date 替换为 WHERE DATE(timestamp) &lt;= v.date,我会收到相同的 v.date 错误 - 我似乎无法访问父表的值。

我对 MySQL 不是很好,但似乎找不到解决方案,我哪里出错了?

ProGuru 的解决方案

感谢ProGuru's answer below,以下查询按预期工作(在JOIN 中使用&lt;= 而不是= 是关键)

SELECT b.date, SUM(a.quantity) AS stockOnDate
FROM (
 SELECT DATE(ADDDATE(DATE_SUB(NOW(),INTERVAL 6 DAY), t3*1000 + t2*100 + t1*10 + t0)) AS `date` 
       FROM (SELECT 0 t0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t0,
            (SELECT 0 t1 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1,
            (SELECT 0 t2 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2,
            (SELECT 0 t3 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3
) b 
LEFT JOIN transactions a ON DATE(a.timestamp) <= b.date
WHERE b.date BETWEEN DATE(NOW()) - INTERVAL 6 DAY AND DATE(NOW())
AND a.organisationId = 1
GROUP BY b.date
ORDER BY b.date ASC

我还可以将 GROUP BY 更改为 GROUP BY a.itemID, b.date 以获取每个 itemId 在给定日期的库存水平。

【问题讨论】:

  • .. WHERE tDate &lt;= v.`date` .. 您不能在 WHERE 中使用输出列别名 (tDate)。要么改用它的表达式 (DATE(timestamp)),要么在 HAVING 中应用这个条件。此外,在指定 LATERAL 之前,您不能从另一个子查询 (v.date) 引用子查询列。
  • 你的 MySQL 精确版本是多少?此外,我建议您使用示例数据创建在线小提琴,并提供所需的输出和详细的解释。当然,对于更短的数据范围...
  • 不幸的是,与HAVIING 的结果相同,正如我所说,使用DATE(timestamp 会导致与v.date 相同的问题。使用 10.4.19-MariaDB,想要的结果在帖子的开头
  • 不幸的是 HAVIING 的结果相同 我预测这一点 - “另外,在指定 LATERAL 之前,您不能从另一个子查询 (v.date) 引用子查询列。” 使用 10.4.19-MariaDB 对于这个版本,您的查询应该完全重写 - 使用序列或递归 CTE 生成日期列表,使用窗口函数进行累积和计算。
  • 期望的结果在文章的开头你认为没有源数据的期望输出可能有意义吗?

标签: mysql sql join subquery nested-queries


【解决方案1】:

要获取数量的累计总和,日期join需要使用&lt;=

修改后的 SQL

SELECT b.date, SUM(COALESCE(a.quantity, 0))
FROM (
 SELECT DATE(ADDDATE(DATE_SUB(NOW(),INTERVAL 7 DAY), t3*1000 + t2*100 + t1*10 + t0)) AS `date` 
       FROM (SELECT 0 t0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t0,
            (SELECT 0 t1 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1,
            (SELECT 0 t2 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2,
            (SELECT 0 t3 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3
) b 
LEFT JOIN transactions a ON DATE(a.timestamp) <= b.date
WHERE b.date BETWEEN DATE(NOW()) - INTERVAL 6 DAY AND DATE(NOW())
GROUP BY b.date
ORDER BY b.date ASC

https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=34f78e3d7c9225727ac2e728588759e2

【讨论】:

  • 这已经成功了 - 从来不知道您可以使用
【解决方案2】:

要在 where 子句中使用别名,您必须将查询封装在另一个 select 中。

例子:

SELECT * FROM
    (SELECT SUM(quantity) AS stockOnDate, DATE(timestamp) as tDate FROM `transactions`) seb
WHERE seb.tDate <= v.`date`

更新试试这个:

SELECT v.*, t.* 
FROM ( SELECT DATE(ADDDATE(DATE_SUB(NOW(),INTERVAL 7 DAY), t3*1000 + t2*100 + t1*10 + t0)) AS `date` 
       FROM (SELECT 0 t0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t0,
            (SELECT 0 t1 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1,
            (SELECT 0 t2 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2,
            (SELECT 0 t3 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3 ) AS v
LEFT JOIN (SELECT SUM(quantity) AS stockOnDate, DATE(timestamp) as tDate FROM transactions) AS t ON t.tDate = v.`date`
WHERE t.tDate <= v.`date` and v.`date` >= DATE_SUB(NOW(),INTERVAL 7 DAY) AND v.`date` <= DATE(NOW())

这里可以使用tDate

【讨论】:

  • 确实如此。已更新。
  • 假设我已经按照您的意思做对了,使用 LEFT JOIN (SELECT * FROM (SELECT SUM(quantity) AS stockOnDate, DATE(timestamp) as tDate FROM transactions) seb WHERE seb.tDate &lt;= v.date) t ON t.tDate = v.date 时,我仍然遇到同样的错误#1054 - Unknown column 'v.date' in 'where clause'
  • v.date 不能在这里引用,它必须放在查询之外(WHERE seb.tDate date and v.date >= DATE_SUB(NOW(),INTERVAL 7 天)和 v.date
  • 检查我的答案(更新)
  • 谢谢,但返回 0 行(虽然没有错误!) - 我需要一个累积总和以便理解它t.date &lt;= v.date 需要在子查询中以确保它只求和到当前行的v.date
【解决方案3】:

首先你需要了解在这个子查询中

(SELECT SUM(quantity) AS stockOnDate, DATE(timestamp) as tDate 
           FROM `transactions` 
           WHERE tDate <= v.`date`) AS t

没有定义为date 甚至别名v。您基本上是在告诉查询在子查询中不存在 v.date 时查找它。

我想这就是你要找的东西:

SELECT dt, IFNULL(SUM(quantity),0) AS stockOnDate
    FROM (SELECT STR_TO_DATE(CONCAT(LEFT(NOW(),7),LPAD(seq,2,0)), '%Y-%m%d') dt
             FROM seq_1_to_31) v
        LEFT JOIN  `transactions` t ON dt=DATE(timestamp)
    WHERE dt >= (SELECT MIN(DATE(timestamp)) FROM `transactions`)
       AND dt <= (SELECT MAX(DATE(timestamp)) FROM `transactions`)
GROUP BY dt;

这里我使用 MariaDB 的内置 sequence engine 来生成天数;与当前的年月组合连接,然后使用STR_TO_DATE 使其可识别为正确的日期格式。生成这些的查询位于子查询v

第二个选项是使用recursive common table expression 根据表中现有的日期数据生成日期范围。这是查询:

WITH RECURSIVE cte AS (
SELECT MIN(DATE(timestamp)) mndt, MAX(DATE(timestamp)) mxdt FROM `transactions`
UNION ALL
SELECT mndt+INTERVAL 1 DAY, mxdt FROM cte WHERE mndt+INTERVAL 1 DAY <= mxdt)
SELECT mndt, IFNULL(SUM(quantity),0) AS stockOnDate
    FROM cte
        LEFT JOIN  `transactions` t ON mndt=DATE(timestamp)
GROUP BY mndt;

上述两个查询都无需包含生成日期范围的长子查询。这是较新的 MySQL 和 MariaDB 版本的优势。

Demo fiddle

【讨论】:

    猜你喜欢
    • 2015-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-27
    • 1970-01-01
    • 2013-11-01
    相关资源
    最近更新 更多