sql >> Databasteknik >  >> RDS >> Mysql

Hur man använder FIND_IN_SET med hjälp av datalistan

Tänk först och främst på att lagra data på ett normaliserat sätt. Här är en bra läsning:Är det verkligen så dåligt att lagra en avgränsad lista i en databaskolumn?

Nu - Antag följande schema och data:

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');

Vi vill hitta produkter med varianter '12345' och '34567' . Det förväntade resultatet är 1:a och 2:a raden.

Normaliserat schema - många-till-många-relation

Istället för att lagra värdena i en kommaseparerad lista skapar du en ny tabell som mappar produkt-ID:n med varianter:

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');

Väljfrågan skulle vara:

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

Som du ser - Med ett normaliserat schema kan problemet lösas med en ganska grundläggande fråga. Och vi kan effektivt använda index.

"Utnyttja" ett FULLTEXT INDEX

Med ett FULLTEXT INDEX på (upc_variation) du kan använda:

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

Detta ser ganska "snyggt" ut och är förmodligen effektivt. Men även om det fungerar för det här exemplet, skulle jag inte känna mig bekväm med den här lösningen, eftersom jag inte kan säga exakt när den inte fungerar.

Med JSON_OVERLAPS()

Sedan MySQL 8.0.17 kan du använda JSON_OVERLAPS() . Du bör antingen lagra värdena som en JSON-array eller konvertera listan till JSON "on the fly":

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

Inget index kan användas för detta. Men det kan inte heller för FIND_IN_SET() .

Använder JSON_TABLE()

Sedan MySQL 8.0.4 kan du använda JSON_TABLE() att generera en normaliserad representation av data "i farten". Även här skulle du antingen lagra data i en JSON-array eller konvertera listan till JSON i frågan:

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');

Inget index kan användas här. Och detta är förmodligen den långsammaste lösningen av alla som presenteras i detta svar.

RLIKA / REGEXP

Du kan också använda ett reguljärt uttryck :

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

Se demo av alla frågor på dbfiddle.uk



  1. Laravel Global Settings Model

  2. SQL-fel Jag kan inte lista ut detta

  3. MariaDB JSON_MERGE_PRESERVE() Förklarad

  4. PHP:Hur man ökar ett värde i tabellraden för att räkna visningar och för att begränsa antalet till en IP-adress