2015-10-25 83 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) 

使用一個派生表得到驗證的電子郵件地址的每個用戶已經