如何在 MySQL 中使用 JOIN SQL select 所有没有数据的日期?

How to select all dates without data using JOIN SQLs in MySQL?

我有 3 个 table,分别是用户、站点和 site_traffic。 users table 包含用户的名称和有关用户的其他详细信息。每个用户都有 1 个或多个存储在站点 table 中的站点。现在每个站点都有自己的流量数据。

我要完成的工作是 select 所有用户的每个站点都没有流量数据的所有日期。这应该显示所有用户的姓名、每个用户的 site_ids 以及每个站点没有数据的日期。

从这个查询开始,我能够获取仅针对 1 个特定用户的没有数据的日期。我如何修改此查询以列出所有用户及其站点以及每个站点没有数据的日期。

这是我的查询:

SELECT b.dates_without_data
FROM (
    SELECT a.dates AS dates_without_data
    FROM (
        SELECT CURDATE() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY as dates
        FROM (SELECT 0 AS a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) as a
        CROSS JOIN (SELECT 0 AS a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) as b
        CROSS JOIN (SELECT 0 AS a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) as c
    ) a
    WHERE a.dates >= DATE_SUB(DATE_SUB(NOW(),INTERVAL 1 DAY), INTERVAL 35 DAY)
) b
WHERE b.dates_without_data NOT IN (
    SELECT recorded_on 
    FROM site_traffic, sites, users
    WHERE site_traffic.site_id = sites.site_id
    AND sites.user_id = users.user_id
    AND users.user_id = 1
)
AND b.dates_without_data < DATE_SUB(NOW(),INTERVAL 1 DAY)
ORDER BY b.dates_without_data ASC

感谢大家的帮助。

我会使用反连接模式。

首先,在生成的可能日期列表和所有站点之间进行交叉连接操作。这为我们提供了每一天的每个站点的行。然后继续加入用户 table.

诀窍是反加入。我们将所有站点和所有日期的集合,然后 "match" 到 site_traffic 中的行。我们只想 return 没有匹配的行。我们可以使用外连接来做到这一点,然后在 WHERE 子句中添加一个条件,如果找到匹配项则排除行。只留下没有匹配的行。

像这样:

 SELECT s.site_id
      , u.user_id
      , d.dt       AS date_without_data
   FROM (

    SELECT DATE(NOW()) - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY AS dt
      FROM (SELECT 0 AS a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) as a
      CROSS JOIN (SELECT 0 AS a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) as b
      CROSS JOIN (SELECT 0 AS a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) as c
    HAVING dt >= DATE(NOW()) + INTERVAL -1-35 DAY
       AND dt <  DATE(NOW()) + INTERVAL -1 DAY

        ) d
  CROSS
   JOIN site s
   JOIN users u
     ON u.user_id = s.user_id
  LEFT
  JOIN site_traffic t
    ON t.site_id      = s.site_id
    ON t.recorded_on >= d.dt
   AND t.recorded_on  < d.dt + INTERVAL 1 DAY
 WHERE t.site_id IS NULL

 ORDER BY s.site_id, u.user_id

诀窍在于 WHERE 子句中的条件。在 site_traffic 中找到匹配行的任何行都将具有 site_id 的非 NULL 值。 (连接条件中与 site_id 的相等比较向我们保证了这一点。)因此,如果我们排除所有具有非 NULL 值的行,我们将剩下没有匹配的行。

(我假设 recorded_on 是一个日期时间,所以我使用了范围比较...来匹配给定日期内 recorded_on 的任何值。如果 recorded_on 实际上是a date(没有时间)那么我们可以做一个更简单的相等比较。)

将您需要的任何表达式添加到 SELECT 列表,来自 us tables.

有人建议内联视图 d(生成 "all dates" 的列表)看起来有点乱。不过我没意见。

如果 MySQL 提供一个 table 赋值函数,或者其他一些 "prettier" 生成一系列整数值的机制,那就太好了。

我会在视图查询本身中包含日期的所有条件,在视图中完成它,而不必处理外部查询。