2012-06-20 67 views
1

我知道標題聽起來像已經有幾十個類似的問題,但我認爲這個有點不同。不過,如果已經有類似的問題,請給我指出。從一個表中獲取不在另一個表中的記錄

基本上,我有兩個表格:usersresumes。下面是他們的模式的片段:

users: 
    id signup_time 
resumes: 
    id user_id modified_time 

現在,我需要獲取所有用戶的總數,而不在用戶指定的時間框架簡歷(所有日期是UNIX時間戳),按天分組,周,或者一般情況下他們沒有上傳簡歷的月份。這是困擾我最大,因爲如果不進行分組,查詢可能看起來像:

SELECT u.id FROM `jb_users` u WHERE 
    u.id NOT IN (
     SELECT r.user_id FROM `jb_resumes` r 
     WHERE (r.modified_time BETWEEN 1330581600 AND 1335848399) 
    ) AND u.signup_time >= 1330581600 

因此,例如,讓我們來看一些例子。希望這會更容易理解。

假設我們有一個數據:

users 
    id signup_time 
    --------------- 
    1 1340214369 (20.06.2012) 
    2 1330754400 (03.03.2012) 
    3 1329285600 (15.02.2012) 
    4 1324447200 (21.12.2011) 
resumes 
    id user_id modified_time 
    -------------------------- 
    1 1  1340214369 (20.06.2012) 
    2 2  1330840800 (04.03.2012) 
    3 2  1340214369 (20.06.2012) 
    4 3  1334506920 (15.04.2012) 
    5 3  1334638800 (17.04.2012) 
    6 2  1334638800 (17.04.2012) 
    7 3  1336798800 (12.05.2012) 

對於時間表01.03.2012 00:00:00 - 30.04.2012 23:59:59(按月份進行分組),它應該返回:

count user_ids time 
2  3,4   1330840800 (03.2012 - can be any date in the month, in fact) 
1  4   1334506920 (04.2012 - can be any date in the month, in fact) 

對於同一時間,但每天的分組,它應該返回:

count user_ids time 
2  3,4   1330840800 (04.03.2012) 
2  2,4   1334506920 (15.04.2012) 
1  4   1334638800 (17.04.2012) 

我希望這個問題是非常明顯的。如果沒有,請讓我知道。

數據將用PHP進行處理,所以如果使用單個查詢(甚至是子查詢)無法實現這一點,那麼使用PHP處理數據也是可以的。

謝謝。

+0

我完全被這句話迷惑: >按日期時,他們沒有一個簡歷上傳 所以,嗯,你需要它的分組他們沒有提交簡歷的日期? –

+0

你想按日或月份分組嗎?不同的查詢或相同的查詢? –

+0

@SomnathMuluk - 我需要按日,周和月進行分組。 – Pateman

回答

1

下面是我提出的按月分組的解決方案。我用你的數據在我的本地MySQL安裝到測試結果:

SELECT 
    COUNT(*) AS cnt, 
    GROUP_CONCAT(b.id ORDER BY b.id) AS user_ids, 
    a.monthgroup 

FROM 
(
    SELECT MONTH(FROM_UNIXTIME(modified_time)) AS monthgroup 
    FROM jb_resumes 
    WHERE modified_time BETWEEN 
     UNIX_TIMESTAMP('2012-03-01 00:00:00') 
     AND UNIX_TIMESTAMP('2012-04-30 23:59:59') 
    GROUP BY monthgroup 
) a 
CROSS JOIN 
    jb_users b 
LEFT JOIN 
    jb_resumes c ON 
     b.id = c.user_id 
     AND a.monthgroup = MONTH(FROM_UNIXTIME(modified_time)) 
WHERE 
    b.signup_time < UNIX_TIMESTAMP('2012-04-30 23:59:59') 
    AND c.user_id IS NULL 
GROUP BY 
    a.monthgroup 
ORDER BY 
    a.monthgroup 

Result Set

這是一個有點笨重,所以我要看看我能想出更完美的解決方案。

白天分組解決方案:

SELECT 
    COUNT(*) AS cnt, 
    GROUP_CONCAT(b.id ORDER BY b.id) AS user_ids, 
    a.daygroup 

