У меня такая таблица:

id_type         id_option
"1"         "1"
"1"         "5"
"2"         "1"
"2"         "5"
"2"         "8"

Я пытаюсь написать запрос, который по списку идентификаторов опций находит «тип», который соответствует списку, но только те идентификаторы

Например, если заданы 1 и 5 в качестве параметров, он должен возвращать тип 1, но только тип 1, поскольку 8, необходимые для соответствия типу 2, отсутствуют.

Я пробовал следующее:

SELECT *
FROM my_table
WHERE id_option IN (1, 5)
GROUP BY id_type
HAVING COUNT(DISTINCT id_option) = 2

Это возвращает оба "типа" - я надеялся, что ограничение COUNT, равное 2, помогло бы, но теперь я понимаю, почему это не так, но я не могу придумать умного способа ограничить это.

Я мог бы просто вытащить первую запись, так как обычно сначала сохраняются типы с меньшим количеством параметров, но я не думаю, что могу полагаться на это на 100%.

Спасибо за ваше время

0
Aphire 4 Июл 2021 в 21:53

3 ответа

Лучший ответ

Вот решение:

SELECT *
FROM my_table
GROUP BY id_type
HAVING SUM(id_option IN (1,5)) = COUNT(*)

Он основан на уловке, характерной для MySQL: boolean true - это буквально целое число 1. Таким образом, вы можете использовать SUM () для подсчета строк, в которых условие истинно, но поместив логическое выражение внутри SUM ().

Для тех, кто читает это и использует другие базы данных помимо MySQL, вам придется использовать выражение для преобразования логического условия в целое число 1:

HAVING SUM(CASE WHEN id_option IN (1,5) THEN 1 ELSE 0 END) = COUNT(*)

В этом случае пусть все строки станут частью групп. То есть не используйте предложение WHERE для ограничения запроса строками, где id_option равно 1 или 5. Затем подсчитайте общее количество строк в группе и «подсчитайте» (т. Е. Используйте трюк SUM ()) строки, в которых id_options равно 1 или 5. Сравнение этих счетчиков будет равно, если нет значений id_options, кроме 1 или 5.

Если вы также хотите убедиться, что найдены и 1, и 5, вам нужно другое условие:

SELECT *
FROM my_table
GROUP BY id_type
HAVING SUM(id_option IN (1,5)) = COUNT(*)
  AND COUNT(DISTINCT CASE WHEN id_option IN (1,5) THEN id_option END) = 2

Выражение CASE вернет 1 или 5, или, если есть другие значения, они преобразуются в NULL. Функция COUNT () игнорирует NULL.

3
Bill Karwin 4 Июл 2021 в 19:39

Если вы можете передать параметры в виде отсортированной строки списка, разделенной запятыми, используйте GROUP_CONCAT():

SELECT id_type
FROM my_table
GROUP BY id_type
HAVING GROUP_CONCAT(id_option ORDER BY id_option) = '1,5'

Если есть повторяющиеся параметры для каждого типа, используйте DISTINCT:

HAVING GROUP_CONCAT(DISTINCT id_option ORDER BY id_option) = '1,5'
3
forpas 4 Июл 2021 в 18:59

Пока я не могу комментировать, вот небольшая поправка к последнему примеру Билла Карвина (в принятом решении):

SELECT *
  FROM my_table
 GROUP BY id_type
HAVING SUM(id_option IN (1,5)) = COUNT(*)
   AND COUNT(DISTINCT id_option) = 2
0
Jon Armstrong 4 Июл 2021 в 21:28