sql >> Databáze >  >> RDS >> Mysql

Jak používat FIND_IN_SET pomocí seznamu dat

Nejprve zvažte ukládání dat normalizovaným způsobem. Zde je dobré čtení:Je ukládání seznamu s oddělovači ve sloupci databáze opravdu tak špatné?

Nyní – Za předpokladu následujícího schématu a dat:

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

Chceme najít produkty s variacemi '12345' a '34567' . Očekávaným výsledkem je 1. a 2. řádek.

Normalizované schéma – vztah mnoho k mnoha

Místo ukládání hodnot do seznamu odděleného čárkami vytvořte novou tabulku, která mapuje ID produktů s variantami:

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ýběrový dotaz by byl:

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 vidíte - S normalizovaným schématem lze problém vyřešit zcela základním dotazem. A můžeme efektivně využívat indexy.

"Zneužívání" FULLTEXTOVÉHO INDEXU

S FULLTEXTOVÝM INDEXEM na (upc_variation) můžete použít:

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

To vypadá docela "hezky" a je pravděpodobně efektivní. Ale i když to v tomto příkladu funguje, necítil bych se s tímto řešením dobře, protože nedokážu přesně říct, kdy to nefunguje.

Pomocí JSON_OVERLAPS()

Od MySQL 8.0.17 můžete použít JSON_OVERLAPS() . Měli byste buď uložit hodnoty jako pole JSON, nebo převést seznam na JSON „za běhu“:

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

K tomu nelze použít žádný index. Ale ani to nemůže pro FIND_IN_SET() .

Pomocí JSON_TABLE()

Od MySQL 8.0.4 můžete použít JSON_TABLE() generovat normalizovanou reprezentaci dat "za běhu". Zde byste opět buď uložili data do pole JSON, nebo převedli seznam na JSON v dotazu:

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

Zde nelze použít žádný index. A toto je pravděpodobně nejpomalejší řešení ze všech prezentovaných v této odpovědi.

RLIKE / REGEXP

Můžete také použít regulární výraz :

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

Podívejte se na ukázku všech dotazů na dbfiddle.uk



  1. SQL Query pomalý v aplikaci .NET, ale okamžitý v SQL Server Management Studio

  2. chyba aktualizace záznamu v databázi

  3. Migrace z Postgres na SQL Server 2008

  4. SQL Server, nelze vložit hodnotu null do pole primárního klíče?