Diferença entre visualização embutida e cláusula WITH?


9

As visualizações embutidas permitem que você selecione de uma subconsulta como se fosse uma tabela diferente:

SELECT
    *
FROM /* Selecting from a query instead of table */
    (
        SELECT
            c1
        FROM
            t1
        WHERE
            c1 > 0
    ) a
WHERE
    a.c1 < 50;

Eu já vi isso mencionado usando termos diferentes: visualizações em linha, cláusula WITH, CTE e tabelas derivadas. Para mim, parece que eles são sintaxe específica de fornecedor diferente para a mesma coisa.

Esta é uma suposição errada? Existem diferenças técnicas / de desempenho entre elas?


5
Os nomes "oficiais" do SQL padrão são Tabela derivada (que o Oracle nomeia Inline View ) e Expressão de tabela comum (= WITH...). Você pode reescrever cada tabela derivada como uma CTE, mas talvez não o contrário (por exemplo recursiva CTE ou usando os CTE várias vezes)
dnoeth

Respostas:


8

Existem algumas diferenças importantes entre as visualizações embutidas (tabelas derivadas) e a cláusula WITH (CTE) no Oracle. Alguns deles são bastante universais, ou seja, são aplicáveis ​​a outros RDBMS.

  1. WITH pode ser usado para criar subconsultas recursivas, exibição em linha - não (até onde eu sei o mesmo é para todos os RDBMS que suportam CTE)
  2. WITHÉ provável que a subconsulta na cláusula seja fisicamente executada primeiro; em muitos casos, escolher entre WITHe a visualização embutida faz com que o otimizador escolha diferentes planos de execução (acho que é específico do fornecedor, talvez até da versão).
  3. A subconsulta WITHpode ser materializada como uma tabela temporária (não sei se existe outro fornecedor, mas o Oracle suporta esse recurso).
  4. A subconsulta em WITHpode ser referenciada várias vezes, em outras subconsultas e na consulta principal (verdadeira para a maioria dos RDBMS).

O MySQL (pelo menos as versões mais recentes do MariaDB) pode materializar tabelas derivadas (e até adicionar índices).
ypercubeᵀᴹ

3
Eu gostaria de acrescentar que, como benefício colateral, o uso de CTEs também é geralmente mais legível para humanos.
Joishi Bodio 2/17/16

@JoishiBodio: Pessoalmente, eu concordo com você, mas a legibilidade é uma questão bastante subjetiva. Eu prefiro evitar mencioná-lo
a1ex07 2/17/17

Além disso, um CTE pode fazer referência a um CTE previamente declarado. Uma tabela derivada não pode fazer referência a uma tabela derivada declarada anteriormente no mesmo nível, a menos que LATERALseja usada.
Lennart

8

Outras respostas cobrem muito bem as diferenças de sintaxe, portanto não vou entrar nisso. Em vez disso, essa resposta cobrirá apenas o desempenho no Oracle.

O otimizador Oracle pode optar por materializar os resultados de um CTE em uma tabela temporária interna. Ele usa uma heurística para fazer isso, em vez de otimização baseada em custos. A heurística é algo como "Materialize o CTE se não for uma expressão trivial e o CTE for referenciado mais de uma vez na consulta". Existem algumas consultas para as quais a materialização melhorará o desempenho. Existem algumas consultas para as quais a materialização prejudicará drasticamente o desempenho. O exemplo a seguir é um pouco artificial, mas ilustra bem o ponto:

Primeiro, crie uma tabela com uma chave primária que contenha números inteiros de 1 a 10000:

CREATE TABLE N_10000 (NUM_ID INTEGER NOT NULL, PRIMARY KEY (NUM_ID));

INSERT /*+APPEND */ INTO N_10000
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL <= 10000
ORDER BY LEVEL;

COMMIT;

Considere a seguinte consulta que usa duas tabelas derivadas:

SELECT t1.NUM_ID
FROM 
(
  SELECT n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
) t1
LEFT OUTER JOIN 
(
  SELECT n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
) t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;

Podemos analisar esta consulta e determinar rapidamente que ela não retornará nenhuma linha. A Oracle deve poder usar o índice para determinar isso também. Na minha máquina, a consulta termina quase instantaneamente com o seguinte plano:

bom plano

