两个表上的MySQL联合,一个带有时间戳,另一个带有日期

时间:2016-05-15 09:48:42

标签: mysql group-by union sql-timestamp

我正在尝试整理一个广告的效果报告,显示一天中的观看次数和点击次数。视图和点击存储在具有不同结构的单独表中,因此我认为我必须执行联合。

我已阅读并理解this fantastic piece。它帮助了我,但我认为这比那里解释的例子更复杂。希望得到社区的一些帮助。

这是我的views表,它存储广告在一天内的观看量的计数器。

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

这是我的clicks表,它单独存储每次点击。 (因为它们与问题无关而遗漏了一些列)

+-------------+---------------------+
| 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;

关于代码的一些注释:

  • 点击次数分别存储在时间戳上,因此必须是 转换为日期,然后按日期分组(或至少这是我得到的 不同报告的有效结果)。
  • 报告将以日期范围为特色,适用于特定广告。这解释了where子句。

在撰写这个问题时,我更好地格式化了代码,为了便于阅读,我改变了解决我初始问题的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      |
+------------+-------+--------+

我剩下的问题是重复日期。我该如何解决这个问题? 非常感谢那些善意回答的人!

2 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

我稍微修改了你的查询(参见内联注释)并将其包装在子查询中以在外部查询中使用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

您也可以使用CAST(clicks.timestamp AS DATE)代替DATE(clicks.timestamp),并希望MySQL将来会使用索引。