Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Wie man FIND_IN_SET mit einer Datenliste verwendet

Erwägen Sie zunächst, die Daten normalisiert zu speichern. Hier ist eine gute Lektüre:Ist das Speichern einer Liste mit Trennzeichen in einer Datenbankspalte wirklich so schlecht?

Jetzt - Unter der Annahme des folgenden Schemas und der folgenden Daten:

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

Wir möchten Produkte mit Variationen '12345' finden und '34567' . Das erwartete Ergebnis ist die 1. und 2. Zeile.

Normalisiertes Schema - Viele-zu-Viele-Beziehung

Anstatt die Werte in einer durch Kommas getrennten Liste zu speichern, erstellen Sie eine neue Tabelle, die Produkt-IDs Variationen zuordnet:

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

Die Auswahlabfrage wä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');

Wie Sie sehen - Mit einem normalisierten Schema kann das Problem mit einer recht einfachen Abfrage gelöst werden. Und wir können Indizes effektiv nutzen.

"Verwertung" eines VOLLTEXTINDEX

Mit einem VOLLTEXTINDEX auf (upc_variation) Sie können verwenden:

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

Das sieht ziemlich "hübsch" aus und ist wahrscheinlich effizient. Aber obwohl es für dieses Beispiel funktioniert, würde ich mich mit dieser Lösung nicht wohl fühlen, weil ich nicht genau sagen kann, wann es nicht funktioniert.

Mit JSON_OVERLAPS()

Seit MySQL 8.0.17 können Sie JSON_OVERLAPS() . Sie sollten die Werte entweder als JSON-Array speichern oder die Liste „on the fly“ in JSON konvertieren:

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

Hierfür kann kein Index verwendet werden. Aber beides nicht für FIND_IN_SET() .

Mit JSON_TABLE()

Seit MySQL 8.0.4 können Sie JSON_TABLE() verwenden um eine normalisierte Darstellung der Daten "on the fly" zu erzeugen. Auch hier würden Sie entweder die Daten in einem JSON-Array speichern oder die Liste in der Abfrage in JSON konvertieren:

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

Hier kann kein Index verwendet werden. Und dies ist wahrscheinlich die langsamste Lösung von allen in dieser Antwort vorgestellten.

RLIKE / REGEXP

Sie können auch einen regulärer Ausdruck verwenden :

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

Siehe Demo aller Abfragen auf dbfiddle.uk