Mysql: consulta lenta

Forum sobre SQL.

Moderador: Moderadores

alxsts
Colaborador
Colaborador
Mensagens: 3092
Registrado em: 12 Ago 2008 15:50
Localização: São Paulo-SP-Brasil

Mysql: consulta lenta

Mensagem por alxsts »

Olá!
cjp escreveu:Realmente vc tem razão, não são todos que precisam ser considerados nesta consulta.
JoséQuintas, [color=#FF0040]em 23/04/2021[/color] escreveu:Será que precisa mesmo todas as cotações de todos os tempos? Cotações antigas só vão servir como perda de tempo, deveria limitar data também.
[]´s
Alexandre Santos (AlxSts)
cjp
Usuário Nível 6
Usuário Nível 6
Mensagens: 1563
Registrado em: 19 Nov 2010 22:29
Localização: paraná
Contato:

Mysql: consulta lenta

Mensagem por cjp »

De fato ficou contraditório.
Mas o fato é que eu ainda preciso definir bem o que realmente é necessário.
Veja, neste primeiro caso (marca='V')
select codigo,max(datahora) as data,nritem from acoes use index (coddt) where marca='V' and hrvisto<>91234 and datahora>'"+dtsql(date()-20)+"' group by codigo order by data
o mais provável é que seja suficiente trazer os dados dos últimos 20 dias (talvez até menos). Mas não será sempre assim. É possível que não tenha nenhuma cotação cadastrada nos últimos 20 dias (ou até em período bem maior), e mesmo assim o código deve constar na lista (aliás, neste caso, com maior razão).
Ainda não pensei num critério ideal para restringir a pesquisa.
O que eu preciso é de uma lista simples dos códigos que não tenham cotação cadastrada nos últimos minutos ou nas últimas horas (dependendo da marca).
Antes a consulta estava mais complexa, verificando isso no próprio select. Agora eu simplifiquei o select, como acima, e verifico localmente depois a hora da última cotação.
Como poderia melhorar isso?
Inacio de Carvalho Neto
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Mysql: consulta lenta

Mensagem por JoséQuintas »

cjp escreveu:Como poderia melhorar isso?
Com uma boa modelagem de dados.

Apenas talvez:

Código: Selecionar todos

SELECT a.codigo, max( acoes.datahora )  AS data, acoes.nritem
FROM
   (
      SELECT
        ( SELECT DISTINCT CODIGO FROM ACOES WHERE MARCA='V' ) AS A
      LEFT JOIN ACOES
      ON A.CODIGO = ACOES.CODIGO AND HRVISTO <> 91234 AND DATAHORA > '2021-08-01' 
   )
WHERE .....
GROUP BY a.codigo
teste.png
apenas um simulado meio equivalente
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
cjp
Usuário Nível 6
Usuário Nível 6
Mensagens: 1563
Registrado em: 19 Nov 2010 22:29
Localização: paraná
Contato:

Mysql: consulta lenta

Mensagem por cjp »

Testei o teu exemplo no Heidi, e deu erro, conforme anexo.
Tirei o Where porque não saberia o que colocar ali. Não sei se foi por isso o erro, mas acho que não.
A verdade é que não entendi bem o teu exemplo. Pra mim ficou confuso com 3 select. Também não sei para que serviriam o left join.
Note que antes eu tinha um select parecido com este, mas eram 2 select, que está no início deste post. E me foi recomendado simplificá-lo.
Anexos
tela.png
Inacio de Carvalho Neto
cjp
Usuário Nível 6
Usuário Nível 6
Mensagens: 1563
Registrado em: 19 Nov 2010 22:29
Localização: paraná
Contato:

Mysql: consulta lenta

Mensagem por cjp »

Pessoal, retomo este post porque, em primeiro lugar, consegui resolver o problema inicialmente aqui relatado. Mudei totalmente a tabela e a programação, o que eliminou totalmente o problema de consultas lentas aqui mencionado. Demorou, mas resolvi isso. De quebra, resolvi também um outro problema que ocorria nesta mesma tabela, que foi relatado em outro post (depois vou noticiar isto também lá).
Mas, em segundo lugar, agora comecei a receber outro relatório de consulta lenta do mesmo provedor, desta vez em outra tabela, e mais incompreensível.

A query apontada como lenta é esta:

Código: Selecionar todos

DELETE FROM ativ WHERE acao = 'Exqado' AND DATA <= '2024-09-16'
Digo incompreensível porque é uma query simples. Como seria possível tornar esta query mais rápida?

Esta é uma tabela grande (neste momento ela está com 2,1 GB, mas varia de 1 a 3 GB), com muitos registros (neste momento com 1.131.800), mas funciona bem. Está com os índices necessários.

Imagino que seria possível fazer deletes parciais (ex: um delete para cada data). Isso deixaria cada query mais rápida, mas no total, todas juntas, ficaria bem mais lento. Faria sentido?

Existe algum outro jeito de resolver isso?

É razoável o provedor ficar reclamando de querys lentas neste caso? No meu modo de ver, 2 GB não é um tamanho excessivo para uma tabela MySQL. Estou errado?
Inacio de Carvalho Neto
Avatar do usuário
dbsh
Usuário Nível 3
Usuário Nível 3
Mensagens: 128
Registrado em: 14 Jul 2004 14:19
Localização: ES

Mysql: consulta lenta

Mensagem por dbsh »

Qual dos dois tem índice ação, data ou nenhum?
Qual dos dois tem menor ocorrência na tabela ação ou data?
Crie índice do que geralmente tem menor ocorrência.

EX: acao = 'Exqado' (10000 registros), DATA <= '2024-09-16' (1000 registros) // aqui seria vantagem cria índice para data
010011110010000001110011011101010110001101100101011100110111001101101111001000001110100100100000011000110110111101101110011100110111010001110010011101011110110101100100011011110010000001100001001000000110111001101111011010010111010001100101
01001101011000010111001001100011011011110111001100100000010000010110111001110100011011110110111001101001011011110010000001000100011001010010000001000010011011110110111001101001
0101010001100101011011000011101000100000001010000011001000110111001010010011100100101101001110010011100000110100001100110010110100110101001100100011100100110000
Avatar do usuário
dbsh
Usuário Nível 3
Usuário Nível 3
Mensagens: 128
Registrado em: 14 Jul 2004 14:19
Localização: ES

Mysql: consulta lenta

Mensagem por dbsh »

saber o total das ocorrências:

Código: Selecionar todos

SELECT  COUNT(*) FROM ativ WHERE  acao = 'Exqado';
SELECT  COUNT(*) FROM ativ WHEREDATA <= '2024-09-16;
010011110010000001110011011101010110001101100101011100110111001101101111001000001110100100100000011000110110111101101110011100110111010001110010011101011110110101100100011011110010000001100001001000000110111001101111011010010111010001100101
01001101011000010111001001100011011011110111001100100000010000010110111001110100011011110110111001101001011011110010000001000100011001010010000001000010011011110110111001101001
0101010001100101011011000011101000100000001010000011001000110111001010010011100100101101001110010011100000110100001100110010110100110101001100100011100100110000
cjp
Usuário Nível 6
Usuário Nível 6
Mensagens: 1563
Registrado em: 19 Nov 2010 22:29
Localização: paraná
Contato:

Mysql: consulta lenta

Mensagem por cjp »

Os dois têm índices.
Neste momento tem 890.000 registros do primeiro, e 142.000 do primeiro.
Inacio de Carvalho Neto
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Mysql: consulta lenta

Mensagem por JoséQuintas »

Pelo jeito usa muito isso.
Cria um índice por acao, data
Isso é diferente de criar dois índices, um pra cada.

Apenas compare com DBF:
Como seria mais rápido em DBF ?
SEEK da ação, e datas em ordem crescente, assim não precisaria processar tudo.
Se no DBF é mais rápido assim, no SQL também vai ser.
Apenas crie o índice que o SQL se vira.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
dbsh
Usuário Nível 3
Usuário Nível 3
Mensagens: 128
Registrado em: 14 Jul 2004 14:19
Localização: ES

Mysql: consulta lenta

Mensagem por dbsh »

Use EXPLAIN no inicio da sua query para verificar como esta sendo feita, EX:
EXPLAIN DELETE FROM ativ WHERE acao = 'Exqado' AND DATA <= '2024-09-16'

O comando EXPLAIN não ira executar o delete, ele verifica a performance do seu comando.

vai aparecer o campo possible_keys e key
possible_keys = possíveis indices a ser utilizado
key = índice usado
senão aparecer índice em key é porque não esta usando índice, revise seus índices que tem algo errado.
010011110010000001110011011101010110001101100101011100110111001101101111001000001110100100100000011000110110111101101110011100110111010001110010011101011110110101100100011011110010000001100001001000000110111001101111011010010111010001100101
01001101011000010111001001100011011011110111001100100000010000010110111001110100011011110110111001101001011011110010000001000100011001010010000001000010011011110110111001101001
0101010001100101011011000011101000100000001010000011001000110111001010010011100100101101001110010011100000110100001100110010110100110101001100100011100100110000
Avatar do usuário
dbsh
Usuário Nível 3
Usuário Nível 3
Mensagens: 128
Registrado em: 14 Jul 2004 14:19
Localização: ES

Mysql: consulta lenta

Mensagem por dbsh »

Tome os cuidados:
se seu campo é caractere e você faz a pesquisa com numero e provável que não vá fazer a pesquisa usando o índice, supondo que você tenha um campo GTIN, caracter e um índice para ele, EX:.

select * from produto where gtin = 5;
a pesquisa será incremental

um indice CREATE INDEX nome_do_indice ON ativ(acao, data);
não vai te ajudar se você tiver que pesquisar só por data, mas vai agilizar muito a pesquisa se você sempre usar os dois campos(acao,data).

[not] é opcional.
Não use SELECT * from ativ where [not] in (select...)
Prefira SELECT * from ativ [not] EXISTS (select...)
010011110010000001110011011101010110001101100101011100110111001101101111001000001110100100100000011000110110111101101110011100110111010001110010011101011110110101100100011011110010000001100001001000000110111001101111011010010111010001100101
01001101011000010111001001100011011011110111001100100000010000010110111001110100011011110110111001101001011011110010000001000100011001010010000001000010011011110110111001101001
0101010001100101011011000011101000100000001010000011001000110111001010010011100100101101001110010011100000110100001100110010110100110101001100100011100100110000
cjp
Usuário Nível 6
Usuário Nível 6
Mensagens: 1563
Registrado em: 19 Nov 2010 22:29
Localização: paraná
Contato:

Mysql: consulta lenta

Mensagem por cjp »

Mas o índice já está assim.
Veja o anexo, com o explain.
Não está certo?
Anexos
tela.png
tela.png (10.83 KiB) Exibido 5513 vezes
Inacio de Carvalho Neto
Avatar do usuário
dbsh
Usuário Nível 3
Usuário Nível 3
Mensagens: 128
Registrado em: 14 Jul 2004 14:19
Localização: ES

Mysql: consulta lenta

Mensagem por dbsh »

Usa um gerenciador de banco de dados (estou usando o HeidiSQL) e mostra como esta criando os índices.
Anexos
Captura de tela 2024-12-11 091701.png
010011110010000001110011011101010110001101100101011100110111001101101111001000001110100100100000011000110110111101101110011100110111010001110010011101011110110101100100011011110010000001100001001000000110111001101111011010010111010001100101
01001101011000010111001001100011011011110111001100100000010000010110111001110100011011110110111001101001011011110010000001000100011001010010000001000010011011110110111001101001
0101010001100101011011000011101000100000001010000011001000110111001010010011100100101101001110010011100000110100001100110010110100110101001100100011100100110000
cjp
Usuário Nível 6
Usuário Nível 6
Mensagens: 1563
Registrado em: 19 Nov 2010 22:29
Localização: paraná
Contato:

Mysql: consulta lenta

Mensagem por cjp »

Código: Selecionar todos

CREATE TABLE `ativ` (
	`USUARIO` VARCHAR(3) NULL DEFAULT NULL COLLATE 'latin1_swedish_ci',
	`PROGRAMA` VARCHAR(100) NULL DEFAULT NULL COLLATE 'latin1_swedish_ci',
	`DATA` DATE NULL DEFAULT NULL,
	`HORA` CHAR(8) NULL DEFAULT NULL COLLATE 'latin1_swedish_ci',
	`ACAO` VARCHAR(35) NULL DEFAULT NULL COLLATE 'latin1_swedish_ci',
	`detalhes` VARCHAR(6000) NULL DEFAULT NULL COLLATE 'latin1_swedish_ci',
	`NRTAREFA` VARCHAR(12) NULL DEFAULT NULL COLLATE 'latin1_swedish_ci',
	`PRIORIDADE` INT(1) NULL DEFAULT NULL,
	`CAMPO` CHAR(1) NULL DEFAULT NULL COLLATE 'latin1_swedish_ci',
	`TEMPOUSADO` DECIMAL(6,1) NULL DEFAULT NULL,
	`TEMPOCOMPU` DECIMAL(6,1) NULL DEFAULT NULL,
	`TEMPOPAGO` DECIMAL(6,1) NULL DEFAULT NULL,
	`assunto` VARCHAR(45) NULL DEFAULT NULL COLLATE 'latin1_swedish_ci',
	`maquina` VARCHAR(50) NULL DEFAULT NULL COLLATE 'latin1_swedish_ci',
	`marca` CHAR(1) NULL DEFAULT NULL COLLATE 'latin1_swedish_ci',
	`id` INT(11) NOT NULL AUTO_INCREMENT,
	PRIMARY KEY (`id`) USING BTREE,
	INDEX `data` (`DATA`) USING BTREE,
	INDEX `usuario` (`USUARIO`) USING BTREE,
	INDEX `dataacao` (`DATA`, `ACAO`) USING BTREE,
	INDEX `acao` (`ACAO`) USING BTREE,
	INDEX `consativ` (`USUARIO`, `HORA`, `ACAO`, `NRTAREFA`, `assunto`) USING BTREE,
	INDEX `listaftp` (`detalhes`(767), `USUARIO`, `DATA`) USING BTREE,
	INDEX `prior` (`USUARIO`, `DATA`, `ACAO`) USING BTREE,
	INDEX `tpc` (`TEMPOCOMPU`, `id`) USING BTREE,
	INDEX `ação+data` (`DATA`, `ACAO`) USING BTREE
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=148367111
;
Inacio de Carvalho Neto
Avatar do usuário
dbsh
Usuário Nível 3
Usuário Nível 3
Mensagens: 128
Registrado em: 14 Jul 2004 14:19
Localização: ES

Mysql: consulta lenta

Mensagem por dbsh »

Aparentemente esta correto "mas", estes dois índices estão repetidos:
INDEX `dataacao` (`DATA`, `ACAO`) USING BTREE,
INDEX `ação+data` (`DATA`, `ACAO`) USING BTREE

Você não precisa deste índice: INDEX `data` (`DATA`) USING BTREE.
Quando você pesquisa por data, o MySQL usará o índice `dataacao`, o campo data é o o primeiro na ordem (`DATA`, `ACAO`), mesmo que você não tenha o campo acao no filtro WHERE.

A mesma coisa. Pode sair o INDEX `usuario` (`USUARIO`) USING BTREE, pode ficar o índice: INDEX `prior` (`USUARIO`, `DATA`, `ACAO`) USING BTREE,

Refaça seus índices, muito índices atrapalha a performance do banco de dados: INSERT/UPDATE/DELETE

Se você quer ver mais informação no EXPLAIN use:
EXPLAIN FORMAT=json DELETE .....

Crie seus índices, posicionando os campos na sequencia mais usadas,
EX: INDEX `consativ` (`USUARIO`, `HORA`, `ACAO`, `NRTAREFA`, `assunto`) USING BTREE,
SELECT * FROM ativ usuario='001' and nrtarefa='123456789', não usaria o índice `consativ`, e sim o índice `prior`, faltou os campos hora e acao
SELECT * FROM ativ usuario='001' and hora='18:00:00' and acao='alguma-coisa', usaria o índice `consativ`.

Para forçar o uso de um índice específico, pode-se usar a sintaxe.
SELECT * FROM ativ FORCE INDEX (dataacao) WHERE .........

O MySQL não usa de índices nos seguintes cenários(deve ter mais):
Comparação de colunas de tipos diferentes.
Tamanhos e conjuntos de caracteres em uma cláusula WHERE ou junção.
A tabela é muito pequena, uma busca sequencial é mais rápida.
A tabela é grande, mas a consulta requer a maioria ou todas as linhas, EX: campos com valores S/N apenas.

O MySQL seleciona o melhor índice para uma query de acordo com a cardinalidade da coluna, que é o número de valores distintos que ela contém.
Os índices funcionam melhor em colunas com uma cardinalidade relativamente alta em relação ao número de linhas da tabela.
010011110010000001110011011101010110001101100101011100110111001101101111001000001110100100100000011000110110111101101110011100110111010001110010011101011110110101100100011011110010000001100001001000000110111001101111011010010111010001100101
01001101011000010111001001100011011011110111001100100000010000010110111001110100011011110110111001101001011011110010000001000100011001010010000001000010011011110110111001101001
0101010001100101011011000011101000100000001010000011001000110111001010010011100100101101001110010011100000110100001100110010110100110101001100100011100100110000
Responder