У меня есть три таблицы в базе данных mysql:
| pieces | line_up | instrument |
--------------------------------------------
| id_piece | piece_id | id_instrument |
| title | instrument_id | instrument |
теперь то, что я пытаюсь достичь, это: я хотел бы запросить те части, чей line_up состоит именно из инструментов, указанных в списке, не меньше и не больше. Этот SQL-запрос уменьшает результат до тех произведений, которые исполняются только на двух инструментах, но включает соло.
SELECT id_piece, title FROM pieces WHERE
(SELECT COUNT(*) FROM line_up WHERE line_up.piece_id = pieces.id_piece)
=
(SELECT COUNT(*) FROM line_up
INNER JOIN instruments ON instruments.id_instrument = line_up.instrument_id
WHERE line_up.piece_id = pieces.id_piece
AND instruments.instrument IN ('guitar', 'drums'));
Например, с этими таблицами:
| pieces | | line_up | | instruments |
----------------------- --------------------------- ------------------------------
| id_piece | title | | piece_id | instrument_id | | id_instrument | instrument |
----------------------- ---------------------------- ------------------------------
| 1 | hello | | 1 | 1 | | 1 | guitar |
| 2 | goodbye | | 1 | 2 | | 2 | drums |
| 3 | goodnight | | 2 | 1 | ------------------------------
------------------------ | 3 | 2 |
----------------------------
единственная актуальная пьеса для гитары и ударных, поэтому результат моего запроса должен быть 1 | hello
.
Какие-либо предложения? Спасибо!
Вам нужно подсчитать количество строк, которые были отфильтрованы с инструментами, и сравнить его с общим количеством инструментов и количеством инструментов, которые вы выбрали.
with pieces as (
select 1 as id_piece, 'hello' as title union all
select 2 as id_piece, 'goodbye' union all
select 3 as id_piece, 'goodnight'
)
, line_up as (
select 1 as piece_id, 1 as instrument_id union all
select 1 as piece_id, 2 as instrument_id union all
select 2 as piece_id, 1 as instrument_id union all
select 3 as piece_id, 2 as instrument_id
)
, instruments as (
select 1 as id_instrument, 'guitar' as instrument union all
select 2 as id_instrument, 'drums' as instrument
)
select p.id_piece, p.title
from pieces as p
join line_up as l
on l.piece_id = p.id_piece
join instruments as i
on l.instrument_id = i.id_instrument
group by p.id_piece, p.title
having sum(case when i.instrument in ('guitar', 'drums') then 1 else 0 end) = count(1)
and count(1) = (
/*To select only valid instruments*/
select count(1)
from instruments as f
where f.instrument in ('guitar', 'drums')
)
-----------+--------
| id_piece | title |
|----------+-------|
| 1 | hello |
-----------+--------
Другой подход. Сопоставьте по комбинации инструментов.
SELECT title
FROM pieces
WHERE id_piece IN (
SELECT piece_id
FROM line_up
WHERE instrument_id IN (
SELECT id_instrument
FROM instruments
WHERE instrument IN ('Guitar', 'Drums')
)
GROUP BY piece_id
HAVING group_concat(instrument_id order by instrument_id separator ',') = (
SELECT group_concat(id_instrument order by id_instrument separator ',')
FROM instruments
WHERE instrument IN ('Guitar', 'Drums')
)
);
Вы можете использовать агрегацию следующим образом:
select p.id_piece, p.title
from pieces as p
inner join line_up as l on l.piece_id = p.id_piece
inner join instruments as i on l.instrument_id = i.id_instrument
group by p.id_piece
having sum(i.instrument in ('guitar', 'drums')) = 2
and sum(i.instrument not in ('guitar', 'drums')) = 0
Если у вас не слишком много инструментов для поиска, альтернативой является агрегация строк в предложении having
:
having group_concat(i.instrument order by i.instrument) = 'drums,guitar'
Это второе выражение требует, чтобы вы предоставили запросу упорядоченный по алфавиту список инструментов.
@astentx: хорошо, если этот список значений встроен в приложение, вы ожидаете, что приложение будет знать, сколько значений задано.
@GMB .. и это именно так. Большое большое спасибо!
сколько инструментов было бы «слишком много»? Я рассматриваю максимальное значение 30 инструментов
Должно быть что-то, что подсчитывает количество запрошенных элементов вместо 2