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

时间:2016-10-18 16:25:29

标签: php mysql

我分别有3个表,用户,网站和site_traffic。 users表包含用户的名称以及有关用户的其他详细信息。每个用户都有一个或多个站点,这些站点存储在站点表中。现在每个站点都有自己的流量数据。

我想要完成的选择是为所有用户选择每个站点没有流量数据的所有日期。这应显示所有用户的名称,每个用户的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

感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

我会使用反连接模式。

首先,在生成的可能日期列表和所有站点之间执行交叉连接操作。这为每个网站提供了每天的行数。然后继续并加入到users表。

诀窍是反加入。我们采用所有网站和所有日期的集合,然后“匹配”到site_traffic中的行。我们只想返回没有匹配的行。我们可以使用外连接执行此操作,然后在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实际上是date (没有时间)那么我们可以做一个更简单的平等比较。)

us表中添加所需表达式的SELECT列表。

有人建议内联视图d(生成“所有日期”列表)看起来有些混乱。但我很好。

如果MySQL提供了一个表值函数,或者用于生成一系列整数值的其他“更漂亮”的机制,那将是很好的。

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