Какой тип данных для нескольких ссылок? - PullRequest
0 голосов
/ 11 февраля 2019

Мне нужен самый быстрый способ получения целых чисел, которые нужно быстро вытащить.Вот мой пример использования: на моем сайте есть опция «сортировать / фильтровать».У меня есть атрибуты для собак, такие как длинные волосы, короткие волосы, большие волосы, собаки и т. Д. Я хочу, чтобы пользователи могли фильтровать по атрибутам собак.

Вот моя таблица:

dogs
- id
- attributes

Вот ссылка на атрибуты о собаках (это не очень важно для вопроса, просто чтобы дать вам представление):

0 = adoptable pet
1 = short hair
2 = long hair
3 = fluffy
4 = black
5 = brown
6 = white
7 = golden/yellow
8 = etc...

Сейчас у меня есть атрибуты, сохраненные вTEXT тип данных в формате JSON.Например, ["0","4"] или ["0"] или ["2"].

Поэтому я хочу выбрать все атрибуты следующим образом:

SELECT * FROM dogs WHERE attributes LIKE %0% OR attributes LIKE %1% OR attributes LIKE %4% attributes LIKE %7%

Как выбрать все строки, где находятся атрибутысодержать 0, или 1, или 4, или 7 (не а).Если в строке есть один из этих атрибутов, выберите их.

Как лучше всего подойти к нему?Должен ли я хранить как тип данных JSON в MySQL или я должен делать что-то еще?Я хочу быть в состоянии SELECT и подтянуть его БЫСТРО.Я чувствую, что то, что у меня есть, LIKE не будет очень быстрым.

Какой самый лучший и быстрый способ, особенно с сотнями тысяч строк до миллионов?

Спасибозаранее!

1 Ответ

0 голосов
/ 12 февраля 2019

Списки, разделенные запятыми, являются проблемным антипаттерном.(Билл Карвин дает отличную презентацию в своей книге 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...