0

我有 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 个标签的池中获取。

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

comedy, scifi, action, suspense, western (西部片和纪录片都有一个clip_count2,但western应该先出现,因为它有一个后面update_timestamp

我不确定这是否有任何相关性,但我正在执行 actor 表上的其他连接,但为了这个问题将它们删除。最好将这全部进行 1 个查询,但是即使使用 2 个查询,我也很难做到这一点。1 或 2 查询解决方案表示赞赏。

4

2 回答 2

1

SQLFiddle,借助关于使用 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`
于 2013-10-27T14:00:10.273 回答
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
于 2013-10-27T12:40:11.427 回答