Выбор только 3 первых соединенных элементов с атрибутом uniq в каждой группе, сгруппированной по группам - PullRequest
2 голосов
/ 26 января 2012

У меня есть простой sql:

SELECT foos.*, bars.* FROM foos 
  LEFT JOIN bars ON bars.foo_id = foos.id
  WHERE foos.id = 1;

=>

+------------------------------------+
| foos.id | bars.id | bars.author_id |
+------------------------------------+
|    1    |    1    |       10       |
|    1    |    3    |       10       |
|    1    |    5    |       3        |
|    1    |    6    |       10       |
|    1    |    7    |       10       |
|    1    |    8    |       10       |
|    1    |    44   |       11       |
|    1    |    32   |       10       |
+------------------------------------+

Теперь мне нужно вернуть не все соединенные bars, а только первые три (срез) для каждого bars.author_id, чтобы он мог эффективно вернуть что-то подобное

+------------------------------------+
| foos.id | bars.id | bars.author_id |
+------------------------------------+
|    1    |    1    |       10       |
|    1    |    3    |       10       |
|    1    |    5    |       3        |
|    1    |    6    |       10       |
|    1    |    44   |       11       |
+------------------------------------+

Ответы [ 3 ]

1 голос
/ 26 января 2012

У меня есть хорошее решение для этого:

For, selecting 1st 3 record within each group 
1) sorting the results asc/desc by applying `order by bars.id` within group_concat()
2) limiting the records by passing the 3rd parameter as the records to limit to SUBSTRING_INDEX(str,'match str','no of records to limit') 

SELECT foos.id, 
       SUBSTRING_INDEX(GROUP_CONCAT(bars.id
                       order by bars.id),',',3),
       bars.author_id

FROM foos LEFT JOIN bars ON bars.foo_id = foos.id

WHERE foos.id = 1
GROUP BY bars.author_id

Результат будет:

+------------------------------------+
| foos.id | bars.id | bars.author_id |
+------------------------------------+
|    1    |    1,3,6    |       10   |
|    1    |    5        |       3    |
|    1    |    44       |       11   |
+------------------------------------+

Позже, на стороне приложения вы можете взорвать его с помощью ',' и использовать его.

1 голос
/ 26 января 2012

Вы можете попробовать:

SELECT f.*, b.* 
FROM foos f
LEFT JOIN (select b1.*
           from bars b1
           where 3 < (select count(*)
                      from bars bn 
                      where bn.foo_id = b1.foo_id and
                            bn.author_id = b1.author_id and
                            bn.id < b1.id)
           ) b
       ON b.foo_id = f.id
WHERE f.id = 1;
0 голосов
/ 30 января 2012
SELECT 
      foos.*
    , bars.* 
FROM 
      foos 
  LEFT JOIN 
      bars 
    ON  bars.foo_id = foos.id
    AND bars.author_id <= COALESCE(
       ( SELECT b.author_id
         FROM bars b
         WHERE b.foo_id = foos.id
         ORDER BY b.author_id ASC
         LIMIT 0 OFFSET 2            --- one less than 3
       ), 2147483647              )
WHERE 
      foos.id = 1

Индекс на bars( foo_id, author_id ) поможет, если он медленный.

...