MySQL加入3个带计数的表

时间:2017-07-21 16:35:26

标签: mysql count left-join

我有以下表格:

用户

  • ID
  • 用户名
  • 如first_name
  • 电子邮件

  • ID
  • USER_ID

付款

  • ID
  • student_id数据
  • is_paid
  • 日期

我有以下问题:

SELECT users.username, users.first_name, users.email,
    COUNT(payments.id) as pending_payments
FROM users
LEFT JOIN students ON users.id = students.user_id 
LEFT JOIN payments ON payments.student_id = students.id AND payments.is_paid = 0
WHERE users.username LIKE 'testuser';

当用户名存在且没有待付款时,它会返回:

| username | first_name | email                | pending_payments |

| testuser | test       | testuser@example.com |        0         |

当用户名存在并且有2个待处理付款时,它会返回:

| username | first_name | email                 | pending_payments |

| testuser2| test2      | testuser@2example.com |        2         |

但是当用户名不存在时,它会返回:

| username | first_name | email              | pending_payments   |

| NULL     | NULL       | NULL               |         0          |

相反,当用户名不存在时,我想要结果:

空集。

如何修改查询以获得预期结果? (保留用户存在时的行为,但当不存在时返回Empty set,而不是一行将NULL值)。

请参阅SQLfiddle:http://sqlfiddle.com/#!9/aab424/11

修改 提供了工作解决方案http://sqlfiddle.com/#!9/aab424/72

5 个答案:

答案 0 :(得分:3)

如果没有使用GROUP BY子句,像COUNT(*)这样的聚合函数总是生成一行。因此,如果您想要一个空结果,请使用GROUP BY子句:

SELECT users.username, users.first_name, users.email,
    COUNT(payments.id) as pending_payments
FROM users
LEFT JOIN students ON users.id = students.user_id 
LEFT JOIN payments ON payments.student_id = students.id AND payments.is_paid = 0
WHERE users.username LIKE 'testuser3'
GROUP BY users.username, users.first_name, users.email;

请注意,每个用户使用该用户名将获得一行。如果username不是唯一的并且您希望为具有相同username的所有用户获取一行,则可以使用HAVING子句而不是GROUP BY来“删除”“空”行。

SELECT users.username, COUNT(payments.id) as pending_payments
FROM users
LEFT JOIN students ON users.id = students.user_id 
LEFT JOIN payments ON payments.student_id = students.id AND payments.is_paid = 0
WHERE users.username LIKE 'testuser3'
HAVING username IS NOT NULL;

SQLFiddle

这与LEFT或任何JOIN无关。这是聚合函数的工作方式(在MySQL中),你会看到相同的行为,只有一个没有连接的简单例子,如

SELECT username, COUNT(*)
FROM users
WHERE username = 'testuser3';

| username | COUNT(*) |
|----------|----------|
|   (null) |        0 |

请注意,此查询不符合SQL标准,并且在严格模式下,您将收到错误,因为选择了username而未在GROUP BY子句中列出。 (SQLFiddle)。

documentation说:

  

如果您要为COUNT()值之外的列命名,请选择a   应该存在GROUP BY子句来命名那些相同的列。   否则,会发生以下情况:

     
      
  • 如果启用了ONLY_FULL_GROUP_BY SQL模式,则会发生错误:   [...]

  •   
  • 如果未启用ONLY_FULL_GROUP_BY,则处理查询   将所有行视为一个组,但为每个组选择的值   命名列是不确定的。服务器可以自由选择值   从任何一行:   [...]

  •   

再次:“服务器可以自由选择任何行的值”。但由于没有匹配的行,因此返回NULL

答案 1 :(得分:1)

我认为为用户名等找到的NULL值主要是因为您的查询中缺少分组。

我认为应该可以使用:

SELECT users.username, 
       users.first_name, 
       users.email,
       SUM(COALESCE( p.is_paid = 0, 0))  as pending_payments
FROM users
LEFT JOIN students s on s.user_id = users.id
LEFT JOIN payments p on p.student_id = s.id
WHERE users.username LIKE 'testuser3'
GROUP BY users.username, 
         users.first_name, 
         users.email;

当省略WHERE行时,它将列出所有用户:

SELECT users.username, 
       users.first_name, 
       users.email,
       SUM(COALESCE( p.is_paid = 0, 0))  as pending_payments
FROM users
LEFT JOIN students s on s.user_id = users.id
LEFT JOIN payments p on p.student_id = s.id
GROUP BY users.username, 
         users.first_name, 
         users.email;

它会提及所有用户,即使那些没有任何付款(未决或未付款)的用户或未列入学生表的用户

答案 2 :(得分:0)

你显然在users表中有用户名为NULL的用户。您可以过滤掉WHERE子句中的NULL值。

SELECT users.username, users.first_name, users.email,
    COUNT(payments.is_paid = 0) as pending_payments
FROM users
LEFT JOIN students ON users.id = students.user_id 
LEFT JOIN payments ON payments.student_id = students.id
WHERE users.username LIKE 'testuser' AND users.username IS NOT NULL;

答案 3 :(得分:0)

你收到" null"响应是由LEFT JOIN语句引起的。即使参数在该表中不匹配,左连接也会完成,因为它们可能构成另一个(连接)表中的列。要仅显示符合条件的结果,请使用INNER JOIN语句。

此外,您希望COUNT(*)按用户名付款,您应该GROUP BY用户名。

尝试以下方法(未经测试,抱歉,不要在电脑前面加上编辑器)

SELECT u.username, u.first_name, u.email, COUNT(p.is_paid) as pending_payments
FROM users u
  INNER JOIN students s ON u.id = s.user_id
  INNER JOIN payments p ON s.user_id = p.student_id
WHERE u.username LIKE '%testuser%'
      AND u.username IS NOT NULL
GROUP BY u.username
ORDER BY p.is_paid DESC, u.username ASC

此外,如果您只想显示已暂停付款的结果,则可以在 HAVING之后添加GROUP BY语句,例如< / p>

HAVING u.pending_payments > 0

使用以下设置进行测试:

Demo setup with demo data

下面显示查询结果。第二个查询没有HAVING语句。

Showing queries with results

答案 4 :(得分:0)

为什么不能使用IS NOT NULL?

 SELECT users.username, users.first_name, users.email,
        COUNT(payments.id) as pending_payments
    FROM users
    LEFT JOIN students ON users.id = students.user_id 
    LEFT JOIN payments ON payments.student_id = students.id AND payments.is_paid = 0
    WHERE users.username LIKE 'testuser' and users.username is NOT NULL;