从具有多个表的时间戳开始按天分组

时间:2014-06-15 12:43:11

标签: mysql sql date group-by

我有两个包含timestamp列的表。

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

我的桌子:

profits

enter image description here

profits_referrals

enter image description here

我想要的结果:

╔═════════════╦══════════════╦══════╗
║    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

1 个答案:

答案 0 :(得分: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

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

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, >=, and an exclusive upper-bound, <查询正连续范围类型(​​除整数计数之外的所有内容)(该帖子是为SQL Server编写的,其中包含时间戳,但问题无处不在。请记住MySQL {{1} } / DATETIME类型具有用户可指定的小数秒数!)。对于负范围,请反转条件。
  • 使用TIMESTAMP上的函数(假设这只是一个标准的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 这里)