我一直在努力处理这些类型的查询。因此,我希望有人检查我的处理方法。 我被问到从第一笔交易开始,平均每个用户在12个小时内执行多少笔交易。
这是数据:
CREATE TABLE IF NOT EXISTS `transactions` (
`transactions_ts` timestamp ,
`user_id` int(6) unsigned NOT NULL,
`transaction_id` bigint NOT NULL,
`item` varchar(200), PRIMARY KEY(`transaction_id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `transactions` (`transactions_ts`, `user_id`, `transaction_id`,`item` ) VALUES
('2016-06-18 13:46:51.0', 13811335,1322361417, 'glove'),
('2016-06-18 17:29:25.0', 13811335,3729362318, 'hat'),
('2016-06-18 23::07:12.0', 13811335,1322363995,'vase' ),
('2016-06-19 07:14:56.0',13811335,7482365143, 'cup'),
('2016-06-19 21:59:40.0',13811335,1322369619,'mirror' ),
('2016-06-17 12:39:46.0',3378024101,9322351612, 'dress'),
('2016-06-17 20:22:17.0',3378024101,9322353031,'vase' ),
('2016-06-20 11:29:02.0',3378024101,6928364072,'tie'),
('2016-06-20 18:59:48.0',13811335,1322375547, 'mirror');
我的方法如下(下面是步骤和查询本身):
1)对于每个不同的user_id,找到他们的第一个小时和12个小时的交易时间戳。这是通过别名为t1的内部查询来完成的
2)然后,通过内部连接到第二个内部查询(t2),基本上,我用第一步的两个变量“ first_trans”和“ right_trans”扩充了事务表的每一行。 3)现在,通过where条件,我只选择落在first_trans和right_trans时间戳指定的间隔内的那些事务时间戳
4)现在将步骤3中的过滤后的表格汇总为每个用户的不同交易ID数
5)上面4个步骤的结果是一个表,其中每个用户都有从第一个时间戳记开始的12小时间隔内的事务计数。我将其包装在另一个选择中,该选择将用户的交易计数求和,然后除以用户数,得出每个用户的平均计数。
我很确定最终结果总体上是正确的,但是我一直认为我可能没有第四个选择。或者,也许整个代码有些笨拙,而我的目的是使此查询尽可能可读,而不必在计算上达到最佳。
select
sum(dist_ts)/count(*) as avg_ts_per_user
from (
select
count(distinct transaction_id) as dist_ts,
us_id
from
(select
user_id as us_id,
min(transactions_ts) as first_trans,
min(transactions_ts) + interval 12 hour as right_trans
from transactions
group by us_id )
as t1
inner join
(select * from transactions )
as t2
on t1.us_id=t2.user_id
where transactions_ts >= first_trans
and transactions_ts < right_trans
group by us_id
) as t3
答案 0 :(得分:1)
我认为本质上没有错误。该代码可以稍微简化(并进行如下整理):
select sum(dist_ts)/count(*) as avg_ts_per_user
from (
select count(distinct transaction_id) as dist_ts, us_id
from (
select user_id as us_id, min(transactions_ts) as first_trans, min(transactions_ts) + interval 12 hour as right_trans
from transactions
group by us_id
) as t1
inner join transactions as t2
on t1.us_id=t2.user_id and transactions_ts >= first_trans and transactions_ts < right_trans
group by us_id
) as t3
(select * from transactions ) as t2
在上面得到了简化,我有些随意地将一个where子句条件移到了内部联接的on子句上。
这是不使用内部联接的第二种方法:
select sum(cnt)/count(*) as avg_ts_per_user from (
select count(*) as cnt, t.user_id
from transactions t
where t.transactions_ts >= (select min(transactions_ts) from transactions where user_id = t.user_id)
and t.transactions_ts < (select min(transactions_ts) + interval 12 hour from transactions where user_id = t.user_id)
group by t.user_id
) sq
您可能应该对这两个查询运行EXPLAIN,以查看哪个查询在您的服务器上运行得更好。另请注意,min(transaction_ts)
为每个用户指定了两次。 MySql是否可以避免冗余计算?我不知道。一种可能性是创建一个由user_id
和min_transaction_ts
组成的临时表,以便该值被计算一次。仅当您的表中有很多行,甚至可能没有行时,这才有意义。