Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Como usar FIND_IN_SET usando lista de dados


Em primeiro lugar, considere armazenar os dados de maneira normalizada. Aqui está uma boa leitura:Armazenar uma lista delimitada em uma coluna de banco de dados é tão ruim assim?

Agora - Assumindo o seguinte esquema e dados:
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');

Queremos encontrar produtos com variações '12345' e '34567' . O resultado esperado é a 1ª e a 2ª linhas.

Esquema normalizado - relação muitos-para-muitos


Em vez de armazenar os valores em uma lista separada por vírgulas, crie uma nova tabela, que mapeia IDs de produtos com variações:
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');

A consulta de seleção seria:
select distinct p.*
from products p
join products_upc_variations v on v.product_id = p.id
where v.upc_variation in ('12345', '34567');

Como você vê - Com um esquema normalizado, o problema pode ser resolvido com uma consulta bastante básica. E podemos efetivamente usar índices.

"Explorando" um ÍNDICE DE TEXTO COMPLETO


Com um FULLTEXT INDEX em (upc_variation) você pode usar:
select p.*
from products p
where match (upc_variation) against ('12345 34567');

Isso parece bastante "bonito" e provavelmente é eficiente. Mas embora funcione para este exemplo, não me sentiria confortável com esta solução, porque não posso dizer exatamente quando não funciona.

Usando JSON_OVERLAPS()


Desde o MySQL 8.0.17 você pode usar JSON_OVERLAPS() . Você deve armazenar os valores como uma matriz JSON ou converter a lista em JSON "on the fly":
select p.*
from products p
where json_overlaps(
  '["12345","34567"]',
  concat('["', replace(upc_variation, ',', '","'), '"]')
);

Nenhum índice pode ser usado para isso. Mas também não pode para FIND_IN_SET() .

Usando JSON_TABLE()


Desde o MySQL 8.0.4 você pode usar JSON_TABLE() para gerar uma representação normalizada dos dados "on the fly". Aqui, novamente, você armazenaria os dados em uma matriz JSON ou converteria a lista em JSON na consulta:
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');

Nenhum índice pode ser usado aqui. E esta é provavelmente a solução mais lenta de todas apresentadas nesta resposta.

RLIKE / REGEXP


Você também pode usar uma expressão regular :
select p.*
from products p
where p.upc_variation rlike '(^|,)(12345|34567)(,|$)'

Veja demonstração de todas as consultas em dbfiddle.uk