Página 1 de 1

Otimizando SQL/etc

Enviado: 28 Nov 2019 08:29
por JoséQuintas
Uma coisa que tem gente que não entendeu: SQL é pra otimizar, não só pra trazer informação.
E quanto menos informação, mais rápido
Em DBF:

Código: Selecionar todos

USE pedido
nTotal := 0
DO WHILE ! Eof()
   IF pedido->DatEmissao < dDatIni
      SKIP
      LOOP
   ENDIF
   IF pedido->DatEmissao > dDatFim
      SKIP
      LOOP
   ENDIF
   IF pedido->Cliente != mCliente
      SKIP
      LOOP
   ENDIF
   nTotal += pedido->Valor
   SKIP
ENDDO
Mesmo em DBF, o fonte acima poderia ser trocado para:

Código: Selecionar todos

SUM pedido->Valor TO nTotal FOR .NOT. ( pedido->DatEmissao < dDatIni .OR. pedido->DatEmissao > dDatFim ) ;
   .AND. pedido->Cliente = mCliente
Em DBF isso pode ser um LIXO, pode ser mais demorado, mas.... a opção existe.

Em SQL devemos considerar quanta informação vém pela rede.
Trazer o mês inteiro de informação pra obter a soma.... isso é idiotice... isso é coisa de DBF.

Vamos as formas idiotas de fazer isso no SQL:

Código: Selecionar todos

SELECT * FROM pedido
Esta é a forma mais idiota de todas. Pra que trazer TUDO? E isto é exatamente o que acontece com DBF

Código: Selecionar todos

SELECT datEmissao, Cliente, Valor FROM pedido
Esta forma é um pouco menos idiota, trás só os campos que precisa

Código: Selecionar todos

SELECT datEmissao, Valor FROM pedido WHERE Cliente = '12345'
Esta forma é um pouco menos idiota, trás só a informação do cliente

Código: Selecionar todos

SELECT Valor FROM pedido WHERE cliente = '12345' AND DatEmissao BETWEEN '2019-10-01' AND '2019-10-31'
Esta forma é um pouco menos idiota, trás só os pedidos que interessam

Código: Selecionar todos

SELECT SUM( VALOR ) FROM pedido WHERE cliente = '12345' AND DatEmissao BETWEEN '2019-10-01' AND '2019-10-31'
Esta seria a forma correta. trás pela rede apenas o valor total, que é o quer interessa, e vai ser digamos... 16 bytes pela rede (ou conexão), algo instantâneo.

Tem programador que não entende isso.
NÃO tem que trazer tudo pra processar local, pede pronto que o servidor manda pronto.

O servidor não é apenas pra guardar a base de dados, ele pode trabalhar.
Para o servidor a base é local, todo processamento é local, então o servidor vai trabalhar rápido.
Ainda mais se a conexão for lenta.... quanto menos informação vier, mais rápido vai chegar.

Se vai emitir uma listagem de pedidos, com números e valores, pede só os números e os valores, não precisa da informação completa dos pedidos.

Até mesmo um simples browse...
Por um lado, o browse do DBF trás somente os registros que cabem na tela.... parece bom
Por outro lado, pra fazer isso, vém o registro completo SEMPRE.

Digamos que o DBF tem 1000 campos de 10 caracteres, vai fazer um browse de 2 campos, 30 registros por vez...
Em DBF, para UMA PÁGINA do browse, vai trazer 300.000 caracteres
Em SQL, solicitando apenas os 2 campos, vão ser 600 caracteres para a mesma informação.
Ou seja... o tempo de rede que o DBF usa pra uma única página desse browse, é o mesmo do SQL pra trazer 500 páginas !!!

Achou exagerado? trazer 500 vezes mais informação no mesmo tempo?

Vamos a outro exemplo:
O DBF é acessado registro a registro, o registro completo SEMPRE. Com filtro, mesmo os registros filtrados são acessados, pra analisar se entram ou não no "browse".
Usando o mesmo exemplo: 1000 campos com 10 caracteres, vai filtrar 1 registro em cada 10, mostrar 2 campos na tela, 30 registros por vez.
Uma única tela teria processado 300 registros x 10.000 caracteres = 3 milhões de caracteres por tela.
Em SQL: 30 registros x 20 caracteres = 600 caracteres por tela
Dá pra trazer 5.000 páginas de browse, no tempo que o DBF demoraria pra trazer uma única página.

Tudo bem, é um exemplo exagerado, mas... nem está sendo considerada a pesquisa de DBF + CDX, ou um possível relacionamento com outros arquivos, ou outras coisas mais. Seria mais informação ainda pra DBF, que no SQL pode vir pronto.

No final das contas: dá pra agilizar DBF? sim... é acessar o mínimo de informação possível, usando índices pra isso, que é o recurso disponível pra DBF.
NÃO é criar índice na hora do processamento, é o índice já existir e tirar proveito dele.

Por exemplo, relatório de produtos em ordem de data....
Pode ter um índice por produtos + data
Ao começar a imprimir o produto: SEEK produto + datainicial SOFTSEEK
Terminou o produto, por exemplo, SEEK ( produto + 1 ) SOFTSEEK
Com essas duas pesquisas, vai pular todas as datas que não interessam.

E por aí vai... cada situação uma solução, ou um índice que pode agilizar.
SET FILTER nem pensar...
SKIP;LOOP.... de certa forma, idem...
Nos dois casos, mesmo que não use, vai estar trazendo o registro completo pela rede.

Otimizando SQL/etc

Enviado: 28 Nov 2019 08:39
por JoséQuintas
Voltando ao primeiro exemplo...
Se tem índice por cliente + data

Código: Selecionar todos

SEEK mcliente + Dtos( ddatainicial ) SOFTSEEK
SUM VALOR TO nTotal WHILE cliente == mcliente .AND. data <= dDataFinal
Qual a agilização?
Vai trabalhar somente com os pedidos do cliente, com data no intervalo solicitado.
Os outros registros nem vão ser lidos, e não vão passar pela rede.
Se tiver um único pedido... vai ser instantâneo, mesmo com milhões de registros no arquivo

Otimizando SQL/etc

Enviado: 28 Nov 2019 08:49
por JoséQuintas
E se fosse o total por cliente? uma combinação desse com agilizações:

Código: Selecionar todos

DO WHILE ! Eof()
   mCliente := pedido->cliente
   SEEK mCliente + Dtos( dDataInicial ) SOFTSEEK
   SUM VALOR TO nTotal WHILE cliente==mCliente .AND. Data <= dDataFinal
   ? mCliente, nTotal
   SEEK ( mCliente + 1 ) SOFTSEEK
ENDDO
Qual a agilização?
Vai pular tudo que não interessa, de todos os clientes.
Isso significa que não vém lixo pela rede, só vém o que interessa.

Nota: é só um exemplo, lógico que o código do cliente precisaria StrZero() ou algo assim, mas é só pra passar a idéia, não o fonte pronto.

Otimizando SQL/etc

Enviado: 28 Nov 2019 10:02
por JoséQuintas
Nesse exemplo... e se fosse em ordem alfabética?

Provavelmente melhor criar um temporário (array ou não), contendo esse resultado, depois pegar o nome dos clientes e colocar em ordem alfabética.
Vai trabalhar só com a movimentação que existe, depois vai pegar só o nome dos clientes que tem movimentação, e só depois coloca em ordem alfabética.
Se fizer o caminho contrário, relacionar clientes por exemplo, só vai poluir mais ainda a rede com lixo.