Przede wszystkim rozważ przechowywanie danych w znormalizowany sposób. Oto dobra lektura:Czy przechowywanie rozdzielanej listy w kolumnie bazy danych jest naprawdę takie złe?
Teraz — zakładając następujący schemat i dane:
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');
Chcemy znaleźć produkty z odmianami '12345'
i '34567'
. Oczekiwany wynik to pierwszy i drugi wiersz.
Schemat znormalizowany — relacja wiele-do-wielu
Zamiast przechowywać wartości na liście oddzielonej przecinkami, utwórz nową tabelę, która mapuje identyfikatory produktów z odmianami:
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');
Zapytanie wybierające to:
select distinct p.*
from products p
join products_upc_variations v on v.product_id = p.id
where v.upc_variation in ('12345', '34567');
Jak widać - przy znormalizowanym schemacie problem można rozwiązać za pomocą dość podstawowego zapytania. I możemy efektywnie korzystać z indeksów.
„Wykorzystywanie” indeksu PEŁNOTEKSTOWEGO
Z INDEKSEM PEŁNOTEKSTOWYM na (upc_variation)
możesz użyć:
select p.*
from products p
where match (upc_variation) against ('12345 34567');
Wygląda to całkiem „ładnie” i prawdopodobnie jest wydajne. Ale chociaż to działa w tym przykładzie, nie czułbym się komfortowo z tym rozwiązaniem, ponieważ nie mogę dokładnie powiedzieć, kiedy to nie działa.
Korzystanie z JSON_OVERLAPS()
Od wersji MySQL 8.0.17 możesz używać JSON_OVERLAPS() . Powinieneś albo przechowywać wartości jako tablicę JSON, albo przekonwertować listę na JSON „w locie”:
select p.*
from products p
where json_overlaps(
'["12345","34567"]',
concat('["', replace(upc_variation, ',', '","'), '"]')
);
Żaden indeks nie może być do tego użyty. Ale nie może też dla FIND_IN_SET()
.
Korzystanie z JSON_TABLE()
Od MySQL 8.0.4 możesz używać JSON_TABLE() do generowania znormalizowanej reprezentacji danych „w locie”. Tutaj ponownie albo przechowasz dane w tablicy JSON, albo przekonwertujesz listę na JSON w zapytaniu:
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');
Nie można tutaj użyć indeksu. I jest to prawdopodobnie najwolniejsze rozwiązanie ze wszystkich przedstawionych w tej odpowiedzi.
RLIKE / REGEXP
Możesz także użyć wyrażenia regularnego :
select p.*
from products p
where p.upc_variation rlike '(^|,)(12345|34567)(,|$)'
Zobacz demo wszystkich zapytań na dbfiddle.uk