SQL Server选择具有多个分组

时间:2012-12-20 16:48:33

标签: sql sql-server

我有两个表格描述用户及其付款:

CREATE TABLE test_users
(id int IDENTITY NOT NULL,
name varchar(25),
PRIMARY KEY (id));

CREATE TABLE test_payments
(id int IDENTITY NOT NULL,
user_id int NOT NULL,
money money NOT NULL,
date datetime NOT NULL,
PRIMARY KEY (id));

INSERT INTO test_users (name) 
VALUES ('john');

INSERT INTO test_users (name) 
VALUES ('peter');

INSERT INTO test_payments (user_id, money, date)
VALUES (1, $1, CONVERT(datetime, '15.12.2012'));

INSERT INTO test_payments (user_id, money, date)
VALUES (1, $2, CONVERT(datetime, '16.12.2012'));

INSERT INTO test_payments (user_id, money, date)
VALUES (2, $1, CONVERT(datetime, '16.12.2012'));

INSERT INTO test_payments (user_id, money, date)
VALUES (2, $3, CONVERT(datetime, '17.12.2012'));

INSERT INTO test_payments (user_id, money, date)
VALUES (1, $1, CONVERT(datetime, '19.12.2012'));

表test_users:

id  name
-------------
1   john
2   peter

表test_payments:

id  user_id money   last_activity
---------------------------------------
1   1   1.0000  2012-12-15
2   1   2.0000  2012-12-16
3   2   1.0000  2012-12-16
4   2   3.0000  2012-12-17
5   1   1.0000  2012-12-19

我需要制作一个用户统计信息,告诉我:

  1. 用户名
  2. 一段时间的总费用
  3. 最后的日期 用户的活动(一般情况下,不是一段时间)。
  4. 例如,参加15-18.12.12期间我期望得到以下结果:

    name    total    last_activity
    --------------------------------
    peter   $4       2012-12-17
    john    $3       2012-12-19
    

    我尝试过以下查询:

    SELECT u.*, SUM(p.money) total, MAX(p.date) last_activity
    FROM test_users u
    JOIN test_payments p
    ON u.id= p.user_id
    WHERE p.date BETWEEN CONVERT(datetime, '15.12.2012') AND CONVERT(datetime, '18.12.2012')
    GROUP BY u.id, u.name
    ORDER BY total DESC;
    

    但是last_activity的结果是错误的,因为它也在日期范围内:

    id  name    total   last_activity
    --------------------------------
    2   peter   4.0000  2012-12-17
    1   john    3.0000  2012-12-16
    

    请提出解决方案。

4 个答案:

答案 0 :(得分:3)

您需要将条件从where子句移到case语句:

SELECT u.id, u.name,
      SUM(case when p.date BETWEEN CONVERT(datetime, '15.12.2012') AND CONVERT(datetime, '18.12.2012')
               then p.money
          end) total,
      MAX(p.date) last_activity
FROM test_users u JOIN
     test_payments p
     ON u.id= p.user_id
GROUP BY u.id, u.name
ORDER BY total DESC;

如果您只想要在该期间内付款的用户,则可以添加:

having total is not null

如果希望NULL值显示为0而不是NULL,则在case语句中包含else 0

答案 1 :(得分:3)

在我工作的时候,看起来有几个其他的答案突然出现,但无论如何它在这里。这里有一个工作的sql小提琴:http://sqlfiddle.com/#!3/14808/6

基本上,无论日期范围如何,您都需要一个查询来提取最大日期。我选择将此作为相关子查询。

SELECT 
  u.id,
  u.name,
  SUM(IsNull(money,0)) as TotalMoneyInRange,
  (SELECT max(date) FROM test_payments where user_id = u.id) AS LastPaymentOverAll
FROM test_users AS u
LEFT JOIN test_payments AS p
ON u.id = p.user_id
WHERE 
  p.date IS NULL OR
  p.date between 
  CAST('12-11-2012' AS datetime) --range begin
  and 
  CAST('12-16-2012' as datetime) --range end
GROUP BY u.id, u.name

答案 2 :(得分:1)

您还可以使用子查询来获得结果:

SELECT u.*, total, last_activity
FROM test_users u
JOIN
(
  select sum(money) total, user_id
  from test_payments
  WHERE date BETWEEN CONVERT(datetime, '2012-12-15') 
    AND CONVERT(datetime, '2012-12-18')
  group by user_id
) p
  ON u.id= p.user_id
inner join 
(
  select user_id, max(date) last_activity
  from test_payments
  group by user_id
) p1
  on p.user_id = p1.user_id
ORDER BY total DESC;

请参阅SQL Fiddle with Demo

答案 3 :(得分:0)

您可以为没有WHERE子句的MAX日期添加子查询,如下所示:

SELECT 
  u.*
  ,SUM(p.money) total
  ,a.max_date last_activity
FROM test_users u
  INNER JOIN test_payments p ON u.id = p.user_id
  INNER JOIN (SELECT user_id, MAX(date) AS max_date 
              FROM test_payments 
              GROUP BY user_id) a ON u.id = a.user_id
WHERE p.date BETWEEN CONVERT(datetime, '15.12.2012') AND CONVERT(datetime, '18.12.2012')
GROUP BY u.id, u.name, a.max_date
ORDER BY total DESC;