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

Sådan bruger du FIND_IN_SET ved hjælp af en liste over data

Overvej først og fremmest at gemme dataene på en normaliseret måde. Her er en god læsning:Er det virkelig så dårligt at gemme en afgrænset liste i en databasekolonne?

Nu - Forudsat følgende skema og 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 ønsker at finde produkter med variationer '12345' og '34567' . Det forventede resultat er 1. og 2. række.

Normaliseret skema - mange-til-mange-relation

I stedet for at gemme værdierne i en kommasepareret liste, skal du oprette en ny tabel, som kortlægger produkt-id'er med variationer:

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

Valgforespørgslen ville være:

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 kan se - Med et normaliseret skema kan problemet løses med en ganske grundlæggende forespørgsel. Og vi kan effektivt bruge indekser.

"Udnyttelse" af et FULLTEXT INDEX

Med et FULLTEXT INDEX på (upc_variation) du kan bruge:

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

Dette ser ret "pænt" ud og er sandsynligvis effektivt. Men selvom det virker for dette eksempel, ville jeg ikke føle mig tryg ved denne løsning, fordi jeg ikke kan sige præcist, hvornår den ikke virker.

Brug af JSON_OVERLAPS()

Siden MySQL 8.0.17 kan du bruge JSON_OVERLAPS() . Du bør enten gemme værdierne som et JSON-array eller konvertere listen til JSON "on the fly":

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

Intet indeks kan bruges til dette. Men det kan heller ikke for FIND_IN_SET() .

Brug af JSON_TABLE()

Siden MySQL 8.0.4 kan du bruge JSON_TABLE() at generere en normaliseret repræsentation af dataene "on the fly". Her igen vil du enten gemme dataene i et JSON-array eller konvertere listen til JSON i forespørgslen:

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

Intet indeks kan bruges her. Og dette er nok den langsomste løsning af alle præsenteret i dette svar.

RLIKE / REGEXP

Du kan også bruge et regulært udtryk :

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

Se demo af alle forespørgsler på dbfiddle.uk



  1. Oprettelse af en kopi af en database i PostgreSQL

  2. Anatomi af en softwareudviklingsrolle:Dataforsker

  3. POWER() Eksempler i SQL Server

  4. Hvorfor bliver MySQL InnoDB indsættelser / opdateringer på store tabeller meget langsomme, når der er nogle få indekser?