SQL 多个 table JOINS、GROUP BY 和 HAVING
SQL Multiple table JOINS, GROUP BY and HAVING
我有一个 table 结构有点类似于此:
CREATE TABLE `user`
(`id` int, `name` varchar(7));
CREATE TABLE `email`
(`id` int, `email_address` varchar(50), `verified_flag` tinyint(1),`user_id` int);
CREATE TABLE `social`
(`id` int,`user_id` int);
INSERT INTO `user`
(`id`, `name`)
VALUES
(1,'alex'),
(2,'jon'),
(3,'arya'),
(4,'sansa'),
(5,'hodor')
;
INSERT INTO `email`
(`id`,`email_address`,`verified_flag`,`user_id`)
VALUES
(1,'alex@gmail.com','1',1),
(2,'jon@gmail.com','0',1),
(3,'arya@gmail.com','0',3),
(4,'sansa@gmail.com','1',4),
(5,'reek@gmail.com','0',3),
(6,'hodor@gmail.com','0',5),
(7,'tyrion@gmail.com','0',1)
;
INSERT INTO `social`
(`id`,`user_id`)
VALUES
(1,4),
(2,4),
(3,5),
(4,4),
(5,4)
;
我要获取的是所有邮件:
- 未验证
- 属于没有(即 0)验证电子邮件的用户
- 属于没有(即 0)社交记录的用户
通过以下查询,我可以应用第一个和第三个条件,但不能应用第二个条件:
SELECT *
FROM `email`
INNER JOIN `user` ON `user`.`id` = `email`.`user_id`
LEFT JOIN `social` ON `user`.`id` = `social`.`user_id`
WHERE `email`.`verified_flag` = 0
GROUP BY `email`.`user_id`,`email`.`email_address`
HAVING COUNT(`social`.`id`) = 0
我怎样才能达到这个结果?
还有sqlfiddle
您可以使用以下查询:
SELECT e.`id`, e.`email_address`, e.`verified_flag`, e.`user_id`
FROM (
SELECT `id`,`email_address`,`verified_flag`,`user_id`
FROM `email`
WHERE `verified_flag` = 0) AS e
INNER JOIN (
SELECT `id`, `name`
FROM `user` AS t1
WHERE NOT EXISTS (SELECT 1
FROM `email` AS t2
WHERE `verified_flag` = 1 AND t1.`id` = t2.`user_id`)
AND
NOT EXISTS (SELECT 1
FROM `social` AS t3
WHERE t1.`id` = t3.`user_id`)
) AS u ON u.`id` = e.`user_id`;
此查询使用两个派生表:
e
实现第一个条件,即 returns 所有未验证的电子邮件
u
实现第二个和第三个条件,即它 returns 一组没有经过验证的电子邮件的所有用户 和 没有社交记录。
在 e
和 u
之间执行 INNER JOIN
returns 所有满足条件号的电子邮件。 1 属于满足条件的用户2 和 3.
您也可以使用此查询:
SELECT *
FROM `email`
WHERE `user_id` IN (
SELECT `email`.`user_id`
FROM `email`
INNER JOIN `user` ON `user`.`id` = `email`.`user_id`
LEFT JOIN `social` ON `user`.`id` = `social`.`user_id`
GROUP BY `email`.`user_id`
HAVING COUNT(`social`.`id`) = 0 AND
COUNT(CASE WHEN `email`.`verified_flag` = 1 THEN 1 END) = 0 )
使用子查询是为了select全部user_id
满足条件号。 2 和 3. 条件编号1 是多余的,因为如果用户没有经过验证的电子邮件,那么经过验证的电子邮件就不可能与该用户相关。
有趣且棘手的一个。
我看到你那里发生了一些事情。但是当你的表变大时,有和子查询就变成了一个非常的坏主意。
方法见下文。不要忘记设置索引!
SELECT * from email
LEFT JOIN social on email.user_id = social.user_id
-- tricky ... i'm going back to email table to pick verified emails PER user
LEFT JOIN email email2 on email2.user_id = email.user_id AND email2.verified_flag = 1
WHERE
-- you got this one going already :)
email.verified_flag = 0
-- user does not have any social record
AND social.id is null
-- email2 comes in handy here ... we limit resultset to include only users that DOES NOT have a verified email
AND email2.id is null
ORDER BY email.user_id asc;
简单地运行一个联合查询:
SELECT `user_id`, `email_address`, `verified_flag`, 'No Email' as `Type`
FROM `email` RIGHT JOIN `user` ON `user`.`id` = `email`.`user_id`
WHERE `email`.`user_id` IS NULL
UNION
SELECT `user_id`, `email_address`, `verified_flag`, 'Not Verified' as `Type`
FROM `email` INNER JOIN `user` ON `user`.`id` = `email`.`user_id`
WHERE `email`.`verified_flag` = 0
UNION
SELECT `user_id`, `email_address`, `verified_flag`, 'No Social' as `Type`
FROM `email` INNER JOIN `user` ON `user`.`id` = `email`.`user_id`
LEFT JOIN `social` ON `user`.`id` = `social`.`user_id`
GROUP BY `user_id`, `email_address`, `verified_flag`
HAVING COUNT(IFNULL(`social`.`id`, 0)) = 0;
SELECT
u.id AS u_id
, u.name AS u_name
, e.email_address AS e_email
, e.verified_flag AS e_verify
, e.user_id AS e_uid
, s.id AS s_id
, s.user_id AS u_id
, COALESCE(ver_e.ver_email_count,0) as ver_email_count
FROM
email as e
LEFT OUTER JOIN
user as u
ON u.id = e.user_id
LEFT OUTER JOIN
social AS s
ON u.id = s.user_id
LEFT OUTER JOIN
(
SELECT
COUNT(email_address) AS ver_email_count
, user_id
FROM
email
) AS ver_e
ON u.id = ver_e.user_id
GROUP BY
e.user_id
HAVING e.verified_flag = 0
AND
ver_email_count = 0
AND
ISNULL(s.id)
使用一个派生的 table 来获取每个用户已获得的已验证电子邮件地址的数量
我有一个 table 结构有点类似于此:
CREATE TABLE `user`
(`id` int, `name` varchar(7));
CREATE TABLE `email`
(`id` int, `email_address` varchar(50), `verified_flag` tinyint(1),`user_id` int);
CREATE TABLE `social`
(`id` int,`user_id` int);
INSERT INTO `user`
(`id`, `name`)
VALUES
(1,'alex'),
(2,'jon'),
(3,'arya'),
(4,'sansa'),
(5,'hodor')
;
INSERT INTO `email`
(`id`,`email_address`,`verified_flag`,`user_id`)
VALUES
(1,'alex@gmail.com','1',1),
(2,'jon@gmail.com','0',1),
(3,'arya@gmail.com','0',3),
(4,'sansa@gmail.com','1',4),
(5,'reek@gmail.com','0',3),
(6,'hodor@gmail.com','0',5),
(7,'tyrion@gmail.com','0',1)
;
INSERT INTO `social`
(`id`,`user_id`)
VALUES
(1,4),
(2,4),
(3,5),
(4,4),
(5,4)
;
我要获取的是所有邮件:
- 未验证
- 属于没有(即 0)验证电子邮件的用户
- 属于没有(即 0)社交记录的用户
通过以下查询,我可以应用第一个和第三个条件,但不能应用第二个条件:
SELECT *
FROM `email`
INNER JOIN `user` ON `user`.`id` = `email`.`user_id`
LEFT JOIN `social` ON `user`.`id` = `social`.`user_id`
WHERE `email`.`verified_flag` = 0
GROUP BY `email`.`user_id`,`email`.`email_address`
HAVING COUNT(`social`.`id`) = 0
我怎样才能达到这个结果? 还有sqlfiddle
您可以使用以下查询:
SELECT e.`id`, e.`email_address`, e.`verified_flag`, e.`user_id`
FROM (
SELECT `id`,`email_address`,`verified_flag`,`user_id`
FROM `email`
WHERE `verified_flag` = 0) AS e
INNER JOIN (
SELECT `id`, `name`
FROM `user` AS t1
WHERE NOT EXISTS (SELECT 1
FROM `email` AS t2
WHERE `verified_flag` = 1 AND t1.`id` = t2.`user_id`)
AND
NOT EXISTS (SELECT 1
FROM `social` AS t3
WHERE t1.`id` = t3.`user_id`)
) AS u ON u.`id` = e.`user_id`;
此查询使用两个派生表:
e
实现第一个条件,即 returns 所有未验证的电子邮件u
实现第二个和第三个条件,即它 returns 一组没有经过验证的电子邮件的所有用户 和 没有社交记录。
在 e
和 u
之间执行 INNER JOIN
returns 所有满足条件号的电子邮件。 1 属于满足条件的用户2 和 3.
您也可以使用此查询:
SELECT *
FROM `email`
WHERE `user_id` IN (
SELECT `email`.`user_id`
FROM `email`
INNER JOIN `user` ON `user`.`id` = `email`.`user_id`
LEFT JOIN `social` ON `user`.`id` = `social`.`user_id`
GROUP BY `email`.`user_id`
HAVING COUNT(`social`.`id`) = 0 AND
COUNT(CASE WHEN `email`.`verified_flag` = 1 THEN 1 END) = 0 )
使用子查询是为了select全部user_id
满足条件号。 2 和 3. 条件编号1 是多余的,因为如果用户没有经过验证的电子邮件,那么经过验证的电子邮件就不可能与该用户相关。
有趣且棘手的一个。
我看到你那里发生了一些事情。但是当你的表变大时,有和子查询就变成了一个非常的坏主意。
方法见下文。不要忘记设置索引!
SELECT * from email
LEFT JOIN social on email.user_id = social.user_id
-- tricky ... i'm going back to email table to pick verified emails PER user
LEFT JOIN email email2 on email2.user_id = email.user_id AND email2.verified_flag = 1
WHERE
-- you got this one going already :)
email.verified_flag = 0
-- user does not have any social record
AND social.id is null
-- email2 comes in handy here ... we limit resultset to include only users that DOES NOT have a verified email
AND email2.id is null
ORDER BY email.user_id asc;
简单地运行一个联合查询:
SELECT `user_id`, `email_address`, `verified_flag`, 'No Email' as `Type`
FROM `email` RIGHT JOIN `user` ON `user`.`id` = `email`.`user_id`
WHERE `email`.`user_id` IS NULL
UNION
SELECT `user_id`, `email_address`, `verified_flag`, 'Not Verified' as `Type`
FROM `email` INNER JOIN `user` ON `user`.`id` = `email`.`user_id`
WHERE `email`.`verified_flag` = 0
UNION
SELECT `user_id`, `email_address`, `verified_flag`, 'No Social' as `Type`
FROM `email` INNER JOIN `user` ON `user`.`id` = `email`.`user_id`
LEFT JOIN `social` ON `user`.`id` = `social`.`user_id`
GROUP BY `user_id`, `email_address`, `verified_flag`
HAVING COUNT(IFNULL(`social`.`id`, 0)) = 0;
SELECT
u.id AS u_id
, u.name AS u_name
, e.email_address AS e_email
, e.verified_flag AS e_verify
, e.user_id AS e_uid
, s.id AS s_id
, s.user_id AS u_id
, COALESCE(ver_e.ver_email_count,0) as ver_email_count
FROM
email as e
LEFT OUTER JOIN
user as u
ON u.id = e.user_id
LEFT OUTER JOIN
social AS s
ON u.id = s.user_id
LEFT OUTER JOIN
(
SELECT
COUNT(email_address) AS ver_email_count
, user_id
FROM
email
) AS ver_e
ON u.id = ver_e.user_id
GROUP BY
e.user_id
HAVING e.verified_flag = 0
AND
ver_email_count = 0
AND
ISNULL(s.id)
使用一个派生的 table 来获取每个用户已获得的已验证电子邮件地址的数量