Списки, разделенные запятыми, являются проблемным антипаттерном.(Билл Карвин дает отличную презентацию в своей книге SQL AntiPatterns: избегая ловушек программирования баз данных, поэтому я не собираюсь углубляться в обсуждение того, почему мы хотим этого избежать. Вместо этого я собираюсь перейти к провереннойшаблон, обычно используемый для реализации многозначных атрибутов.)
Нормативный реляционный шаблон будет создавать таблицу с одним атрибутом в каждой строке.Например, что-то вроде этого:
CREATE TABLE dog_attribute
( dog_id int not null comment 'fk ref dog.id'
, attr int not null comment '1=adoptable, 2=...'
, PRIMARY KEY (dog_id, attr)
, CONSTRAINT FK_dog_attribute_dog FOREIGN KEY dog_id REFERENCES dog (id)
ON UPDATE CASCADE ON DELETE CASCADE
, UNIQUE KEY dog_attribute_UX (attr, dog_id)
);
-
Чтобы добавить три атрибута для данного dog_id, мы добавим три строки:
INSERT INTO dog_attibute (dog_id, attr) VALUES
(1,2)
,(1,3)
,(1,5)
-
Существует несколько возможных шаблонов запросов.В качестве нескольких быстрых примеров:
Чтобы найти dog_id, который имеет и атрибут 2, и атрибут 3, мы могли бы сделать это:
SELECT da.dog_id
FROM dog_attribute da
WHERE da.attr IN (2,3)
GROUP
BY da.dog_id
HAVING COUNT(1) = 2
Существуют другие шаблоны запросов, которые дают эквивалентный результат, например
SELECT da.dog_id
FROM dog_attribute da
JOIN dog_attribute dd
ON dd.dog_id = da.dog_id
AND dd.attr = 3
WHERE da.attr = 2
Первый шаблон запроса может быть расширен, чтобы найти dog_id, который имеет как минимум два из данного набора атрибутов.например,
WHERE da.attr IN (2,3,5,7)
HAVING COUNT(1) >= 2
Второй запрос может быть расширен, чтобы добавить объединения и анти-объединения для отдельных атрибутов,
-
Чтобы найти строки, имеющие атрибут 2, иимеют один (или оба) атрибут 3 и 4 и не имеют ни атрибута 7, ни 8, мы могли бы сделать что-то вроде этого:
SELECT da.dog_id
FROM dog_attribute da
LEFT
JOIN dog_attribute dn
ON dn.dog_id = da.dog_id
AND dn.attr IN (7,8)
WHERE dn.dog_id IS NULL
AND da.attr = 2
AND EXISTS ( SELECT 1
FROM dog_attribute de
WHERE de.dog_id = da.dog_id
AND de.attr IN (3,4)
)
(против объединения, чтобы исключить dog_id с атрибутом 7, ...)
Если бы я не смог реализовать шаблон таблицы, как показано выше, ... если мне абсолютно необходимо было использовать разделенные запятыми списки, чтобы хранить несколько атрибутов в одном столбце ... для небольшого размера,статический набор атрибутов (определенный в определении таблицы, динамически не добавляемый или удаляемый). Я бы использовал тип данных MySQL SET
.
https://dev.mysql.com/doc/refman/8.0/en/set.html
attributes SET('','adoptable','short hair','long hair','fluffy','black','brown','white','golden')
и для запроса этого мы могли бы сделать:
WHERE FIND_IN_SET('black',t.attributes)
AND FIND_IN_SET('long hair',t.attributes)