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 处理数据。

谢谢你。

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一个时间范围内的所有 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

导致:

第1步

然后为了比较monthgroup每个用户的组合,找出哪些用户在 内没有修改时间,我们必须在和所有用户monthgroup之间做一个笛卡尔积。monthgroup由于上面的查询已经使用 a 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步

现在我们有了monthgroups 和 all ids 的组合,但是我们不想包括晚于时间范围的用户,所以我们通过在我们的子句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的条件是用户在其中进行了简历修改,jb_resumes 并且修改发生在该monthgroup值的月份内。如果用户在该月没有修改简历,则LEFT JOIN返回NULL表中的值。我们希望那些条件不满足的用户,因此我们必须将第二个条件放在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

导致:

第 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

给我们想要的结果:

结果集

于 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关键字,因为一个用户可以多次更新简历,否则您也可以获得甚至不在该日期范围之间的记录。

于 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 回答