3rd Oct 2024 14 minutos de leitura O CTE do MySQL e como usá-lo Gustavo du Mortier MySQL cte Índice Sintaxe básica de um CTE do MySQL Um CTE no MySQL que retorna um único valor CTE com funções de agrupamento e agregação CTEs múltiplos e aninhados Uso de vários CTEs em uma única consulta Aninhamento de CTEs Uso de CTEs do MySQL com outros comandos DML Uso de um CTE com INSERT Uso de um CTE com UPDATE CTEs recursivos no MySQL CTEs do MySQL em poucas palavras As expressões comuns de tabela (CTEs) no MySQL proporcionam ainda mais versatilidade a esse popular sistema de gerenciamento de banco de dados. Descubra como usar CTEs para simplificar consultas complexas e implementar recursão. Todas as linguagens de programação têm maneiras de simplificar os problemas, dividindo-os em partes que podem ser resolvidas individualmente. Os usuários podem unificar os resultados parciais para obter um único resultado final. Graças a algo chamado expressões de tabela comuns (CTEs), o MySQL agora é uma exceção. Um CTE do MySQL permite que você atribua um nome a um conjunto de resultados temporários e, em seguida, faça referência a esse conjunto de resultados pelo nome (como se fosse uma tabela ou uma visualização) nas instruções SELECT, INSERT, UPDATE, ou DELETE. Os CTEs fazem parte do padrão SQL desde 1999. Entretanto, sua implementação efetiva nos dialetos de cada sistema de gerenciamento de banco de dados relacional (RDBMS) ocorreu gradualmente, à medida que os usuários começaram a considerar esse novo recurso útil. O MySQL foi um dos últimos RDBMSs populares a incorporar o suporte a CTEs. Foi somente na versão 8, lançada em abril de 2018, que o suporte a CTEs apareceu no MySQL. Como veremos a seguir, um dos principais usos do CTE do MySQL é implementar a recursão. Você pode fazer nosso curso interativo sobre consultas recursivas no MySQL se quiser ir direto para esse assunto. Neste curso, você pode obter todo o conhecimento necessário para dominar as consultas recursivas no MySQL 8. Tudo o que você precisa é de um navegador da Web, uma conexão com a Internet e conhecimento de SQL básico (incluindo as cláusulas JOIN e GROUP BY ). Há 114 exercícios interativos com dicas e ajuda on-line para que você possa realmente aprimorar suas habilidades. Por enquanto, porém, vamos nos concentrar nos conceitos básicos dos CTEs do MySQL, começando com a sintaxe mais simples. Sintaxe básica de um CTE do MySQL A sintaxe para escrever um CTE do MySQL começa com a palavra WITH, seguida pelo nome do CTE e uma lista das colunas que o CTE retornará. Essa lista não é obrigatória, pois pode ser deixada para ser definida pelo resultado da consulta que define o CTE. Essa consulta deve ser escrita entre parênteses após a palavra AS. A sintaxe básica das expressões de tabela comum no MySQL pode ser resumida da seguinte forma: WITH cte_name (column_list) AS ( query ) SELECT * FROM cte_name; Após o parêntese que envolve a consulta CTE, temos uma instrução SQL convencional que se refere ao CTE por seu nome, como se fosse uma visualização. A instrução pode ser um comando DML (Data Manipulation Language, Linguagem de Manipulação de Dados), como SELECT, INSERT, DELETE, ou UPDATE. Se você quiser um guia de referência rápida para a sintaxe ou os comandos do MySQL, marque nossa MySQL Cheat Sheet gratuita. Ela o ajudará a refrescar sua memória sobre frases comuns (e não tão comuns) do MySQL. Um CTE no MySQL que retorna um único valor Vamos ver um exemplo de um CTE criado em um banco de dados MySQL. Usaremos informações de vendas e mostraremos a diferença entre o valor de cada venda e o valor médio da venda. Temos uma tabela sales com informações detalhadas sobre vendas. Para este exemplo, estamos interessados em três colunas: customer_id sale_id e amount. Aqui estão alguns dados de amostra: customer_idsale_idamount 800340121507995294.45 7900552315079981045.12 48102066150800112.57 492300211508022499.14 Criaremos um CTE chamado sales_average que retornará um único valor: a média de todos os valores da coluna amount da tabela sales da tabela (ou seja, a média geral de todos os valores de venda). No SELECT externo - aquele que invoca o CTE - usaremos o valor retornado pelo CTE para calcular a diferença entre o valor de cada venda e a média geral. Isso nos permitirá identificar as vendas mais significativas: WITH sales_average AS ( SELECT AVG(amount) AS amount_avg FROM sales ) SELECT customer_id, sale_id, amount, amount - amount_avg as difference FROM sales, sales_average; Quando o MySQL encontra uma consulta como a acima, ele primeiro executa a consulta na cláusula WITH e salva seus resultados temporariamente sob o nome do CTE (sales_average). Em seguida, ele executa o SELECT externo usando sales_average como uma tabela comum; isso produz o conjunto de resultados finais. A execução de toda a consulta com os dados de amostra detalhados acima retorna os seguintes resultados: customer_idsale_idamountdifference 800340121507995294.45-168.7500 7900552315079981045.12582.2500 48102066150800112.57-449.7500 492300211508022499.1436.2500 CTE com funções de agrupamento e agregação Vejamos um exemplo um pouco mais complexo: criaremos uma consulta que usa um CTE para encontrar as chamadas mais antigas em uma central de atendimento. Temos uma tabela chamada calls que inclui caller_id e call_time de cada chamada (entre outros dados, como a duração da chamada, que não incluiremos aqui). Estas são algumas linhas da tabela calls tabela: caller_idcall_time 1-555-9784-45162024-08-11 08:45:23.124 1-549-995-04472024-08-11 17:02:55.045 1-555-9784-45162024-08-12 09:22:14.341 1-549-995-04472024-08-13 11:36:38.229 1-599-1008-99982024-08-14 13:22:59.003 Nosso CTE, chamado first_contact, retornará a data e a hora da chamada mais antiga de cada chamador. No site externo SELECT, juntamos o CTE com a tabela calls para retornar todos os dados de cada chamada mais antiga. Neste exemplo (diferentemente do anterior), detalharemos os nomes das colunas retornadas pelo CTE em sua declaração: WITH first_contact (caller_id, first_call_time) AS ( SELECT caller_id, MIN(call_time) AS first_call_time FROM calls GROUP BY caller_id ) SELECT c.* FROM calls AS c INNER JOIN first_contact AS fc ON fc.caller_id = c.caller_id AND fc.first_call_time = c.call_time; Para executar a consulta acima, o MySQL primeiro obterá os resultados da subconsulta com o nome first_contact. Isso armazenará temporariamente todos os valores distintos de caller_id junto com o call_time mais antigo de cada um. Em seguida, o site externo SELECT juntará os resultados temporários da subconsulta anterior com a tabela calls produzindo todos os dados da chamada mais antiga de cada ID de chamador. Esses são os resultados da consulta: caller_idcall_timeduration 1-555-9784-45162024-08-11 08:45:2315 1-549-995-04472024-08-11 09:02:55129 1-599-1008-99982024-08-14 13:22:5926 Se estiver aprendendo sobre os CTEs do MySQL para se candidatar a uma entrevista de emprego, leia esta compilação de perguntas de entrevista sobre CTEs do SQL para ver o que terá de enfrentar. CTEs múltiplos e aninhados No MySQL, uma única instrução SQL pode conter vários CTEs. Independentemente de quantos CTEs você definir em uma consulta, a palavra WITH é usada apenas uma vez no início da instrução; as definições de CTE são separadas por vírgulas. Uso de vários CTEs em uma única consulta No exemplo a seguir, temos uma tabela chamada customers que armazena a área em que cada cliente está localizado: customer_idarea 80034012WEST 79005523EAST 48102066CENTER 49230021WEST Suponha que estejamos interessados em usar as informações dessa tabela em conjunto com a tabela sales tabela. Especificamente, queremos obter os totais de vendas dos clientes que pertencem às áreas "WEST" e "EAST". Para isso, definiremos dois CTEs que nos permitirão filtrar somente os clientes das áreas em que estamos interessados. Em seguida, na declaração externa SELECT, combinaremos os dados dos dois CTEs com a tabela sales para obter as médias de vendas de cada uma dessas áreas: WITH customers_west AS ( SELECT * FROM customers WHERE area = 'WEST'), customers_east AS ( SELECT * FROM customers WHERE area = 'EAST') SELECT cw.area, AVG(sw.amount) AS amount_avg FROM customers_west AS cw INNER JOIN sales sw ON sw.customer_id = cw.customer_id UNION SELECT ce.area, AVG(se.amount) FROM customers_east AS ce INNER JOIN sales se ON se.customer_id = ce.customer_id; E aqui está o resultado: areaamount_avg WEST396.5 EAST1045 Aninhamento de CTEs O MySQL também nos permite aninhar CTEs para que um CTE possa fazer referência a um CTE definido anteriormente - tudo em uma única instrução SQL. A técnica de aninhamento de CTEs nos permite dividir consultas grandes e complexas em subconsultas menores e mais gerenciáveis. Isso nos permite abordar gradualmente a solução de um problema, reduzindo a etapa final a um simples SELECT. No exemplo a seguir, usamos dois CTEs aninhados para obter o total de vendas agrupadas por país e região. A tabela sales A tabela inclui uma coluna de país que indica o país onde cada venda foi feita. customer_idsale_idamountcountry 800340121507995294.45United States 7900552315079981045.12Germany 48102066150800112.57Spain 492300211508022499.14Ireland A tabela countries tabela inclui uma coluna de região que indica a região geográfica à qual cada país pertence: countryregion United StatesNorth America GermanyEurope SpainEurope MexicoCentral America Para obter os totais de vendas por região, usamos dois CTEs que executam uma abordagem passo a passo para a solução final: WITH sales_by_country AS ( SELECT country, SUM(amount) AS total_sales_by_country FROM sales group BY country ), sales_by_region AS ( SELECT c.region, SUM(s.total_sales_by_country) AS total_sales_by_region FROM sales_by_country s INNER JOIN countries c ON c.country = s.country GROUP BY c.region ) SELECT * FROM sales_by_region; Na consulta acima, primeiro definimos um CTE chamado sales_by_country que agrupa os dados por país e retorna o total de vendas de cada país. Em seguida, definimos um segundo CTE chamado sales_by_region que une o CTE sales_by_country à tabela de países e agrupa os totais de vendas por região. Por fim, toda a consulta é resolvida com um simples SELECT do CTE sales_by_region. Uso de CTEs do MySQL com outros comandos DML Os exemplos anteriores usaram CTEs para resolver consultas SELECT complexas, reduzindo sua complexidade para, por fim, resolvê-las com um simples SELECT. Mas os CTEs também podem ser usados com outros comandos. Nesta seção, veremos como usar CTEs nos comandos INSERT e UPDATE. Uso de um CTE com INSERT Suponhamos que tenhamos uma tabela employees com as colunas empl_id (INT), empl_name (VARCHAR) e salary (DECIMAL). Em seguida, temos uma tabela applicants com as mesmas colunas que employees mais a coluna aptitude (TINYINT) que indica se um candidato foi aprovado no teste de aptidão (aptitude = 1) ou não (aptitude = 0). Somente os candidatos aprovados no teste estão qualificados para se tornarem funcionários. Periodicamente, as linhas da tabela applicants que têm o valor 1 na coluna aptitude devem ser inseridas na tabela. employees tabela. Para isso, usaremos um CTE chamado qualified_applicants para filtrar os candidatos aprovados no teste de aptidão, mas que ainda não se tornaram funcionários. Os dados retornados pelo CTE qualified_applicants são os dados de origem para o INSERT que os incorpora à tabela. employees tabela. INSERT INTO employees (empl_id, empl_name, salary) WITH qualified_applicants (empl_id, empl_name, salary) AS ( SELECT empl_id, empl_name, salary FROM applicants AS a WHERE a.aptitude = 1 AND NOT EXISTS (SELECT * FROM employees AS e WHERE e.empl_id = a.empl_id) ) SELECT empl_id, empl_name, salary FROM qualified_applicants; Quando um CTE é usado em combinação com um INSERT, todo o código do CTE, desde a cláusula WITH até o SELECT externo, é escrito após a linha INSERT INTO table (column1, column2, ...) linha. Para realizar essa inserção, o MySQL primeiro executará toda a instrução CTE e depois inserirá os resultados na tabela especificada. O bom da instrução acima é que ela pode ser executada repetidamente sem medo de criar dados duplicados ou violar uma chave primária. Isso ocorre porque a condição WHERE na definição do CTE inclui uma cláusula que impede que o INSERT tente reinserir dados que já estejam na tabela employees tabela. Uso de um CTE com UPDATE Assim como usamos um CTE do MySQL para inserir linhas em uma tabela, também podemos usar um CTE para atualizar a tabela com novas informações. No exemplo a seguir, veremos como usar um CTE em conjunto com um comando UPDATE. Usaremos a mesma employees tabela e uma nova tabela salaries tabela com as colunas empl_id (INT) e salary (DECIMAL). Essa tabela armazena os salários atualizados de cada funcionário. O CTE retornará as linhas de salaries nas quais o salário do funcionário é maior do que na tabela employees tabela para o mesmo funcionário. Aqui está a consulta completa: WITH raised_salaries (empl_id, salary) AS ( SELECT s.empl_id, s.salary FROM salaries s INNER JOIN employees e ON e.empl_id = s.empl_id WHERE s.salary > e.salary ) UPDATE employees e INNER JOIN raised_salaries rs ON rs.empl_id = e.empl_id SET e.salary = rs.salary; A sintaxe necessária para atualizar os dados de um CTE segue a forma geral do comando MySQL UPDATE JOIN. Quando usado junto com um CTE, o comando UPDATE JOIN deve ser usado como um substituto para o SELECT externo do CTE. O nome do CTE é colocado na cláusula JOIN para combiná-lo com a tabela a ser atualizada. Obviamente, não é possível atualizar os campos do CTE - você só pode atualizar os campos da(s) tabela(s) unidas ao CTE. Como a instrução INSERT que usamos anteriormente, essa combinação de CTE do MySQL e instrução UPDATE pode ser executada repetidamente sem alterar uma única letra. Cada vez que for executada, ela atualizará somente as linhas de employees com um salário menor do que o declarado na tabela salaries tabela. CTEs recursivos no MySQL Além de simplificar e esclarecer consultas complexas, os CTEs possibilitam a implementação de recursão no MySQL. Ao poder resolver a recursão usando apenas instruções SQL, evitamos a necessidade de empregar outras linguagens de programação. Isso nos permite escrever consultas mais eficientes. Os CTEs recursivos são compostos pelos seguintes elementos: Um membro âncora que fornece o(s) valor(es) inicial(is) para a sequência recursiva. Um membro recursivo que obtém os resultados das iterações anteriores e adiciona novas informações a eles. O(s) membro(s) âncora e o(s) membro(s) recursivo(s) são combinados pelas cláusulas UNION. Uma condição de término, que é uma cláusula WHERE (anexada ao membro recursivo) que define a condição que determinará o fim do ciclo recursivo. Uma invocação, ou o SELECT externo que se refere ao CTE recursivo pelo nome (como qualquer outro CTE). Você encontrará informações mais detalhadas neste artigo sobre CTEs recursivos. Um uso típico de funções recursivas na programação é a geração de números de Fibonacci até um determinado valor. Os números de Fibonacci são usados em áreas tão variadas quanto o desenvolvimento de algoritmos de pesquisa e a simulação do crescimento populacional. No seguinte CTE recursivo do MySQL, obtemos os 10 primeiros números da sequência de Fibonacci: WITH RECURSIVE cte_fib AS ( SELECT 1 counter, CAST(0 AS decimal) fibo, CAST(0 AS decimal) prev UNION ALL SELECT 2, 1, 0 UNION ALL SELECT counter + 1, fibo + prev, fibo FROM cte_fib WHERE counter < 10 AND fibo > 0 ) SELECT counter, fibo FROM cte_fib ORDER BY counter; No exemplo acima, o membro âncora é formado pelos dois primeiros membros do UNION, porque eles fornecem os elementos iniciais da série de Fibonacci (as duas primeiras linhas nos dados resultantes): SELECT 1 counter, CAST(0 AS decimal) fibo, CAST(0 AS decimal) prev UNION ALL SELECT 2, 1, 0 O membro recursivo é a terceira parte do UNION, porque adiciona uma linha aos dados retornados pela iteração anterior do mesmo CTE: SELECT counter + 1, fibo + prev, fibo FROM cte_fib WHERE counter < 10 AND fibo > 0 E a condição de término é a cláusula WHERE entre parênteses: WHERE counter < 10 AND fibo > 0 Ela afirma que a recursão deve parar quando o contador atingir o valor de 10. E a invocação é o SELECT fora dos parênteses, que retorna o contador e o número de Fibonacci para cada linha retornada pelo CTE. Há muitos outros usos de CTEs recursivos no MySQL. Por exemplo, eles podem ser usados para percorrer tabelas com informações ordenadas hierarquicamente (por exemplo, organogramas) ou estruturas de dados de árvores ou gráficos. Eles podem até ser usados para tarefas SQL não convencionais, como desenhar uma árvore de Natal com os resultados de uma consulta. CTEs do MySQL em poucas palavras Ao longo deste artigo, fizemos um tour pelos CTEs no MySQL. Aprendemos que as expressões de tabela comuns: Simplificam consultas complexas, dividindo-as e organizando-as em partes menores e mais fáceis de entender. Isso também melhora a legibilidade das consultas. Nos ajudam a escrever códigos mais eficientes. Uma subconsulta definida como um CTE pode ser reutilizada em diferentes partes da mesma consulta - até mesmo em outras subconsultas - sem a necessidade de repeti-la. Encapsulam a lógica sem criar objetos desnecessários. Os CTEs encapsulam a lógica das subconsultas em um nome, assim como fazem as exibições. Mas, diferentemente das exibições, eles não implicam a criação de objetos permanentes no banco de dados. Implementar recursão. Esse é um dos principais motivos da popularidade dos CTEs, pois sem eles é bastante complicado implementar recursão no MySQL. Até agora, você deve ter uma ideia do que pode ser feito com CTEs recursivos no MySQL. Depois de ler este artigo, você estará pronto para solidificar seu conhecimento por meio de alguns exercícios práticos sobre CTEs. Mas o que você viu neste artigo é apenas a ponta do iceberg. Para descobrir todo o potencial dos CTEs no MySQL, considere fazer o curso LearnSQL.com.br Consultas Recursivas no MySQL. Com seus 114 exercícios interativos, esse curso fornecerá todo o conhecimento necessário para dominar as consultas recursivas no MySQL 8. Você nem precisa ter acesso a um servidor MySQL; o ambiente do curso fornecerá todas as ferramentas necessárias. Tudo o que você precisa é de um navegador da Web, uma conexão com a Internet e conhecimento de SQL básico. Você pode até fazer o teste introdutório gratuitamente para ter certeza de que o curso atende às suas necessidades! Concluindo, os CTEs são uma ferramenta poderosa no MySQL, especialmente quando a organização e a legibilidade do código SQL são valorizadas. Em equipes multidisciplinares, onde engenheiros de banco de dados SQL experientes colaboram com analistas de dados, designers de banco de dados e DBAs, os CTEs são um recurso valioso para aumentar a produtividade da equipe e obter um software mais sustentável e durável. Tags: MySQL cte