【问题标题】:Group by day from timestamp with multiple tables从带有多个表的时间戳按天分组
【发布时间】:2014-08-05 10:51:28
【问题描述】:

我有两个带有timestamp 列的表。

我想按天对结果进行分组。例如:从2014/06/102014/06/13。这些日期之间是否有记录无关紧要,我希望它按天分组。

我的桌子:

profits:

profits_referrals:

我想要的结果:

╔═════════════╦══════════════╦══════╗
║    date     ║  .........   ║  ..  ║
╠═════════════╬══════════════╬══════╣
║  2014/06/10 ║  .........   ║  ..  ║
║  2014/06/11 ║  .........   ║  ..  ║
║  2014/06/12 ║  .........   ║  ..  ║
║  2014/06/13 ║  .........   ║  ..  ║
╚═════════════╩══════════════╩══════╝

注意:如果某个日期没有记录,我仍然希望它显示该日期,amount 将是0

到目前为止我做了什么:

SELECT      SUM(`profits`.`amount`) AS `profAmount`,
            COUNT(`profits`.`amount`) AS `profCount`,

            SUM(`profits_referrals`.`amount`) AS `refAmount`,
            COUNT(`profits_referrals`.`amount`) AS `refCount`,

            DATE(FROM_UNIXTIME(`profits`.`date`)) AS `profDate`,
            DATE(FROM_UNIXTIME(`profits_referrals`.`date`)) AS `refDate`
FROM        `profits`
JOIN        `profits_referrals`
ON          `profits`.`userid` = `profits_referrals`.`referral`
WHERE       `profits`.`userid` = " . (int)$user->id . "
GROUP BY    DATE(FROM_UNIXTIME(`profits`.`date`)), DATE(FROM_UNIXTIME(`profits_referrals`.`date`))
ORDER BY    `profDate`
DESC

结果:(我做了一些 PHP 代码来显示它)

Date              Sales              Referrals      Total
2014-04-28    2 / $7.35 USD     2 / $1.4 USD    $8.75 USD
2014-04-28    2 / $7.35 USD     2 / $1.4 USD    $8.75 USD
2014-03-27    1 / $2.10 USD     1 / $0.7 USD    $2.80 USD
2014-03-27    1 / $2.10 USD     1 / $0.7 USD    $2.80 USD
2014-03-25    3 / $6.30 USD     3 / $2.0 USD    $8.40 USD
2014-03-25    3 / $6.30 USD     3 / $2.0 USD    $8.40 USD

【问题讨论】:

  • 到目前为止你得到了什么?请注意,如果您有一个预先生成的日历表(这是最有用的“维度”/分析实用程序表之一),您将度过最轻松的时光。
  • @Clockwork-Muse 我不太明白你的解决方案,你能解释一下吗?
  • 无论我如何看待解决方案,您已经尝试过什么?特别是,您要在行上运行哪些计算?为什么需要两张表?
  • @Clockwork-Muse 我想显示每天的报告:用户赚了多少钱(来自profits,来自profits_referrals 和总计)。我更新了我的问题,请看代码。
  • 啊,现在我们可以看看你的问题是什么......给我一分钟。

标签: mysql sql date group-by


【解决方案1】:

首先,您通过id 连接这两个表,但不是通过date,它生成的临时结果集如下所示:

t1.date        t1.amt   t2.date        t2.amt
'2014-04-28'   1        '2014-03-27'   5
'2014-04-28'   1        '2014-03-25'   6

这是因为连接实际上是说“嘿,对于匹配此条件的 每一 行,将左侧和右侧放在一起”。右侧的行与左侧的 one 行匹配的次数越多,左侧重复的次数就越多。很明显,准确的结果将被抛到窗外。为了拥有(至多)一对一的关系,我们需要在连接之前进行聚合,通常使用子查询;

SELECT ....
FROM {base_table} b
JOIN (SELECT {joinColumn}, {AGGREGATE_FUNCTION}
      FROM {other_table}
      GROUP BY {joinColumn}) o
  ON o.{joinColumn} = b.{joinColumn}

不幸的是,您的数据集没有一个规范的“基表” - 不能保证任一表中的行,所以像 FULL OUTER JOIN(或 MySQL 等价物)这样的东西不是去上班(即,如果两个表都没有日期,您将错过日期)。我们需要创建自己的基表。

