junção externa esquerda em duas colunas problema de desempenho
Pergunta
Eu estou usando uma consulta SQL que é semelhante à seguinte forma:
SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.person_uid = table2.person_uid
AND table1.period = table2.period
E é de qualquer forma muito lenta ou deadlocking de alguma coisa, porque é preciso pelo menos 4 minutos para retorno. Se eu fosse para alterá-lo para isto:
SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.person_uid = table2.person_uid
WHERE table1.period = table2.period
, em seguida, ele funciona muito bem (embora não retornar o número correto de colunas). Existe alguma maneira de acelerar isso?
Atualizar : Ele faz a mesma coisa se eu mudar as duas últimas linhas da última consulta:
SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.period = table2.period
WHERE table1.person_uid = table2.person_uid
UPDATE 2: Estes são realmente vê que eu estou juntando. Infelizmente, eles estão em um banco de dados que eu não tenho controle sobre, por isso não posso (facilmente) fazer qualquer alteração para a indexação. Estou inclinado a concordar que este é um problema indexação embora. Vou esperar um pouco antes de aceitar uma resposta no caso de haver alguma maneira mágica para sintonizar essa consulta que eu não sei sobre. Caso contrário, eu vou aceitar uma das respostas atuais e tentar descobrir outra maneira de fazer o que eu quero fazer. Obrigado pela ajuda de todos até agora.
Solução
Tenha em mente que as declarações 2 e 3 são diferentes para o primeiro.
Como? Bem, você está fazendo uma junção externa esquerda e sua cláusula WHERE não está levando isso em conta (como a cláusula ON faz). No mínimo, tente:
SELECT col1, col2
FROM table1, table2
WHERE table1.person_uid = table2.person_uid (+)
AND table1.period = table2.period (+)
e veja se você tem o mesmo problema de desempenho.
O que índices que você tem sobre estas mesas? É esta relação definida por uma restrição de chave estrangeira?
O que você provavelmente necessidade é um índice composto em ambos person_uid e período (em ambas as tabelas).
Outras dicas
Eu acho que você precisa entender por que os dois últimos não são a mesma consulta como a primeira. Se você fizer uma associação à esquerda e depois adicionar uma cláusula onde referncing um campo na tabela no lado direito da junção (o que nem sempre pode ter um registro para coincidir com a primeira tabela), então você tem efetivamente mudou a juntar-se a uma junção interna. Há uma exceção a esta e que é se você faz referência a algo como
SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.person_uid = table2.person_uid
WHERE table2.person_uid is null
Neste caso, você perguntar para o registro que não têm um registro na segunda tabela. Mas para além deste caso especial, você está mudando a esquerda se unem para uma junção interna se você refence um campo em table2 na cláusula where.
Se a consulta não é rápido o suficiente, gostaria de olhar para a sua indexação.
Qualquer coisa qualquer um lhe diz com base na informação fornecida é uma suposição.
Olhe para o plano de execução para a consulta. Se você não vê uma razão para a lentidão no plano, o cargo o plano aqui.
http://download.oracle .com / docs / cd / B28359_01 / server.111 / b28274 / ex_plan.htm # PFGRF009
Você tem cobrindo índices em person_uid
e period
para ambas as tabelas?
Se não, adicione-os e tente novamente.
Dê uma olhada no plano de execução e ver o que a consulta está realmente fazendo.
Além disso: Quais são os tipos de dados dos campos? eles são os mesmos em ambas as tabelas? Uma conversão implícita pode coisas realmente lento.
Será que estas tabelas têm índices nas colunas que você está juntando? Instalar produto SQLDeveloper livre da Oracle e usá-lo para fazer um "explicar" nessa consulta e ver se ele está fazendo varreduras seqüenciais de ambas as tabelas.
Em uma associação à esquerda você estaria digitalização table1 para cada combinação única de (person_uid, período), em seguida, à procura table2 para todos os registros correspondentes lá. Se table2 não tem um índice apropriado, isso pode envolver a digitalização de toda aquela mesa também.
Meu melhor palpite, sem ver um plano de execução, é que a primeira consulta (o único que parece ser correto) é ter de mesa table2 varredura, bem como table1.
Como você diz que você não pode mudar os índices, você precisa mudar a consulta. Tanto quanto eu posso dizer, só há uma alternativa realista ...
SELECT
col1, col2
FROM
table2
FULL OUTER JOIN
table1
ON table1.person_uid = table2.person_uid
AND table1.period = table2.period
WHERE
table1.person_uid IS NOT NULL
A esperança aqui é que você table2 varredura para cada combinação única de (person_uid, período), mas fazer uso de índices na tabela 1. (Ao contrário de digitalização table1 e fazendo uso de índices em table2, que o que eu esperava de sua consulta.)
Se table1 não tem índices apropriados, no entanto, você vai ser muito provável que vejamos uma melhoria de desempenho em todos os ...
Dems.
Em uma das atualizações dos estados OP que ele é realmente consultando opiniões não mesas. Neste caso, o desempenho poderia ser aumentada por meio de consulta diretamente as tabelas que ele precisa, especialmente se os pontos de vista são complexas e junte-se a muitas outras tabelas que não contêm informações que ele precisa ou são vistas que os pontos de vista de chamadas.
junção ANSI sintaxe fornece uma distinção muito clara entre condições de junção e predicados filtro; isso é muito importante quando se escreve junções externas. Usando o emp / mesas Dept, olhar para os resultados dos dois exterior a seguir une
Q1
SELECT dname, d.deptno, e.ename, e.mgr, d.loc
FROM dept d
LEFT OUTER JOIN emp e
on d.deptno = e.deptno
and loc in ('NEW YORK','BOSTON' )
;
DNAME DEPTNO ENAME MGR LOC
-------------- ---------- ---------- ---------- -------------
ACCOUNTING 10 CLARK 7839 NEW YORK
ACCOUNTING 10 KING NEW YORK
ACCOUNTING 10 MILLER 7782 NEW YORK
RESEARCH 20 DALLAS
SALES 30 CHICAGO
OPERATIONS 40 BOSTON
====
Q2
SELECT dname, d.deptno, e.ename, e.mgr, d.loc
FROM dept d
LEFT OUTER JOIN emp e
on d.deptno = e.deptno
where loc in ('NEW YORK','BOSTON' )
;
DNAME DEPTNO ENAME MGR LOC
-------------- ---------- ---------- ---------- -------------
ACCOUNTING 10 CLARK 7839 NEW YORK
ACCOUNTING 10 KING NEW YORK
ACCOUNTING 10 MILLER 7782 NEW YORK
OPERATIONS 40 BOSTON
O primeiro exemplo, mostra Q1 é um exemplo de "ingressar em uma constante". Essencialmente, a condição do filtro é aplicada antes de se efectuar a junção externa. Então você eliminar linhas, que são posteriormente adicionados de volta como parte da junção externa. Não é necessariamente errado, mas é que a consulta que você realmente pediu? Muitas vezes é os resultados mostrados na Q2 que são exigidas, em que o filtro é aplicado após o (exterior) juntar-se.
Há também uma implicação desempenho também, para grandes conjuntos de dados. Em muitos casos, juntando-se em um constante tem de ser resolvida internamente pelo otimizador, criando uma vista lateral, que normalmente só pode ser otimizado através de um loop aninhado em vez de um hash
Para os desenvolvedores que estão familiarizados com o exterior do Oracle sintaxe JOIN, a consulta provavelmente teria sido escrito como
SELECT dname, d.deptno, e.ename, e.mgr, d.loc
FROM dept d
,emp e
where d.deptno = e.deptno(+)
and loc in ('NEW YORK','BOSTON' )
Esta consulta é semanticamente equivalente Q2 acima.
Então, em resumo, é extremamente importante o que você entenda a diferença entre a cláusula JOIN e a cláusula WHERE ao escrever ANSI associações externas.