Sql результат запроса «многие ко многим», только если соответствует только и точно всем элементам в массиве

У меня есть три таблицы в базе данных 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. Какие-либо предложения? Спасибо!

Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для
В последние годы архитектура микросервисов приобрела популярность как способ построения масштабируемых и гибких приложений. Laravel , популярный PHP...
Как построить CRUD-приложение в Laravel
Как построить CRUD-приложение в Laravel
Laravel - это популярный PHP-фреймворк, который позволяет быстро и легко создавать веб-приложения. Одной из наиболее распространенных задач в...
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
В предыдущем посте мы создали функциональность вставки и чтения для нашей динамической СУБД. В этом посте мы собираемся реализовать функции обновления...
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
Роли и разрешения пользователей без пакета Laravel 9
Роли и разрешения пользователей без пакета Laravel 9
Этот пост изначально был опубликован на techsolutionstuff.com .
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
1
0
123
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вам нужно подсчитать количество строк, которые были отфильтрованы с инструментами, и сравнить его с общим количеством инструментов и количеством инструментов, которые вы выбрали.

дб<>рабочий пример

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'

Это второе выражение требует, чтобы вы предоставили запросу упорядоченный по алфавиту список инструментов.

Должно быть что-то, что подсчитывает количество запрошенных элементов вместо 2

astentx 13.12.2020 23:00

@astentx: хорошо, если этот список значений встроен в приложение, вы ожидаете, что приложение будет знать, сколько значений задано.

GMB 13.12.2020 23:27

@GMB .. и это именно так. Большое большое спасибо!

ddgg 14.12.2020 00:19

сколько инструментов было бы «слишком много»? Я рассматриваю максимальное значение 30 инструментов

ddgg 14.12.2020 00:22

Другие вопросы по теме