20th Aug 2024 13 minutos de leitura CTE do PostgreSQL: o que é e como usá-lo Jill Thornhill postgresql cte Índice Expressões de tabela comuns no PostgreSQL Sintaxe do CTE no PostgreSQL Exemplo de CTE no PostgreSQL Definição de nomes de colunas CTE Consultas aninhadas usando o CTE do PostgreSQL CTE do PostgreSQL em Linguagem de Manipulação de Dados Consultas Recursivas Saiba mais sobre os CTEs do PostgreSQL Os CTEs, ou Common Table Expressions (expressões comuns de tabela), são uma ferramenta poderosa do PostgreSQL que é frequentemente ignorada. Este artigo examina vários CTEs do PostgreSQL PostgreSQL - incluindo CTEs aninhados e recursivos - e o que você pode fazer com eles. Se você escrever consultas complexas em SQL, logo perceberá que seu código se tornará confuso e difícil de ler. Os CTEs, também conhecidos como cláusulas WITH, são principalmente uma forma de simplificar as consultas. Entretanto, eles também permitem que você use a recursão. A recursão, entre outros usos, permite que você navegue facilmente por estruturas hierárquicas. Os CTEs (ou Common Table Expressions) do PostgreSQL são muito semelhantes às subconsultas; a diferença é que os CTEs são nomeados e definidos na parte superior do seu código. Isso permite que você divida uma consulta grande em pequenas seções. Neste artigo, apresentarei vários exemplos de CTEs do PostgreSQL. Presumo que você já esteja familiarizado com a escrita de consultas no PostgreSQL. Caso contrário, vale a pena fazer o download da nossaFolha de dicas do PostgreSQL . Se você acha que os CTEs do PostgreSQL o ajudarão em seu trabalho, talvez queira dar uma olhada em nosso curso interativo Common Table Expressions in PostgreSQL. Esse curso foi desenvolvido para aqueles que já estão familiarizados com o SQL básico. Você terá muita prática no uso de CTEs do PostgreSQL, graças a mais de cem exercícios interativos. Vamos nos aprofundar nas expressões de tabela comuns no Postgres! Expressões de tabela comuns no PostgreSQL Sintaxe do CTE no PostgreSQL Vamos agora dar uma olhada mais de perto na sintaxe do CTE. Em sua forma mais simples, ela tem a seguinte aparência: WITH cte_name AS (query_1) query_2; cte_name é o nome que você atribui ao CTE. Você pode se referir a esse nome em sua consulta principal ou em subconsultas, da mesma forma que faria com uma tabela. query_1 é qualquer SELECT válido query_2 é uma instrução SQL válida. Pode ser um SELECT, um UPDATE, um INSERT ou um DELETE. Os resultados de query_1 estarão disponíveis como se fossem uma tabela. O nome da tabela será o nome que você especificou como cte_name. Você pode usá-la no restante de sua consulta da mesma forma que usa outras tabelas. Exemplo de CTE no PostgreSQL Vamos dar uma olhada em um exemplo. A Alpha Sales é uma varejista on-line. Eles querem saber se sua estratégia de marketing mais recente foi eficaz e que tipo de cliente respondeu melhor a ela. Aqui está uma amostra de sua tabela order_summary que contém o valor de cada pedido feito em abril, maio e junho de 2024. order_idcustomer_idorder_datevalue 112024-06-05700 212024-04-18400 312024-05-15500 422024-04-25200 5882024-05-04700 6882024-06-18500 7882024-05-25150 83452024-04-02250 93452024-06-25450 103452024-06-19300 116572024-05-25900 126572024-06-25200 Como primeira etapa da análise do sucesso de sua campanha de marketing, os líderes da empresa querem comparar as vendas de junho por cliente com a média mensal de vendas por cliente de abril e maio e calcular a variação percentual. É claro que você poderia fazer isso usando subconsultas, mas o código seria bastante complexo. Você deseja ver a média do mês anterior no relatório, mas também usá-la no cálculo da variação percentual. Usando um CTE, a consulta teria a seguinte aparência: WITH april_may_sales AS (SELECT customer_id, SUM(value) / 2 AS prev_avg FROM order_summary WHERE EXTRACT (MONTH FROM order_date) in (4,5) GROUP BY customer_id; ) SELECT order_summary.customer_id, prev_avg, SUM(value) AS jun_total, (SUM(value) - prev_avg) * 100 / prev_avg AS percent_change FROM order_summary JOIN april_may_sales ONapril_may_sales.customer_id = order_summary.customer_id WHERE EXTRACT (MONTH FROM order_date) = 6 GROUP BY order_summary.customer_id, prev_avg ORDER BY customer_id; Essa consulta usa a cláusula WITH para criar uma tabela virtual chamada april_may_sales. Ela extrai o total de vendas por cliente para os meses de abril e maio, divide o resultado por 2 para obter uma média mensal e armazena essas informações em uma coluna chamada prev_avg. Essa tabela é unida à tabela order_summary na consulta principal para que possamos ver o total de junho junto com a média de abril e maio. A consulta produz o seguinte conjunto de resultados: customer_idprev_avgjun_totalpercent_change 1450.00700.0055.56 88425.00500.0017.65 345125.00750.00500.00 657450.00200.00-55.56 Definição de nomes de colunas CTE Opcionalmente, você pode definir especificamente os nomes das colunas para a tabela CTE usando a seguinte sintaxe: WITH cte_name (column_name_list) AS (query_1) query_2; Aqui, column_name_list é uma lista de nomes de colunas separados por vírgulas. Alterar o exemplo anterior para usar essa sintaxe nos dá a seguinte consulta: WITH april_may_sales (customer_id, prev_avg) AS ( SELECT customer_id, SUM(value) /2 FROM order_summary WHERE EXTRACT (MONTH FROM order_date) in (4,5) GROUP BY customer_id ) SELECT order_summary.customer_id, prev_avg, SUM(value) AS jun_total, (SUM(value) - prev_avg) * 100/prev_avg AS percent_change FROM order_summary JOIN april_may_sales ON april_may_sales.customer_id = order_summary.customer_id WHERE EXTRACT (MONTH FROM order_date) = 6 GROUP BY order_summary.customer_id, prev_avg ORDER BY customer_id; Isso não faz diferença na saída da consulta, que permanece igual à consulta de exemplo anterior. No entanto, torna mais fácil para outra pessoa entender sua consulta. Consultas aninhadas usando o CTE do PostgreSQL Você pode definir dois ou mais CTEs usando uma palavra-chave WITH no PostgreSQL. Você simplesmente começa usando a palavra-chave WITH e, em seguida, especifica cada CTE separado por vírgulas. A sintaxe é semelhante a esta: WITH cte_name_1 AS (query_1), cte_name_2 AS (query_2) query_3; Cada CTE tem seu próprio nome e instrução select. Cada CTE pode se referir a qualquer CTE definido anteriormente para obter os dados de que precisa. Observe que você não repete a palavra-chave WITH: você apenas lista os CTEs separados por vírgulas. Vamos ver isso em ação. Suponha que a Alpha Sales agora queira levar essa análise a um estágio mais avançado. Ela gostaria de extrair dados demográficos dos clientes que compraram mais em junho do que a média de compras de abril e maio. Para isso, ela precisa combinar os dados extraídos na consulta anterior com os dados de sua tabela customer tabela. Aqui está uma amostra dos dados: customer_idprev_avgjun_totalpercent_change 1450.00700.0055.56 88425.00500.0017.65 345125.00750.00500.00 657450.00200.00-55.56 Para fazer isso, você pode: Mover a consulta principal anterior para a frente como um CTE aninhado. Isso cria efetivamente uma tabela virtual que contém o customer_id, a média anterior, o total de junho e a alteração percentual. Escrever uma nova consulta principal que junte essa tabela com a tabela customer para calcular a idade do cliente e extrair seu estado. A nova consulta tem a seguinte aparência: WITH april_may_sales AS (SELECT customer_id, SUM(value) / 2 AS prev_avg FROM order_summary WHERE EXTRACT (MONTH FROM order_date) in (4,5) GROUP BY customer_id ), comparison AS ( SELECT order_summary.customer_id, prev_avg, SUM(value) AS jun_total, (SUM(value) - prev_avg) * 100/prev_avg AS percent_change FROM order_summary JOIN april_may_sales ON april_may_sales.customer_id = order_summary.customer_id WHERE EXTRACT (MONTH FROM order_date) = 6 GROUP BY order_summary.customer_id, prev_avg ) SELECT customer.customer_id, name, EXTRACT(YEAR from CURRENT_DATE) - EXTRACT(YEAR from date_of_birth) AS age, state, prev_avg, jun_total, percent_change FROM customer JOIN comparison ON comparison.customer_id = customer.customer_id WHERE percent_change > 0; Como antes, a consulta define o CTE chamado april_may_sales como uma tabela virtual que contém a média de vendas de abril e maio. Em seguida, ela define um novo CTE chamado comparação, que contém uma comparação dos totais de junho por cliente com o conteúdo de april_may_sales. Por fim, a consulta principal combina os dados da tabela virtual comparison com os dados da tabela customer tabela. O conjunto de resultados tem a seguinte aparência: customer_idnameagestateprev_avgjun_totalpercent_change 1John Smith30KY450.00700.0055.56 88Tinashe Mpofu50ID425.00500.0017.65 345Jennifer Perry26HI125.00750.00500.00 CTE do PostgreSQL em Linguagem de Manipulação de Dados Vamos agora dar uma olhada nas instruções de manipulação de dados como INSERT, UPDATE e DELETE. Uma das limitações dos CTEs é que você não pode usá-los diretamente no lugar de um valor em uma instrução UPDATE da mesma forma que você pode fazer com uma subconsulta. Digamos que você queira atualizar o saldo na tabela customer adicionando o valor de todos os pedidos de junho. Com subconsultas comuns, você pode fazer algo assim: UPDATE customer SET balance = balance + (select SUM(value) FROM order_summary WHERE customer.customer_id = order_summary.customer_id AND EXTRACT (MONTH from order_date) = 6); Não é possível fazer isso com um CTE. O que você pode fazer, entretanto, é usar a seguinte sintaxe: WITH cte_name AS (select_statement) UPDATE tablename SET column_name_1 = column_name_2 FROM cte_name WHERE join_clause; cte_name é o nome que você usará para se referir à "tabela" criada pelo CTE. select_statement é a instrução que você usará para preencher o CTE. column_name_1 é o nome da coluna na tabela principal que você deseja atualizar. column_name_2 é o nome da coluna em seu CTE que você usará para definir o novo valor. join_clause especifica a condição que você usará para unir as duas tabelas. A consulta a seguir adiciona o total de pedidos de junho da tabela order_summary ao saldo da tabela customer tabela: WITH june_total AS (SELECT customer_id, SUM(value) AS jun_tot FROM order_summary WHERE EXTRACT(MONTH FROM order_date) = 6 GROUP BY customer_id ) UPDATE customer SET balance = balance + jun_tot FROM june_total WHERE customer.customer_id = june_total.customer_id; Primeiro, a cláusula WITH cria uma pseudotabela chamada june_total. Ela contém totais por customer_id de pedidos em que o mês de order_date é 6. Em seguida, a coluna jun_tot dessa tabela é usada para aumentar o saldo onde customer_id corresponde entre as duas tabelas. A tabela customer agora contém os seguintes dados: customer_idnamedate_of_birthstatebalance 1John Smith5/7/1994KY1000 2Shamila Patel14/3/2006CT1000 88Tinashe Mpofu17/4/1974ID500 345Jennifer Perry21/10/1998HI850 657Sarah Jones25/4/1984KY570 Você também pode usar CTEs para inserir ou excluir linhas da mesma forma. Consultas Recursivas As consultas recursivas são um recurso dos CTEs. Essas consultas permitem que você faça um loop a partir de uma única consulta base para executar repetidamente uma tarefa específica. Elas são especialmente úteis para consultar dados hierárquicos, como estruturas organizacionais e listas de materiais. Uma cobertura completa das consultas recursivas está além do escopo deste artigo. Veremos apenas a sintaxe e um exemplo simples. Para obter mais detalhes, dê uma olhada em What Is a Recursive CTE in SQL (O que é um CTE recursivo em SQL), que fornece uma explicação completa e vários exemplos. A sintaxe das consultas recursivas no PostgreSQL é: WITH RECURSIVE cte_name AS (query_1 UNION query_2) query_3; A palavra-chave RECURSIVE indica que essa é uma consulta recursiva. query_1 é a consulta base, ou ponto de partida. Por exemplo, suponha que você esteja trabalhando com uma estrutura organizacional. Nesse caso, query_1 poderia ser uma consulta que seleciona o gerente de nível superior em um arquivo de funcionários. query_2 é a consulta recursiva. Essa consulta será repetida até que não haja mais linhas que atendam aos critérios especificados no site WHERE. Ela pode fazer referência à última linha adicionada ao conjunto de resultados para coletar dados. Isso pode ser usado para encontrar todos os funcionários que se reportam a um gerente. UNION combina os resultados. Se você usar UNION ALL, as duplicatas serão mantidas; caso contrário, serão omitidas. query_3 é usado para retornar o conjunto de resultados finais. Ele pode fazer referência à tabela virtual criada pelo CTE. Vamos pensar em um exemplo de uma tabela employee em que os registros dos funcionários têm um campo que identifica o gerente ao qual eles se reportam. O que realmente acontece se você usar uma consulta recursiva para navegar nessa hierarquia? Os resultados da consulta de base são adicionados à tabela virtual. A consulta de base extrai o registro do funcionário do CEO. Em seguida, o mecanismo de banco de dados usa essa linha para encontrar todas as linhas que correspondem aos critérios da parte recursiva da consulta. Esses serão todos os funcionários que se reportam diretamente ao gerente de nível superior. Para cada um desses registros, por sua vez, o mecanismo encontrará todos os funcionários que se reportam a essa pessoa. Isso é repetido até que não haja mais funcionários que atendam à condição. Vamos dar uma olhada em um exemplo simples. Uma empresa de consultores de TI tem vários projetos em andamento e sua política é agendar reuniões semanais de progresso para cada projeto. Uma tabela chamada projects contém detalhes de novos projetos. Um exemplo dessa tabela é o seguinte: proj_namestart_dateend_datemeet_daymeet_time Online Shopping2024-05-012024-08-29209:00 Customer Migration2024-04-012024-05-16415:00 A empresa deseja criar detalhes das reuniões agendadas em uma tabela chamada meetingsEssas informações serão usadas para enviar lembretes e reservar um local a cada semana. A coluna meet_day contém o dia da semana em que as reuniões serão agendadas. Ela é armazenada como um número de dia dentro da semana, em que 0 representa domingo. Você poderia fazer isso com a seguinte consulta recursiva: WITH RECURSIVE date_list (proj_name, meet_date, end_date, meet_day, meet_time) AS ( SELECT proj_name, start_date, end_date, meet_day, meet_time FROM projects UNION ALL SELECT proj_name, meet_date + 1, end_date, meet_day, meet_time FROM date_list WHERE meet_date + 1 <= end_date ) INSERT INTO meetings SELECT proj_name, meet_date, meet_time FROM date_list WHERE meet_day = EXTRACT (DOW from meet_date) ORDER BY proj_name, meet_date; Após a execução da consulta, a tabela meetings contém os seguintes dados: proj_namemeet_datemeet_time Customer Migration2024-04-0315:00:00 Customer Migration2024-04-1015:00:00 Customer Migration2024-04-1715:00:00 Customer Migration2024-04-2415:00:00 Customer Migration2024-05-0115:00:00 Customer Migration2024-05-0815:00:00 Customer Migration2024-05-1515:00:00 Online Shopping2024-05-0709:00:00 Online Shopping2024-05-1409:00:00 Online Shopping2024-05-2109:00:00 Online Shopping2024-05-2809:00:00 Online Shopping2024-06-0409:00:00 Online Shopping2024-06-1109:00:00 Online Shopping2024-06-1809:00:00 Online Shopping2024-06-2509:00:00 Online Shopping2024-07-0209:00:00 Online Shopping2024-07-0909:00:00 Online Shopping2024-07-1609:00:00 Online Shopping2024-07-2309:00:00 Online Shopping2024-07-3009:00:00 Online Shopping2024-08-0609:00:00 Online Shopping2024-08-1309:00:00 Online Shopping2024-08-2009:00:00 Online Shopping2024-08-2709:00:00 Vamos dividir a consulta em partes e ver o que ela realmente faz. Primeiro, ela define as colunas que serão incluídas no CTE date_list: WITH RECURSIVE date_list (proj_name, meet_date, end_date, meet_day, meet_time) Em seguida, ela estabelece os dados de base para a recursão, que é o conteúdo da tabela de projetos: AS ( SELECT proj_name, start_date, end_date, meet_day, meet_time from projects Em seguida, especifica quais dados devem ser incluídos em cada recursão, com uma condição que garante que a recursão termine quando concluída: UNION ALL SELECT proj_name, meet_date + 1, end_date, meet_day, meet_time FROM date_list WHERE meet_date + 1 <= end_date Por fim, a consulta principal insere os resultados mantidos na tabela virtual na tabela meetings. Isso parece útil? Você pode aprender mais sobre consultas recursivas e praticar alguns exemplos do mundo real se fizer nosso curso on-line CTE no PostgreSQL. Saiba mais sobre os CTEs do PostgreSQL Embora os CTEs no PostgreSQL possam não melhorar o desempenho de suas consultas, eles certamente tornam as consultas complexas mais fáceis de escrever e de entender. Ao dividir uma consulta longa em partes componentes, você pode organizar seus pensamentos e manter a codificação simples. Os CTEs também facilitam o trabalho com estruturas hierárquicas usando a cláusula RECURSIVE. Este artigo usa especificamente a sintaxe e os exemplos do PostgreSQL, mas os CTEs funcionam da mesma forma em outros dialetos SQL, como o MS SQL Server. Se você quiser se familiarizar com o uso de CTEs, o curso Common Table Expressions in PostgreSQL do LearnSQL tem mais de 100 exercícios práticos que o ajudarão a realmente entender essa ferramenta. Se você quiser praticar um pouco mais, experimente estes 11 exercícios gratuitos sobre expressões de tabelas comuns do SQL. Cada exercício apresenta o tipo de desafio que você enfrentaria no mundo real, e as soluções e explicações estão incluídas. E se estiver se preparando para uma entrevista, aqui estão alguns exemplos de perguntas e respostas sobre CTE para entrevistas. Espero que este artigo tenha lhe dado uma boa ideia do que o CTE do PostgreSQL pode fazer por você. Se você quiser aprender alguns outros conceitos avançados do PostgreSQL, este artigo é um bom lugar para começar. Agora é com você! Lembre-se de que a prática leva à perfeição, portanto, confira nossa trilha de aprendizado SQL Avançado Practice para praticar mais os recursos avançados de SQL! Tags: postgresql cte