2015-10-25 241 views
1

我的表结构有点类似于此:SQL多表连接,GROUP BY和HAVING

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,'[email protected]','1',1), 
(2,'[email protected]','0',1), 
(3,'[email protected]','0',3), 
(4,'[email protected]','1',4), 
(5,'[email protected]','0',3), 
(6,'[email protected]','0',5), 
(7,'[email protected]','0',1) 
; 
INSERT INTO `social` 
    (`id`,`user_id`) 
VALUES 
(1,4), 
(2,4), 
(3,5), 
(4,4), 
(5,4) 
; 

我想要得到的是所有电子邮件:

  1. 未核实
  2. 属于一个用户谁没有,即0,验证电子邮件
  3. 属于一个用户谁没有,即0,社会记录

随着下面的查询,我能申请的第1和第3条件,但不是第二届一个:

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以及

回答

1

您可以使用下面的查询:

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实现了第一个条件,即返回哪些不是所有的电子邮件已验证
  • u实现第二和第三个条件,即它返回一组没有经过验证的电子邮件的所有用户没有社交记录。

eu之间执行INNER JOIN会返回满足条件no的所有电子邮件。 1属于满足条件no的用户。 2和3

Demo here

您也可以使用此查询:

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) 

子查询中使用,以选择所有user_id满足条件没有。 2和3。 1是多余的,因为如果用户没有经过验证的电子邮件,那么验证的电子邮件与此用户无关。

Demo here

+0

谢谢你,先生的数量。 :)你认为还有其他方法可以达到同样的效果吗? – mrudult

+0

@MrudulT检查我所做的修改。 –

+0

@MrudulT当你的表变大时,“拥有”和“子查询”变成了一个非常糟糕的主意。看到我的答案替代 – yomexzo

3

有趣的和棘手的一个。

我看到你在那里发生了一些事情。但子查询成为非常糟糕的想法,当你的表变大。

请参阅下面的方法。不要忘记设置你的索引!

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; 
+0

这一个是非常棘手的:) – mrudult

+0

大声笑..是的..你可能想要去与此...学到的教训子查询和索引困难 - 同时处理跨越数百万记录的表。 – yomexzo

0

只需运行一个联合查询:

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; 
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) 

使用一个派生表得到验证的电子邮件地址的每个用户已经