Mysql
 sql >> Database >  >> RDS >> Mysql

Come utilizzare FIND_IN_SET utilizzando l'elenco di dati

Innanzitutto considera di archiviare i dati in modo normalizzato. Ecco una buona lettura:La memorizzazione di un elenco delimitato in una colonna del database è davvero così male?

Ora - Assumendo lo schema e i dati seguenti:

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

Vogliamo trovare prodotti con varianti '12345' e '34567' . Il risultato atteso è la prima e la seconda riga.

Schema normalizzato - relazione molti-a-molti

Invece di memorizzare i valori in un elenco separato da virgole, crea una nuova tabella, che mappa gli ID prodotto con le variazioni:

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

La query selezionata sarebbe:

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

Come vedi - Con uno schema normalizzato il problema può essere risolto con una query abbastanza semplice. E possiamo utilizzare efficacemente gli indici.

"Sfruttare" un INDICE FULLTEXT

Con un FULLTEXT INDEX su (upc_variation) puoi usare:

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

Questo sembra abbastanza "carino" ed è probabilmente efficiente. Ma sebbene funzioni per questo esempio, non mi sentirei a mio agio con questa soluzione, perché non posso dire esattamente quando non funziona.

Utilizzo di JSON_OVERLAPS()

Da MySQL 8.0.17 puoi usare JSON_OVERLAPS() . Dovresti archiviare i valori come un array JSON o convertire l'elenco in JSON "al volo":

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

Nessun indice può essere utilizzato per questo. Ma nemmeno per FIND_IN_SET() .

Utilizzo di JSON_TABLE()

Da MySQL 8.0.4 puoi usare JSON_TABLE() generare una rappresentazione normalizzata dei dati "al volo". Anche in questo caso dovresti archiviare i dati in un array JSON o convertire l'elenco in JSON nella query:

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

Nessun indice può essere utilizzato qui. E questa è probabilmente la soluzione più lenta di tutte presentate in questa risposta.

MI PIACE / REGEXP

Puoi anche utilizzare un'espressione regolare :

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

Vedi demo di tutte le query su dbfiddle.uk