Pergunta conceitual: as consultas individuais são mais rápidas que as associações, ou: Devo tentar espremer todas as informações que desejo do lado do cliente em uma instrução SELECT ou apenas usar quantas parecer conveniente?
TL; DR : Se minha consulta ingressada demorar mais do que a execução de consultas individuais, isso é culpa minha ou isso é esperado?
Primeiro, não sou muito conhecedor de bancos de dados, portanto, sou eu, mas notei que, quando preciso obter informações de várias tabelas, é "frequentemente" mais rápido obter essas informações por meio de várias consultas em tabelas individuais (talvez contendo uma junção interna simples) e remende os dados juntos no lado do cliente para tentar escrever uma consulta ingressada (complexa) onde eu possa obter todos os dados em uma consulta.
Eu tentei colocar um exemplo extremamente simples:
Configuração do esquema :
CREATE TABLE MASTER
( ID INT NOT NULL
, NAME VARCHAR2(42 CHAR) NOT NULL
, CONSTRAINT PK_MASTER PRIMARY KEY (ID)
);
CREATE TABLE DATA
( ID INT NOT NULL
, MASTER_ID INT NOT NULL
, VALUE NUMBER
, CONSTRAINT PK_DATA PRIMARY KEY (ID)
, CONSTRAINT FK_DATA_MASTER FOREIGN KEY (MASTER_ID) REFERENCES MASTER (ID)
);
INSERT INTO MASTER values (1, 'One');
INSERT INTO MASTER values (2, 'Two');
INSERT INTO MASTER values (3, 'Three');
CREATE SEQUENCE SEQ_DATA_ID;
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.3);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.5);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.7);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 2, 2.3);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 3, 3.14);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 3, 3.7);
Consulta A :
select NAME from MASTER
where ID = 1
| NAME |
--------
| One |
Consulta B :
select ID, VALUE from DATA
where MASTER_ID = 1
| ID | VALUE |
--------------
| 1 | 1.3 |
| 2 | 1.5 |
| 3 | 1.7 |
Consulta C :
select M.NAME, D.ID, D.VALUE
from MASTER M INNER JOIN DATA D ON M.ID=D.MASTER_ID
where M.ID = 1
| NAME | ID | VALUE |
---------------------
| One | 1 | 1.3 |
| One | 2 | 1.5 |
| One | 3 | 1.7 |
Obviamente, não medi nenhum desempenho com isso, mas podemos observar:
- A consulta A + B retorna a mesma quantidade de informações utilizáveis que a consulta C.
- A + B deve retornar 1 + 2x3 == 7 "Células de Dados" para o cliente
- C precisa retornar 3x3 == 9 "Células de Dados" ao cliente, porque com a junção naturalmente incluo alguma redundância no conjunto de resultados.
Generalizando a partir disso (por mais buscado que seja):
Uma consulta ingressada sempre deve retornar mais dados do que as consultas individuais que recebem a mesma quantidade de informações. Como o banco de dados precisa reunir os dados, para conjuntos de dados grandes, pode-se supor que o banco de dados tenha que trabalhar mais em uma única consulta unida do que nas individuais, pois (pelo menos) ele deve retornar mais dados ao cliente.
Daqui resulta que, quando observo que a divisão de uma consulta do lado do cliente em várias consultas produz um melhor desempenho, esse é apenas o caminho a seguir ou seria melhor dizer que eu estraguei a consulta unida?
fonte
Respostas:
Em qualquer cenário de desempenho, você precisa testar e medir as soluções para ver qual é mais rápido .
Dito isso, é quase sempre o caso de um conjunto de resultados unidos de um banco de dados ajustado corretamente ser mais rápido e escalar melhor do que retornar as linhas de origem ao cliente e depois juntá-las lá. Em particular, se os conjuntos de entrada forem grandes e o conjunto de resultados for pequeno - pense na seguinte consulta no contexto de ambas as estratégias: junte duas tabelas de 5 GB cada, com um conjunto de resultados de 100 linhas. Isso é extremo, mas você entende meu ponto.
É altamente provável que o esquema ou os índices do banco de dados possam ser aprimorados para atender melhor às consultas que você está fazendo.
Geralmente não é esse o caso. Na maioria das vezes, mesmo que os conjuntos de entradas sejam grandes, o conjunto de resultados será muito menor que a soma das entradas.
Dependendo do aplicativo, conjuntos de resultados de consulta muito grandes retornados ao cliente são uma bandeira vermelha imediata: o que o cliente está fazendo com um conjunto tão grande de dados que não pode ser feito mais perto do banco de dados? Exibir 1.000.000 de linhas para um usuário é altamente suspeito, para dizer o mínimo. A largura de banda da rede também é um recurso finito.
Não necessariamente. Se os dados forem indexados corretamente, é mais provável que a operação de junção seja realizada com mais eficiência no banco de dados, sem a necessidade de varrer uma grande quantidade de dados. Além disso, os mecanismos de banco de dados relacional são especialmente otimizados em um nível baixo para ingresso ; pilhas de clientes não são.
Como você disse que é inexperiente no que diz respeito a bancos de dados, sugiro aprender mais sobre design de banco de dados e ajuste de desempenho. Tenho certeza de que é aí que o problema está aqui. Também são possíveis consultas SQL gravadas ineficientemente, mas com um esquema simples que é menos provável que seja um problema.
Agora, isso não quer dizer que não há outras maneiras de melhorar o desempenho. Há cenários em que você pode optar por varrer um conjunto de dados de médio a grande porte e devolvê-lo ao cliente se a intenção for usar algum tipo de mecanismo de armazenamento em cache. O armazenamento em cache pode ser ótimo, mas introduz complexidade no seu design. O armazenamento em cache pode até não ser apropriado para o seu aplicativo.
Uma coisa que não foi mencionada em nenhum lugar é manter a consistência nos dados retornados do banco de dados. Se consultas separadas forem usadas, é mais provável (devido a muitos fatores) a devolução de dados inconsistentes, a menos que seja usada uma forma de isolamento de instantâneo para cada conjunto de consultas.
fonte
Você cria um bom código de exemplo. Você olhou para o tempo no SQL Fiddle? Até mesmo alguns breves testes de desempenho não-científicos mostram que a consulta três em sua demonstração leva aproximadamente a mesma quantidade de tempo para ser executada como a consulta um ou dois separadamente. Um e dois combinados levam cerca de duas vezes o tempo que três e isso é antes de qualquer junção do lado do cliente ser realizada.
À medida que você aumenta os dados, a velocidade da consulta um e dois diverge, mas a associação ao banco de dados ainda é mais rápida.
Você também deve considerar o que aconteceria se a junção interna estivesse eliminando dados.
fonte
O otimizador de consulta também deve ser considerado. Seu papel é pegar seu SQL declarativo e convertê-lo em etapas processuais. Para encontrar a combinação mais eficiente de etapas processuais, ele examinará as combinações de uso, classificação, armazenamento em cache de conjuntos de resultados intermediários e todo tipo de outras coisas também. O número de permutações pode ser extremamente grande, mesmo com consultas bastante simples.
Grande parte do cálculo feito para encontrar o melhor plano é direcionado pela distribuição de dados nas tabelas. Essas distribuições são amostradas e armazenadas como objetos de estatística. Se estiverem errados, eles levam o otimizador a fazer más escolhas. As más escolhas no início do plano levam a escolhas ainda piores mais tarde, em um efeito de bola de neve.
Não é desconhecido que uma consulta de tamanho médio retorne pequenas quantidades de dados para levar minutos para ser executada. A indexação correta e boas estatísticas reduzem isso a milissegundos.
fonte
Várias consultas é o caminho a percorrer. Se você lida com cenários simples como esse - o custo adicional do otimizador de consulta é um fator. Com mais dados, a ineficiência de rede da junção (linhas redundantes) entra. Somente com muito mais dados há eficiência.
No final, o que você experimenta é algo que muitos desenvolvedores veem. Os DBAs sempre dizem "não, faça uma junção", mas a realidade é: é mais rápido fazer várias seleções simples nesse caso.
fonte