Eu não gosto de me repetir, então vamos tentar a mesma consulta com um CTE:

WITH N_10000_CTE AS (
  SELECT n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
)
SELECT t1.NUM_ID
FROM N_10000_CTE t1
LEFT JOIN N_10000_CTE t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;

Aqui está o plano:

plano ruim

Esse é um plano muito ruim. Em vez de usar o índice, o Oracle materializa 10000 X 10000 = 100000000 linhas em uma tabela temporária apenas para retornar 0 linhas. O custo deste plano é de cerca de 6 M, o que é muito mais alto que a outra consulta. A consulta levou 68 segundos para terminar na minha máquina.

Observe que a consulta pode ter falhado se não houver memória suficiente ou espaço livre no espaço de tabela temporário.

Posso usar a INLINEdica não documentada para impedir o otimizador de materializar o CTE:

WITH N_10000_CTE AS (
  SELECT /*+ INLINE */ n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
)
SELECT t1.NUM_ID
FROM N_10000_CTE t1
LEFT JOIN N_10000_CTE t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;

Essa consulta é capaz de usar o índice e termina quase instantaneamente. O custo da consulta é o mesmo de antes, 11. Portanto, para a segunda consulta, a heurística usada pelo Oracle resultou na escolha de uma consulta com um custo estimado de 6 M em vez de uma consulta com um custo estimado de 11.


1

Para o SQL Server, WITH CTEespecifica o conjunto de resultados nomeado temporário, mas é necessário apenas para o primeiro CTE. ie

WITH CTE AS (SELECT .... FROM), 
CTE2 AS (SELECT .... FROM)

SELECT CTE.Column, CTE2.Column
FROM CTE
INNER JOIN CTE2 on CTE.Column = CTE2.Column

Mas isso não é uma subconsulta ou subconsulta correlacionada. Há coisas que você pode fazer com um CTE e o que não pode fazer com uma subconsulta no SQL Server, como atualizar as Tabelas referenciadas em um CTE. Aqui está um exemplo de atualização de uma tabela com um CTE.

Uma subconsulta seria algo como

SELECT
   C1,
   (SELECT C2 FROM SomeTable) as C2
FROM Table

Ou uma subconsulta correlacionada é o que você forneceu no seu OP se você referenciar / ingressar / limitar seus resultados com base em a.c1.

Portanto, eles definitivamente não são a mesma coisa, embora em muitos casos você possa usar um ou mais desses métodos para obter o mesmo resultado. Depende apenas do resultado final.


1

A principal diferença entre withcláusula e uma subconsulta no Oracle é que você pode fazer referência a uma consulta dentro da cláusula várias vezes. Em seguida, você pode fazer algumas otimizações como transformá-lo em uma tabela temporária usando a materializedica. Você também pode fazer consultas recursivas fazendo referência a si mesma dentro de uma withcláusula. Você não pode fazer isso com uma exibição embutida.

Mais informações podem ser encontradas aqui e aqui .


Em geral, a dica de materialização não é necessária. Por padrão, o Oracle Optimizer decide se faz sentido materializar o CTE ou não - mas você pode sobrescrever a avaliação do otimizador com a dica MATERIALIZEresp. INLINEpelo contrário.
Wernfried Domscheit

@WernfriedDomscheit isso é verdade. Mas, às vezes, o otimizador não escolhe materializar o CTE e, nesse caso, usar a materializedica é a opção válida. Às vezes, eu precisava especificá-lo ao otimizar consultas muito complexas, onde sabia que materializar o CTE beneficiaria o plano de execução.
Marko Vodopija

0

Você precisa ter cuidado com os CTEs no SQL Server e não apenas com a Oracle, há casos em que as consultas apresentam desempenho muito pior ao usar CTEs em comparação com subconsultas, aplicação cruzada etc.

Como sempre, é importante testar qualquer consulta sob várias condições de carga para determinar qual delas funciona melhor.

Semelhante ao @scsimon da Oracle, às vezes o MS SQL Server não faz o que você espera em relação ao uso do índice.

Se você usar os mesmos dados mais de uma vez, as CTEs poderão ser mais úteis; se você estiver usando apenas uma vez, geralmente uma subconsulta é mais rápida em grandes conjuntos de dados.

por exemplo, selecione * de (minha subconsulta) junte-se a outra coisa ...

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.