MySQL 获取日期列表并加入另一个选项卡

MySQL get list of dates and join with another tab

我正在尝试为过去 7 天内的每个日期生成七行,并加入来自 transactions table 的查询。目的是每个日期都有一个 table,并且 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 <= v.date 替换为 WHERE DATE(timestamp) <= v.date,我会得到与 v.date 相同的错误 - 我似乎无法访问父 table 的值。

我对 MySQL 不太满意,但似乎找不到解决方案,我哪里出错了?

ProGuru 的解决方案

感谢 ,下面的查询按预期工作(在 JOIN 中使用 <= 而不是 = 是关键)

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 子句中使用别名,您必须将查询封装在另一个 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

获取数量累计和,date join需要使用<=

已修订 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

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

(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 根据 table 中的现有日期数据生成日期范围。这是查询:

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