2013-10-27 30 views
0

我有3個表:左連接子選擇與限制MySQL的

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_CONCAT'd標籤標題爲基努·裏維斯結果:

comedy, scifi, action, suspense, western (西方和紀錄片有2 clip_count,但western應該是第一位的,因爲它有一個後update_timestamp

我我不確定這是否具有任何相關性,但是我正在執行actors表上的其他連接,但是在此問題中刪除了它們。 這將是非常可取的,使這全1查詢,但我很難在如何做到這一點,即使有2個查詢。 1或2查詢解決方案表示讚賞。

+0

1查詢少做?做更多?? .. http://sqlfiddle.com/#!2/279a21/10你將殺死MySQL性能與這個查詢在有很多記錄的大型表上。創建tmp表和複製到tmp表需要最長的時間時間... –

+0

問題是你可以像這樣使用GROUP_CONCAT進行ORDER BY。 GROUP_CONCAT(t.title ORDER BY t.title),但只有列出的記錄.. –

+0

@RaymondNijland我欣賞表現免責聲明,但我對可能性/解決方案因素感興趣。我打開2個查詢,但我會等待看看別人有什麼要說的。 – danronmoon

回答

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` 
+0

+1這很好,這永遠不會跨越我的腦海好方法這將保持性能見http://sqlfiddle.com/#!2/279a21/62(只有表(演員)需要一個完整的索引掃描)..確實http://dev.mysql.com/doc/ refman/5.7/en/group-by-functions.html#function_group-concat您可以通過GROUP_CONCAT中的多個列進行排序,您可能還想執行此查詢SET [GLOBAL | SESSION] group_concat_max_len = val;解釋在文檔中 –

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