RDBMS: MariaDB 10.3
Digamos que você tenha uma tabela que armazena informações específicas ( info_1
, info_2
, etc.) de acordo com um determinado par de nomes de usuário; igual a:
+----------+----------+----------+----------+----------+------+--..
| user_1 | user_2 | info_1 | info_2 | info_3 | .. | ..
+----------+----------+----------+----------+----------+------+--..
onde os valores de user_1
e user_2
cada igual a um nome de usuário específico e distinto ( VARCHAR(50)
), e são definidos como índice de várias colunas (user_1,user_2) da tabela na criação. As consultas que meu aplicativo faz pesquisa em função de:
A) um determinado par de usuários:
WHERE user_1 = name_1 AND user_2 = name_2 OR user_1 = name_2 AND user_2 = name_1
B) um determinado usuário/retorno todos os registros onde um determinado usuário é um dos membros:
WHERE user_1 = name_1 OR user_1 = '%' AND user_2 = name_1
Problema: Devo garantir que as pesquisas sejam sempre indexadas. Ao usar uma OR
cláusula, uma varredura completa da tabela é executada a cada vez.
SOLUÇÃO I)
Graças a esta resposta, as possíveis consultas que resultam em pesquisas indexadas para esses casos são:
A)
SELECT * FROM my_table WHERE user_1 = name_1 AND user_2 = name_2
UNION ALL
SELECT * FROM my_table WHERE user_1 = name_2 AND user_2 = name_1;
B)
SELECT * FROM my_table WHERE user_1 = name_1
UNION ALL
SELECT * FROM my_table WHERE user_1 = '%' AND user_2 = name_1;
Porém com a desvantagem de sempre usar duas SELECT
instruções e uma WHERE
cláusula que sempre será em vão (pois sempre haverá um registro por par de usuário). É por isso que tentei otimizar ainda mais a consulta usando um índice de texto completo , o que resultou na seguinte tabela:
SOLUÇÃO II)
+---------+----------+----------+----------+------+--..
| users | info_1 | info_2 | info_3 | .. | ..
+---------+----------+----------+----------+------+--..
onde users
foi definido como FULLTEXT VARCHAR(150)
, e seu valor sempre corresponde ao formato name_1-name_2
, enquanto eu usei um hífen porque uma vírgula é usada na MATCH...AGAINST
sintaxe para executar consultas de índice de texto completo.
Com essa estrutura, agora obtenho as seguintes novas possibilidades de consulta para A) e B):
A)
SELECT * FROM my_table WHERE MATCH(users) AGAINST ('"name_1-name_2","name_2-name_1"');
B)
SELECT * FROM my_table WHERE MATCH(users) AGAINST ('name_1');
O que na minha opinião facilita drasticamente a estrutura da consulta, e não usa nem duas SELECT
declarações, nem uma WHERE
cláusula adicional que será em vão. Ainda assim, como ainda não sei muito sobre otimização de desempenho de consulta e a comparação de pesquisas de índice de texto completo com pesquisas de índice regulares, gostaria de saber qual solução de I e II pode ter melhor desempenho e por quê? Ou existe ainda uma abordagem melhor?
Crítica
1A não pode usar índices por causa de
OR
1B --
user_1 = '%'
impede o uso deINDEX(user_1, ...)
, então a solução 1B está fora.1B pode ser recuperado tendo um segundo índice:
INDEX(user_2)
. Então basta dizer2-- FULLTEXT tem limitações, como o tamanho da "palavra". Portanto, seja cauteloso.
2A (
WHERE MATCH(users) AGAINST ('"name_1-name_2","name_2-name_1"')
-- provavelmente é suficiente dizer simplesmenteWHERE MATCH(users) AGAINST ('+name_1 +name_2' IN BOOLEAN MODE); The
+` diz que ambos são necessários, mas em qualquer ordem.2B -- OK (com as ressalvas acima)
FULLTEXT
, quando aplicável , provavelmente terá um bom desempenho. Aqui estão dois truques que usei:A 'palavra' curta (R) será ignorada; ele irá procurar por James eficientemente. Observe o uso seletivo de '+'.
Isso pressupõe que o nome pode estar no meio de uma coluna grande e eu preciso verificar a inicial. O teste FT será o primeiro (e rápido), seguido pelo LIKE (lento, mas contra poucas linhas).
Isso ainda tem problemas porque "E. Anders e R. Anderson" seriam pegos erroneamente.
(Etc, etc. Mas eu discordo do seu caso de uso simples de 2 palavras.)
Linha inferior:
1B (índice extra) é o ideal. (Mas suspeito que você tenha diluído a Pergunta.)
FULLTEXT
seria minha próxima escolha.Deixe-me adicionar uma opção 3:
Classifique os usuários antes de inserir e ao consultar. Aquilo é
então você precisa (para o caso A) apenas
INDEX(user1, user2)
junto comInfelizmente, isso provavelmente atrapalhará o caso B. ( Todas as linhas têm 2 nomes de usuário, mas algumas consultas têm apenas 1 usuário?)
E Opção 4
Tenha outra tabela que mapeie os nomes de usuário
my_table
porid
. Normalmente 2 linhas nesta nova tabela para cada linha emmy_table
. Opcionalmente, jogue as colunas do usuário demy_table
.Não entrarei no resto dos detalhes; eles ficam confusos.