需要创建所谓的Calendar Table(这个特殊的用于 SQL Server,但可以适应)。这些是您可以制作或使用的最有用的维度/分析表之一。实际内容由您决定,但对于这种类型的查询,它履行了{base_table} 的角色。它还将帮助我们(可能)获得分组的索引访问。

一、修改后的子查询:

SELECT Calendar.calendar_date, 
       COUNT(Profits) AS profCount, COALESCE(SUM(Profits.amt), 0) AS profAmount
FROM Calendar
LEFT JOIN Profits
       ON Profits.userId = {desiredUserId}
          AND Profits.date >= UNIX_TIMESTAMP(Calendar.calendar_date)
          AND Profits.date < UNIX_TIMESTAMP(Calendar.calendar_date + INTERVAL 1 DAY)
WHERE Calendar.calendar_date >= {rangeStart}
      AND Calendar.calendar_date < {rangeEnd}

所以。
这里有几点需要注意:

  • 我已经对参数值进行了描述。实际上,您应该使用parameterized queries,否则您将面临 SQL 注入的风险。由于转换为 int,您当前的查询是安全的,但最好不必担心。
  • 始终使用inclusive lower-bound, &gt;=, and an exclusive upper-bound, &lt; 查询正的连续范围类型(​​除整数之外的所有类型)(这篇文章是为 SQL Server 和其中的时间戳编写的,但问题无处不在。请记住 MySQL DATETIME/@987654335 @ 类型具有用户可指定的小数秒数!)。对于负范围,反转条件。
  • 使用Calendar.calendar_date 上的函数(假设这只是一个标准的DATE 类型)将阻止在连接上使用索引...从Calendar 端。从Profits 方面来看,它会有很好的单值可供搜索。据推测,Profits 中的每个日历日都有多行,这意味着这是连接的缓慢方面。

无论如何,这将输入一个临时结果集,如下所示:

cal_date       Count   Amount
'2014-06-10'   1       5
'2014-06-11'   0       0
'2014-06-12'   1       -9.5
'2014-06-13'   99      99999999.1

成功;每天单行,预先汇总的金额。我们现在可以将它与另一个表 (Profits_Referrals) 的查询结合起来,得到我们的结果:

SELECT Profits.Calendar_date,
       Profits.profAmount, Profits.profCount,
       Referrals.refAmount, Referrals.refCount
FROM (SELECT Calendar.calendar_date,
             COUNT(Profits) AS profCount, COALESCE(SUM(Profits.amt), 0) AS profAmount
             FROM Calendar
             LEFT JOIN Profits
                    ON Profits.userId = ?
                       AND Profits.date >= UNIX_TIMESTAMP(Calendar.calendar_date)
                       AND Profits.date < UNIX_TIMESTAMP(Calendar.calendar_date + INTERVAL 1 DAY)
             WHERE Calendar.calendar_date >= ?
                   AND Calendar.calendar_date < ?) Profits
JOIN (SELECT Calendar.calendar_date,
             COUNT(Refferals) AS refCount, COALESCE(SUM(Refferals.amt), 0) AS refAmount
             FROM Calendar
             LEFT JOIN Profits_Referrals Refferals
                    ON Refferals.userId = ?
                       AND Refferals.date >= UNIX_TIMESTAMP(Calendar.calendar_date)
                       AND Refferals.date < UNIX_TIMESTAMP(Calendar.calendar_date + INTERVAL 1 DAY)
             WHERE Calendar.calendar_date >= ?
                   AND Calendar.calendar_date < ?) Refferals
  ON Referrals.calendar_date = Profits.calendar_date
ORDER BY Profits.Calendar_Date

(请记住,各个子查询为每个日期输出一行,并且所有内容都已在该日期聚合 - 我们可以根据日期加入。这也意味着我们不需要单独的 {base_table} 此处)

【讨论】:

  • 说真的,你想要一个日历表。在我看来,它们是最有用的分析表。您可以在它们上添加任意数量的索引(因为您几乎从不写入它们),这意味着您可以对通常必须忽略它们的查询进行基于索引的访问(即按星期几聚合,忽略月初)。对于常用的“派生”列(如我在此处使用的“第二天”),您可以创建输出数学的视图,如果性能最终成为问题,只需将它们具体化。
猜你喜欢
  • 2013-01-24
  • 2015-02-12
  • 2022-01-16
  • 2021-04-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-03
  • 1970-01-01
相关资源
最近更新 更多