在MySQL数据库中,我有一个包含以下列的购买表:
USERID PURCHASE_AMOUNT
3 20
9 30
3 5
4 5
1 10
1 5
我想生成一个像这样的报告
SUM_OF_PURCHASES_RANGE NUM_OF_USERS
0-1 0
1-5 1
5-20 1
20-30 2
这意味着:有0个用户购买最多1个(购买总和)(含),有1个用户购买1到5个等...
我应该使用什么查询来生成它?
答案 0 :(得分:1)
更简单的语法:
SELECT PURCHASE_RANGE , COUNT(*) as NUM_OF_USERS
FROM
(
SELECT
CASE
WHEN PURCHASE_AMOUNT <= 1 THEN 1
WHEN PURCHASE_AMOUNT > 1 AND PURCHASE_AMOUNT <= 5 THEN 5
WHEN PURCHASE_AMOUNT > 5 AND PURCHASE_AMOUNT <= 10 THEN 10
WHEN PURCHASE_AMOUNT > 10 AND PURCHASE_AMOUNT <= 20 THEN 20
WHEN PURCHASE_AMOUNT > 20 AND PURCHASE_AMOUNT <= 30 THEN 30 END AS PURCHASE_RANGE
FROM Table1
) AS A
GROUP BY PURCHASE_RANGE
ORDER BY PURCHASE_RANGE
答案 1 :(得分:1)
您可以使用UNION
创建范围,只需LEFT JOIN
即可获得所有类别; (根据所需结果进行了编辑)
SELECT CONCAT(base.lower,'-',base.upper) PURCHASE_RANGE, COUNT(userid) NUM_OF_USERS
FROM (
SELECT 0 lower, 1 upper UNION SELECT 2, 5 UNION SELECT 6,20 UNION SELECT 21,30
) base
LEFT JOIN (
SELECT userid, SUM(purchase_amount) pa FROM purchases GROUP BY userid
) p
ON p.pa >= base.lower AND p.pa <= base.upper
GROUP BY base.upper
答案 2 :(得分:0)
试试这个
select PURCHASE_RANGE , NUM_OF_USERS
from (
select 1 as PURCHASE_RANGE ,count(*) as NUM_OF_USERS from table1 where PURCHASE_AMOUNT between 0 and 1
union all
select 5 ,count(*) from table1 where PURCHASE_AMOUNT between 1 and 5
union all
select 20 ,count(*) from table1 where PURCHASE_AMOUNT between 6 and 20
union all
select 30 ,count(*) from table1 where PURCHASE_AMOUNT between 21 and 30
)t
答案 3 :(得分:0)
如果您需要性能(这将进行全表扫描),有更快的方法可以执行此操作,但请尝试以下操作:
SELECT
SUM(CASE WHEN purchase_amount BETWEEN 0 AND 1 THEN 1 ELSE 0) bucket_0_to_1,
SUM(CASE WHEN purchase_amount BETWEEN 1 AND 5 THEN 1 ELSE 0) bucket_1_to_5,
SUM(CASE WHEN purchase_amount BETWEEN 5 AND 20 THEN 1 ELSE 0) bucket_5_to_20,
SUM(CASE WHEN purchase_amount BETWEEN 20 AND 30 THEN 1 ELSE 0) bucket_20_to_30,
SUM(CASE WHEN purchase_amount > 30 THEN 1 ELSE 0) bucket_over_30, FROM my_table LIMIT 1;
答案 4 :(得分:0)
如果范围发生变化,这可能会更容易。
with ranges(rstart, rfinish) as (
select 0, 1 union all
select 2, 5 union all
select 6, 20 union all
select 21, 30
), purchases(amount) as (
select sum(PURCHASE_AMOUNT)
from <purchases_basetable> -- <-- your tablename goes here
group by USERID
)
select
-- concat(case when r.rstart = 0 then 0 else r.rstart-1 end, '-', r.rfinish) as SUM_OF_PURCHASES_RANGE /* op's name for the group */,
concat(r.rstart, '-', r.rfinish) as SUM_OF_PURCHASES_RANGE /* better name for the group */,
count(*) as NUM_OF_USERS
from
purchases as p inner join
ranges as r
on p.amount between r.start and r.finish
group by r.rstart, r.rfinish
order by r.rstart, r.rfinish
我不知道mysql查询计划会是什么样子。将查询更改为使用派生表而不是表表达式是微不足道的。 (但无论如何我都把它包括在内。)
您可能还会发现UNPIVOT操作在支持它的平台上很有用。
select
-- concat(case when r.rstart = 0 then 0 else r.rstart-1 end, '-', r.rfinish) as SUM_OF_PURCHASES_RANGE /* op's name for the group */,
concat(r.rstart, '-', r.rfinish) as SUM_OF_PURCHASES_RANGE /* better name for the group */,
count(*) as NUM_OF_USERS
from
(
select sum(PURCHASE_AMOUNT) as amount
from <purchases_basetable> -- <-- your tablename goes here
group by USERID
) as p inner join
(
select 0 as rstart, 1 as rfinish union all
select 2, 5 union all
select 6, 20 union all
select 21, 30
) as r
on p.amount between r.start and r.finish
group by r.rstart, r.rfinish
order by r.rstart, r.rfinish
答案 5 :(得分:0)
要在行中获取所需的值,您需要从包含您感兴趣的所有值的驱动程序表开始,然后left outer join
到数据:
select driver.mina, coalesce(sum(cnt), 0) as Num_Of_Users
from (select 1 as mina, 5 as maxa union all
select 5, 10 union all
select 10, 20 union all
select 20, 30 union all
select 30, NULL
) driver left outer join
(select purchase_amount, count(*) as cnt
from purchases
group by purchase_amount
) pa
on driver.mina >= pa.purchase_amount and
(pa.purchase_amount < driver.maxa or driver.maxa is null)
group by driver.mina
order by driver.mina
如果没有内部group by
,您实际上可以这样做。这可能会在加入之前显着减少数据的大小(特别是在您的示例中)。
我建议你在每一行都包括范围的下限和上限。