MySQL 两张表的联合,一张带有时间戳,一张带有日期

MySQL union on two tables, one with timestamp and one with date

我正在尝试汇总一个广告的效果报告,显示它在一天内的浏览量和点击量。观看次数和点击次数存储在具有不同结构的单独 table 中,因此我认为我必须执行合并。

我已阅读并理解this fantastic piece。它对我有帮助,但我认为这比那里解释的例子复杂一个层次。希望社区提供一些帮助。

这是我的 views table,它存储了一个广告一天的浏览量计数器。

+-------------+--------------+
| COLUMN_NAME | COLUMN_TYPE  |
+-------------+--------------+
| ad_day_id   | bigint(13)   |
| advert_id   | bigint(20)   |
| date        | date         |
| views       | mediumint(6) |
+-------------+--------------+

这是我的 clicks table,它单独存储每次点击。 (有些列因与问题无关而被省略)

+-------------+---------------------+
| COLUMN_NAME | COLUMN_TYPE         |
+-------------+---------------------+
| id          | bigint(20) unsigned |
| advert_id   | bigint(20)          |
| timestamp   | timestamp           |
+-------------+---------------------+

结果应该是这样的(没有使用实数,只是为了显示格式):

+------------+-------+--------+
| event_date | views | clicks |
+------------+-------+--------+
| 2016-05-09 |    25 |      4 |
| 2016-05-10 |     2 |        |
| 2016-05-11 |   105 |     10 |
| 2016-05-13 |    96 |      7 |
| 2016-05-14 |       |      1 |
+------------+-------+--------+

关于结果:

关闭代码...这是我目前拥有的:

SELECT
    $views_table.date AS event_date,
    $views_table.views,
    '' AS clicks
FROM
    $views_table
WHERE
    ( $views_table.date BETWEEN '$from_date' AND '$to_date' )
    AND $views_table.advert_id=$advert_id
UNION
SELECT
    CAST($clicks_table.timestamp AS DATE) AS event_date,
    '' AS views,
    COUNT($clicks_table.advert_id) AS clicks
FROM
    $clicks_table
WHERE
    ( CAST($clicks_table.timestamp AS DATE) BETWEEN '$from_date' AND '$to_date' )
    AND $clicks_table.advert_id=$advert_id
GROUP BY
    event_date
ORDER BY
    event_date ASC;

代码的一些注释:

在撰写这个问题时,我对代码进行了更好的格式化,为了便于阅读,我更改了 select 语句的顺序,这解决了我最初的问题。显然,两个 select 必须具有相同的列并且顺序相同。

我想我快到了,因为这是我目前的结果:

+------------+-------+--------+
| event_date | views | clicks |
+------------+-------+--------+
| 2016-05-09 | 1     |        |
| 2016-05-09 |       | 1      |
| 2016-05-10 | 2     |        |
| 2016-05-11 | 105   |        |
| 2016-05-11 |       | 7      |
| 2016-05-13 | 96    |        |
| 2016-05-13 |       | 16     |
| 2016-05-14 | 2     |        |
| 2016-05-14 |       | 1      |
| 2016-05-15 | 2     |        |
| 2016-05-15 |       | 2      |
+------------+-------+--------+

我剩下的问题是重复的日期。我该如何解决这个问题?
非常感谢好心解答的朋友们!

您需要的不是简单的并集,而是并集和联接。因此,您需要一个联合来从视图和点击中获取日期的组合列表 table。然后您需要左加入视图并单击日期列表中的 tables:

select ds.event_date, max(v.views) views, count(c.clicks) clicks
from
    (select distinct date as event_date from views
     union distinct
     select distinct date(timestamp) from clicks) ds
left join views v on ds.event_date=v.date
left join clicks c on ds.event_date=date(c.timestamp)
where ...
group by ds.event_date

我稍微修改了您的查询(请参阅内联注释)并将其包装在子查询中以在外部查询中使用 GROUP BY event_date

SELECT event_date, MAX(views) AS views, MAX(clicks) AS clicks
FROM (
    SELECT
        views.date AS event_date,
        views.views,
        0 AS clicks -- '' causes strange results on sqlfiddle
    FROM
        views
    WHERE
        ( views.date BETWEEN '2016-05-09' AND '2016-05-15' )
        AND views.advert_id=1
    UNION
    SELECT
        CAST(clicks.timestamp AS DATE) AS event_date,
        0 AS views, -- '' causes strange results on sqlfiddle
        COUNT(clicks.advert_id) AS clicks
    FROM
        clicks
    WHERE
        ( CAST(clicks.timestamp AS DATE) BETWEEN '2016-05-09' AND '2016-05-15' )
        AND clicks.advert_id=1
    GROUP BY
        event_date
    -- ORDER BY is useless here
) sub
GROUP BY event_date
ORDER BY event_date

Demo

你也可以用DATE(clicks.timestamp)代替CAST(clicks.timestamp AS DATE),希望MySQL以后使用索引