2

我有以下查询,它将返回表交易中收入在 100 美元到 200 美元之间的用户数量

SELECT COUNT(users.id)
FROM transactions
LEFT JOIN users ON users.id = transactions.user_id
WHERE transactions.amount > 100 AND transactions.amount < 200

上面的查询返回下面的正确结果:

COUNT(users.id)
559               

我想扩展它,以便查询可以返回以下格式的数据:

COUNT(users.id)    :     amount
1678               :     0-100
559                :     100-200
13                 :     200-300

我怎样才能做到这一点?

4

3 回答 3

3

您可以在聚合函数中使用 CASE 表达式,它将在列中获得结果:

SELECT 
  COUNT(case when amount >= 0 and amount <= 100 then users.id end) Amt0_100,
  COUNT(case when amount >= 101 and amount <= 200 then users.id end) Amt101_200,
  COUNT(case when amount >= 201 and amount <= 300 then users.id end) Amt201_300
FROM transactions
LEFT JOIN users 
  ON users.id = transactions.user_id;

请参阅带有演示的 SQL Fiddle

您会注意到我更改了 0-100、101-200、201-300 的范围,否则您将在 100、200 值上计算两次用户 ID。

如果您想要行中的值,那么您可以使用:

select count(u.id), 
  CASE 
    WHEN amount >=0 and amount <=100 THEN '0-100'
    WHEN amount >=101 and amount <=200 THEN '101-200'
    WHEN amount >=201 and amount <=300 THEN '101-300'
  END Amount
from transactions t
left join users u
  on u.id = t.user_id
group by 
  CASE 
    WHEN amount >=0 and amount <=100 THEN '0-100'
    WHEN amount >=101 and amount <=200 THEN '101-200'
    WHEN amount >=201 and amount <=300 THEN '101-300'
  END 

请参阅带有演示的 SQL Fiddle

但是,如果您有许多需要计算计数的范围,那么您可能需要考虑创建一个包含范围的表,类似于以下内容:

create table report_range
(
  start_range int,
  end_range int
);

insert into report_range values
(0, 100),
(101, 200), 
(201, 300);

然后,您可以使用此表连接到当前表并按范围值分组:

select count(u.id) Total, concat(start_range, '-', end_range) amount
from transactions t
left join users u
  on u.id = t.user_id
left join report_range r
  on t.amount >= r.start_range
  and t.amount<= r.end_range
group by concat(start_range, '-', end_range);

请参阅SQL Fiddle with Demo

如果您不想使用范围创建新表,则始终可以使用派生表来获得相同的结果:

select count(u.id) Total, concat(start_range, '-', end_range) amount
from transactions t
left join users u
  on u.id = t.user_id
left join
(
  select 0 start_range, 100 end_range union all
  select 101 start_range, 200 end_range union all
  select 201 start_range, 300 end_range 
) r
  on t.amount >= r.start_range
  and t.amount<= r.end_range
group by concat(start_range, '-', end_range);

请参阅带有演示的 SQL Fiddle

于 2013-06-20T21:59:28.617 回答
2

一种方法是在您的 group by 中使用 case/when 语句。

SELECT
-- NB this must match your group by statement exactly 
-- otherwise you will get an error
CASE 
    WHEN amount <= 100
    THEN '0-100'
    WHEN amount <= 200
    THEN '100-200'
    ELSE '201+'
END Amount,
COUNT(*)
FROM  
    transactions
GROUP BY
CASE 
    WHEN amount <= 100
    THEN '0-100'
    WHEN amount <= 200
    THEN '100-200'
    ELSE '201+'
END

如果您打算在其他地方使用分组,将其定义为标量函数可能是有意义的(它看起来也更干净)

例如

SELECT 
AmountGrouping(amount),
COUNT(*)
FROM  
transactions
GROUP BY
AmountGrouping(amount)

如果您想完全通用:

SELECT
    concat(((amount DIV 100) * 100),'-',(((amount DIV 100) + 1) * 100)) AmountGroup,
    COUNT(*)
FROM  
    transactions
GROUP BY
    AmountGroup

Sql 小提琴

于 2013-06-20T22:05:07.367 回答
0

Bilbo,我尝试有创意并找到了一个非常好的解决方案 [对于那些喜欢数学的人(比如我)]
当 MySQL 整数除法运算符解决我们的问题时总是令人惊讶。

DROP SCHEMA IF EXISTS `stackoverflow3`;
CREATE SCHEMA `stackoverflow3`;
USE `stackoverflow3`;

CREATE TABLE users (
  id INT UNSIGNED PRIMARY KEY NOT NULL AUTO_INCREMENT,
  name VARCHAR(25) NOT NULL DEFAULT "-");

CREATE TABLE transactions(
  id INT UNSIGNED PRIMARY KEY NOT NULL AUTO_INCREMENT,
  user_id INT UNSIGNED NOT NULL,
  amount INT UNSIGNED DEFAULT 0,
  FOREIGN KEY (user_id) REFERENCES users (id));

INSERT users () VALUES (),(),();
INSERT transactions (user_id,amount)
  VALUES (1,120),(2,270),(3, 350),
    (2,500), (1,599), (1,550), (3,10),
    (3,20), (3,30), (3,50), (3,750);

SELECT
  COUNT(t.id),
  CONCAT(
    ((t.amount DIV 100)*100)," to ",((t.amount DIV 100 + 1)*100-1)
  ) AS amount_range
FROM transactions AS t
GROUP BY amount_range;

等待您的提问,巴金斯先生。

于 2013-06-20T23:09:22.390 回答