在MySQL中使用LIMIT左连接子选择

时间:2013-10-27 11:53:38

标签: mysql sql left-join limit

我有3张桌子:

actor

|    FIELD |             TYPE | NULL | KEY | DEFAULT |          EXTRA |
|----------|------------------|------|-----|---------|----------------|
| actor_id | int(10) unsigned |   NO | PRI |  (null) | auto_increment |
| username |      varchar(30) |   NO |     |  (null) |                |


tag
|  FIELD |             TYPE | NULL | KEY | DEFAULT |          EXTRA |
|--------|------------------|------|-----|---------|----------------|
| tag_id | int(10) unsigned |   NO | PRI |  (null) | auto_increment |
|  title |      varchar(40) |   NO |     |  (null) |                |

actor_tag_count
|            FIELD |             TYPE | NULL | KEY |           DEFAULT |                       EXTRA |
|------------------|------------------|------|-----|-------------------|-----------------------------|
|         actor_id | int(10) unsigned |   NO | PRI |            (null) |                             |
|           tag_id | int(10) unsigned |   NO | PRI |            (null) |                             |
|       clip_count | int(10) unsigned |   NO |     |            (null) |                             |
| update_timestamp |        timestamp |   NO |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |

SQLFiddle

我想为每个演员获得最常见的5个(最高clip_count)和最近更新的(最新update_timestamp)个标签。

我的尝试查询是:

SELECT
    `a`.`actor_id`,
    `a`.`username`,
    GROUP_CONCAT(atc.clip_count) AS `tag_clip_counts`,
    GROUP_CONCAT(t.tag_id) AS `tag_ids`,
    GROUP_CONCAT(t.title) AS `tag_titles`
FROM
    `actor` AS `a`
LEFT JOIN (
    SELECT
        `atc`.`actor_id`,
        `atc`.`tag_id`,
        `atc`.`clip_count`
    FROM
        `actor_tag_count` AS `atc`
    INNER JOIN `actor` AS `a` USING (actor_id)
    ORDER BY
        atc.clip_count DESC,
        atc.update_timestamp DESC
    LIMIT 5
) AS `atc` USING (actor_id)
LEFT JOIN `tag` AS `t` ON atc.tag_id = t.tag_id
GROUP BY
    `a`.`actor_id`

问题是左连接子选择仅计算一次,并且集合中每个结果的标记仅从5个标记的池中获取。

Keanu Reeves的预期GROUP_CONCAT'd标签标题结果:

comedy, scifi, action, suspense, western (西方和纪录片都有clip_count为2,但western应首先出现,因为它后来有update_timestamp

我不确定这是否与任何相关性有关,但我正在执行actor表上的其他联接,但是已针对此问题删除了这些联接。 这个全部1个查询都是非常可取的,但是即使有2个查询,我也不知道如何做到这一点。 1或2查询解决方案赞赏。

2 个答案:

答案 0 :(得分:1)

SQLFiddle,在一个非常好的answer的帮助下,关于使用GROUP_CONCAT限制解决方法:

SELECT
    `a`.`actor_id`,
    `a`.`username`,
    SUBSTRING_INDEX(GROUP_CONCAT(atc.clip_count ORDER BY atc.clip_count DESC, atc.update_timestamp DESC), ',', 5) AS `tag_clip_counts`,
    SUBSTRING_INDEX(GROUP_CONCAT(t.tag_id ORDER BY atc.clip_count DESC, atc.update_timestamp DESC), ',', 5) AS `tag_ids`,
    SUBSTRING_INDEX(GROUP_CONCAT(t.title ORDER BY atc.clip_count DESC, atc.update_timestamp DESC), ',', 5) AS `tag_titles`
FROM
    `actor` AS `a`
LEFT JOIN actor_tag_count AS `atc` USING (actor_id)
LEFT JOIN `tag` AS `t` ON atc.tag_id = t.tag_id
GROUP BY
    `a`.`actor_id`

答案 1 :(得分:0)

可以通过添加序列号来实现,但在大型表上可能效果不佳。

像这样(未经测试): -

    SELECT actor_id,
        username,
        GROUP_CONCAT(clip_count) AS tag_clip_counts,
        GROUP_CONCAT(tag_id) AS tag_ids,
        GROUP_CONCAT(title) AS tag_titles
    FROM
    (
    SELECT  actor.actor_id,
            actor.username,
            atc.clip_count, 
            tag.tag_id,
            tag.title,
            @aSeq := IF(@aActorId = actor.actor_id, @aSeq, 0) + a AS aSequence,
            @aActorId := actor.actor_id
    FROM
    (
        SELECT actor.actor_id,
            actor.username,
            atc.clip_count, 
            tag.tag_id,
            tag.title
        FROM actor
        LEFT JOIN actor_tag_count AS atc ON actor.actor_id = atc.actor_id
        LEFT JOIN tag ON atc.tag_id = tag.tag_id
        ORDER BY actor.actor_id, atc.clip_count DESC, atc.update_timestamp DESC
    )
    CROSS JOIN (SELECT @aSeq:=0, @aActorId:=0)
    )
    WHERE aSequence <= 5
    GROUP BY actor_id, username

另一种方法是在select语句中使用具有相关子查询的子选择(限制为5),然后使用外部查询来执行组连接。这样的事情(再次没有经过测试)

SELECT
    actor_id,
    username,
    GROUP_CONCAT(clip_count) AS tag_clip_counts,
    GROUP_CONCAT(tag_id) AS tag_ids,
    GROUP_CONCAT(title) AS tag_titles
FROM
(
SELECT
    a.actor_id,
    a.username,
    (
    SELECT
        atc.clip_count,
        t.tag_id,
        t.title
    FROM actor_tag_count AS atc ON a.actor_id = atc.actor_id
    LEFT JOIN tag t ON atc.tag_id = t.tag_id
    ORDER BY atc.clip_count DESC, atc.update_timestamp DESC
    LIMIT 5
)
FROM actor a
)
GROUP BY actor_id, username