Mysql
 sql >> база данни >  >> RDS >> Mysql

Как да използвате FIND_IN_SET с помощта на списък с данни

На първо място помислете за съхраняване на данните по нормализиран начин. Ето едно добро четиво:Толкова ли лошо ли е съхраняването на разделен списък в колона на база данни?

Сега - Приемаме следната схема и данни:

create table products (
  id int auto_increment,
  upc varchar(50),
  upc_variation text,
  primary key (id),
  index (upc)
);
insert into products (upc, upc_variation) values
  ('01234', '01234,12345,23456'),
  ('56789', '45678,34567'),
  ('056789', '045678,034567');

Искаме да намерим продукти с вариации '12345' и '34567' . Очакваният резултат е 1-ви и 2-ри ред.

Нормализирана схема – релация много към много

Вместо да съхранявате стойностите в списък, разделен със запетая, създайте нова таблица, която съпоставя идентификаторите на продукти с вариации:

create table products_upc_variations (
  product_id int,
  upc_variation varchar(50),
  primary key (product_id, upc_variation),
  index  (upc_variation, product_id)
);
insert into products_upc_variations (product_id, upc_variation) values 
  (1, '01234'),
  (1, '12345'),
  (1, '23456'),
  (2, '45678'),
  (2, '34567'),
  (3, '045678'),
  (3, '034567');

Заявката за избор ще бъде:

select distinct p.*
from products p
join products_upc_variations v on v.product_id = p.id
where v.upc_variation in ('12345', '34567');

Както виждате - С нормализирана схема проблемът може да бъде решен с доста елементарна заявка. И можем ефективно да използваме индекси.

„Експлоатация“ на ПЪЛТЕКСТОВ ИНДЕКС

С ПЪЛЕН ТЕКСТОВ ИНДЕКС на (upc_variation) можете да използвате:

select p.*
from products p
where match (upc_variation) against ('12345 34567');

Това изглежда доста "красиво" и вероятно е ефективно. Но въпреки че работи за този пример, не бих се чувствал комфортно с това решение, защото не мога да кажа точно кога не работи.

Използване на JSON_OVERLAPS()

От MySQL 8.0.17 можете да използвате JSON_OVERLAPS() . Трябва или да съхранявате стойностите като JSON масив, или да конвертирате списъка в JSON "в движение":

select p.*
from products p
where json_overlaps(
  '["12345","34567"]',
  concat('["', replace(upc_variation, ',', '","'), '"]')
);

За това не може да се използва индекс. Но нито за FIND_IN_SET() .

Използване на JSON_TABLE()

От MySQL 8.0.4 можете да използвате JSON_TABLE() за генериране на нормализирано представяне на данните "в движение". Тук отново бихте съхранили данните в JSON масив, или ще преобразувате списъка в JSON в заявката:

select distinct p.*
from products p
join json_table(
  concat('["', replace(p.upc_variation, ',', '","'), '"]'),
  '$[*]' columns (upcv text path '$')
) v
where v.upcv in ('12345', '34567');

Тук не може да се използва индекс. И това е може би най-бавното решение от всички представени в този отговор.

RLIKE / REGEXP

Можете също да използвате регулярен израз :

select p.*
from products p
where p.upc_variation rlike '(^|,)(12345|34567)(,|$)'

Вижте демо на всички заявки в dbfiddle.uk



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. използвайте КЛАУЗА WHERE за данни за търсене от дата А до дата Б

  2. Запитване на няколко бази данни наведнъж

  3. Получаване на синтаксиса на подготвените изрази

  4. Автоматично актуализиране на изгледа в MySql

  5. Грешка в MySQL:стойност извън диапазона за колона „сума“ на ред 1