我有两个包含timestamp
列的表。
我想按天划分结果。例如:从2014/06/10
到2014/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
答案 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}
因此。
这里要注意的一些事情:
int
,但最好不必担心它。>=
, 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
这里)