1

タイトルからして似たような質問がすでに何十件も出ているように聞こえるかもしれませんが、これは少し違うと思います。それでも、私と同様の質問が既にある場合は、それを教えてください。

基本的に、私は 2 つのテーブルを持っています: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)

2012 年 3 月 1 日 00:00:00 ~ 2012 年 4 月 30 日 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 で処理するので、1 つのクエリ (サブクエリを使用しても) でそれができない場合は、PHP でデータを処理してもかまいません。

ありがとうございました。

4

3 に答える 3

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

結果セット

少しぎこちないので、もっと洗練された解決策を思い付くことができるかどうか見ていきます。

日のグループ化の解決策:

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のは、時間枠内のすべてのから月のグループを抽出することです。

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

その結果:

ステップ1

次に、monthgroup各ユーザーの組み合わせを比較して、 内で変更時間がないユーザーを特定するには、とすべてのユーザーmonthgroupの間でデカルト積を作成する必要があります。monthgroup上記のクエリはすでに を使用しているためGROUP BY、そのクエリに直接結合することはできませんが、代わりに副選択でラップしてFROM句に入れる必要があります。

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

その結果:

ステップ2

monthgroupこれで、とすべての の組み合わせができましたが、時間範囲より遅いidユーザーを含めたくないため、句signup_timeに最初の条件を導入してそれらを除外します。WHERE

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

その結果:

ステップ 3

通知id 1は除外されました。これで、次の方法で比較できます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

その結果:

ステップ 4

ここでは、ユーザーが履歴書の変更を行い、変更が値の月内に発生したLEFT JOINという条件で ing しています。ユーザーがその月に履歴書を変更していない場合、はテーブルの値を返します。条件が満たされないユーザーが欲しいので、2 番目の条件を句に入れる必要があります。jb_resumes monthgroupLEFT JOINNULLWHERE

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

その結果:

ステップ 5

最後に、monthgroupフィールドでグループ化し、 COUNT()andGROUP_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

望ましい結果が得られます。

結果セット

于 2012-06-20T19:56:58.040 に答える
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キーワードを追加しました。これは、1 人のユーザーが複数回履歴書を更新できるためです。それ以外の場合は、その日付範囲に収まらないレコードも取得できます。

于 2012-06-20T18:42:33.907 に答える
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
于 2012-06-20T19:01:13.673 に答える