FROM 
(
    SELECT MAKEDATE(YEAR(FROM_UNIXTIME(modified_time)), DAYOFYEAR(FROM_UNIXTIME(modified_time))) AS daygroup 
    FROM jb_resumes 
    WHERE modified_time BETWEEN 
     UNIX_TIMESTAMP('2012-03-01 00:00:00') 
     AND UNIX_TIMESTAMP('2012-04-30 23:59:59') 
    GROUP BY daygroup 
) a 
CROSS JOIN 
    jb_users b 
LEFT JOIN 
    jb_resumes c ON 
     b.id = c.user_id 
     AND a.daygroup = MAKEDATE(YEAR(FROM_UNIXTIME(modified_time)), DAYOFYEAR(FROM_UNIXTIME(modified_time))) 
WHERE 
    b.signup_time < UNIX_TIMESTAMP('2012-04-30 23:59:59') 
    AND c.user_id IS NULL 
GROUP BY 
    a.daygroup 
ORDER BY 
    a.daygroup 

編輯:爲期一個月的分組查詢的說明:

既然你問了解決方案的說明,這裏是我想通了:

我們首先要做的是在一段時間內從所有modified_time s中提取月份分組:

SELECT MONTH(FROM_UNIXTIME(modified_time)) AS monthgroup 
FROM jb_resumes 
WHERE modified_time BETWEEN 
    UNIX_TIMESTAMP('2012-03-01 00:00:00') 
    AND UNIX_TIMESTAMP('2012-04-30 23:59:59') 
GROUP BY monthgroup 

,導致:

Step 1

然後以比較各monthgroup和每個用戶的組合,找出哪些用戶不具有monthgroup內被修改的時候,我們必須做出monthgroup與所有用戶之間的笛卡爾積。由於上面的查詢已在使用GROUP BY,我們不能直接在查詢中的連接,而是必須把它包在一個子選擇去FROM子句中:

Step 2

SELECT 
    a.monthgroup, 
    b.* 
FROM 
(
    SELECT MONTH(FROM_UNIXTIME(modified_time)) AS monthgroup 
    FROM jb_resumes 
    WHERE modified_time BETWEEN 
     UNIX_TIMESTAMP('2012-03-01 00:00:00') 
     AND UNIX_TIMESTAMP('2012-04-30 23:59:59') 
    GROUP BY monthgroup 
) a 
CROSS JOIN 
    jb_users b 
-- 
ORDER BY a.monthgroup, b.id #for clarity's sake 

,導致

現在我們有monthgroup s和所有id s的組合,但我們不想包含晚於時間範圍的signup_time的用戶,所以我們通過在我們的WHERE clau中引入第一個條件來過濾它們SE:

SELECT 
    a.monthgroup, 
    b.* 
FROM 
(
    SELECT MONTH(FROM_UNIXTIME(modified_time)) AS monthgroup 
    FROM jb_resumes 
    WHERE modified_time BETWEEN 
     UNIX_TIMESTAMP('2012-03-01 00:00:00') 
     AND UNIX_TIMESTAMP('2012-04-30 23:59:59') 
    GROUP BY monthgroup 
) a 
CROSS JOIN 
    jb_users b 
WHERE 
    b.signup_time < UNIX_TIMESTAMP('2012-04-30 23:59:59') 
-- 
ORDER BY a.monthgroup, b.id #for clarity's sake 

,導致:

Step 3

通知id1已經被過濾掉了。 現在我們可以通過LEFT JOIN讓我們比較:

SELECT 
    a.monthgroup, 
    b.*, 
    c.* 
FROM 
(
    SELECT MONTH(FROM_UNIXTIME(modified_time)) AS monthgroup 
    FROM jb_resumes 
    WHERE modified_time BETWEEN 
     UNIX_TIMESTAMP('2012-03-01 00:00:00') 
     AND UNIX_TIMESTAMP('2012-04-30 23:59:59') 
    GROUP BY monthgroup 
) a 
CROSS JOIN 
    jb_users b 
LEFT JOIN 
    jb_resumes c ON 
     b.id = c.user_id 
     AND a.monthgroup = MONTH(FROM_UNIXTIME(modified_time)) 
WHERE 
    b.signup_time < UNIX_TIMESTAMP('2012-04-30 23:59:59') 
-- 
ORDER BY a.monthgroup, b.id #for clarity's sake 

結果造成:

Step 4

在這裏,我們LEFT JOIN荷蘭國際集團對用戶具有jb_resumes簡歷修改的條件該修改發生在monthgroup值的月份內。如果用戶在該月沒有重新開始修改,則LEFT JOIN將爲表中的值返回NULL。我們WANT那些條件不滿足的用戶,因此,我們必須把我們的第二個條件WHERE子句中:

SELECT 
    a.monthgroup, 
    b.*, 
    c.* 
FROM 
(
    SELECT MONTH(FROM_UNIXTIME(modified_time)) AS monthgroup 
    FROM jb_resumes 
    WHERE modified_time BETWEEN 
     UNIX_TIMESTAMP('2012-03-01 00:00:00') 
     AND UNIX_TIMESTAMP('2012-04-30 23:59:59') 
    GROUP BY monthgroup 
) a 
CROSS JOIN 
    jb_users b 
LEFT JOIN 
    jb_resumes c ON 
     b.id = c.user_id 
     AND a.monthgroup = MONTH(FROM_UNIXTIME(modified_time)) 
WHERE 
    b.signup_time < UNIX_TIMESTAMP('2012-04-30 23:59:59') 
    AND c.user_id IS NULL 
-- 
ORDER BY a.monthgroup, b.id #for clarity's sake 

,導致:

Step 5

最後,我們可以組上monthgroup現場放入我們的COUNT()GROUP_CONCAT()功能:

SELECT 
    COUNT(*) AS cnt, 
    GROUP_CONCAT(b.id ORDER BY b.id) AS user_ids, 
    a.monthgroup 

FROM 
(
    SELECT MONTH(FROM_UNIXTIME(modified_time)) AS monthgroup 
    FROM jb_resumes 
    WHERE modified_time BETWEEN 
     UNIX_TIMESTAMP('2012-03-01 00:00:00') 
     AND UNIX_TIMESTAMP('2012-04-30 23:59:59') 
    GROUP BY monthgroup 
) a 
CROSS JOIN 
    jb_users b 
LEFT JOIN 
    jb_resumes c ON 
     b.id = c.user_id 
     AND a.monthgroup = MONTH(FROM_UNIXTIME(modified_time)) 
WHERE 
    b.signup_time < UNIX_TIMESTAMP('2012-04-30 23:59:59') 
    AND c.user_id IS NULL 
GROUP BY 
    a.monthgroup 
ORDER BY 
    a.monthgroup 

給我們期望的結果:

Result Set

+0

到目前爲止,它的外觀和作品**非常好**!如果你能解釋這個問題,甚至可以簡單地解釋這個問題,那就不僅僅是完美的了。如果沒有,請讓我知道,我會接受你的答案。 – Pateman

+0

發佈日分組解決方案。稍後我會再次編輯該答案,以便對中間結果集進行詳細解釋和分解。 –

+0

我能弄清楚如何避免使用'NOT EXISTS'相關的子查詢,並用'LEFT JOIN'替換它 - 編輯解決方案。 –

0

試試這個:

SELECT count(u.id) FROM `jb_users` u WHERE 
     u.id NOT IN (
      SELECT distinct r.user_id FROM `jb_resumes` r 
      WHERE (r.modified_time BETWEEN 1330581600 AND 1335848399) 
) AND u.signup_time >= 1330581600 GROUP BY FROM_UNIXTIME(u.signup_time) ORDER BY u.signup_time 

FROM_UNIXTIME將返回Unix時間戳爲日期格式。

它會按日期返回特定時間範圍內的總用戶數。您可以根據您的要求轉換日期格式。

我加了DISTINCT關鍵字在內部選擇查詢中,因爲一個用戶可以更新一次以上的簡歷,否則你可以得到那個甚至不在該日期範圍之間的記錄。

+0

謝謝,Nishu,但那個查詢應該如何按日期分組? – Pateman

+0

我認爲他的問題實際上是關於如何對外部查詢進行分組。順便說一下,Pateman,我認爲在子查詢中'order by'需要被刪除 - 這是沒有用的,可能會減慢查詢速度。 – ametren

+0

@ametren,是的,你是對的。我只是在試驗,忘了扔掉。 – Pateman

0

不確定這是否可行,但您可以嘗試與if進行連接。

SELECT DISTINCT 
if(r.modified_time NOT BETWEEN 1330581600 AND 1335848399, u.id, null) as UID 
FROM `jb_users` u 
Left Join `jb_resumes` r ON u.id = r.user_id 
WHERE 
u.signup_time >= 1330581600 
+0

@SuperMykEI,請看看我期待的輸出。 – Pateman

相關問題