Você está na página 1de 14

Uma subconsulta (ou mais conhecida, subquery) é uma instrução

SELECT que está condicionada dentro de outra instrução SQL. Como


resultado desta operação, podemos fazer uso de subconsultas para
criarmos consultas que seriam difíceis ou impossíveis de serem feitas
utilizando outras maneiras. Desde que saibamos codificar nossas
instruções SELECT, saberemos como codificar uma subconsulta, já
que ela é apenas uma instrução SQL no interior de outra instrução SQL.
O que precisamos saber nesse momento para utilizarmos as
subqueries é onde e quando devemos utilizá-las. Vamos então
aprender sobre algumas das especificidades de utilizarmos
subconsultas ao longo deste artigo.

Utilizando subqueries

A utilização de subqueries é bastante simples, pois o que precisamos


realmente é ter cuidado aonde devemos utilizá-las. Neste caso,
afirmamos que nossas subqueries podem ser codificadas ou mesmo
introduzidas em cláusulas WHERE, HAVING, FROM, ou mesmo SELECT
de uma outra instrução SELECT. Essa instrução, por exemplo, pode
então usar uma subconsulta na condição de pesquisa de uma cláusula
WHERE. Quando ela é utilizada em uma condição de pesquisa, a
subconsulta pode ser referida como sendo uma condição de pesquisa
ou uma subconsulta predicado. Vejamos um pequeno exemplo de sua
utilização, de acordo com a Listagem 1.

Listagem 1. Exemplo de uso de subconsultas na cláusula SELECT.

SELECT DISTINCT NomeFornecedor,


(SELECT MAX(DataFatura) FROM Faturas
WHERE Faturas.Id_fornecedor = Fornecedores.Id_fornecedor) AS
UltimaFatura
FROM Fornecedores
ORDER BY UltimaFatura DESC;

No exemplo que apresentamos, mostramos como usar


subconsultas(subqueries) na cláusula SELECT. Como podemos
observar, nós podemos usar uma subconsulta no lugar de uma
especificação de coluna, por isso, uma subconsulta deve retornar um
único valor. Na maioria dos casos, as subconsultas que usamos na
cláusula SELECT serão subconsultas correlacionadas, estas são
tipicamente ineficientes. Como mostrado pela Listagem 1, o exemplo
é para calcular a data máxima das faturas para cada fornecedor na
tabela de fornecedores, neste caso, ele se refere à coluna
Id_fornecedor presente na tabela de Faturas numa consulta externa.

É preciso termos cuidado com a utilização de subqueries em cláusulas


do tipo SELECT, pois elas, às vezes, são difíceis de ler. Na maioria dos
casos, porém, podemos substituir uma subconsulta por um JOIN para
facilitar mais as coisas. A consulta mostrada primeiramente
pela Listagem 1 poderia ser reapresentada como mostrado pelo
código apresentado pela Listagem 2. Esta consulta então une as
tabelas de Fornecedores e Faturas, agrupando as linhas por
NomeFornecedor e, em seguida, usa a função MAX para calcular a
data máxima da fatura para cada fornecedor. Como podemos
observar, essa consulta é muito mais fácil de ser lida do que aquela
com a subconsulta, além de que, esta é executada muito mais
rapidamente, já que ela faz a busca uma única vez, enquanto que
utilizando subconsultas, é feita uma varredura para cada linha em
execução consumindo mais recursos da aplicação.

Listagem 2. Consulta de faturas utilizando JOIN.

SELECT NomeFornecedor, MAX(DataFatura) AS UltimaFatura


FROM
Fornecedores LEFT JOIN Faturas ON Faturas.Id_fornecedor =
Fornecedores.Id_fornecedor
GROUP BY NomeFornecedor
ORDER BY UltimaFatura DESC;

Quando uma subconsulta nos retorna um único valor, podemos usá-la


em qualquer lugar no qual poderíamos usar normalmente uma
expressão. No entanto, uma subconsulta também pode retornar um
conjunto de resultados de coluna única com duas ou mais linhas.
Nesse caso, ela pode ser usada no lugar de uma lista de valores, tais
como a lista para um operador IN. Além disso, se uma subconsulta é
codificada dentro de uma cláusula FROM, esta pode retornar um
conjunto de resultados com duas ou mais colunas. Iremos aprender
sobre todos esses tipos diferentes de subqueries neste artigo.

Além disso, também podemos codificar uma subconsulta dentro de


outra subconsulta. Nesse caso, as subconsultas são ditas como sendo
subconsultas aninhadas, mas elas podem ser de difícil leitura e
também resultar em um baixo desempenho, por isso, devemos usá-las
somente quando necessário. Existem, no entanto, quatro maneiras de
introduzirmos uma subconsulta em uma instrução do tipo SELECT,
que são:

• Através de uma cláusula WHERE como sendo uma condição de


pesquisa;
• Em uma cláusula HAVING como condição de pesquisa;
• Na cláusula FROM como uma especificação de tabela;
• Na cláusula SELECT como uma especificação de coluna.

O que podemos dizer sobre as subqueries é que elas podem ser usadas
para a realização de pesquisas compostas, onde uma informação da
qual estamos precisando depende de uma ou mais informações vindas
com base em outras respostas. Para esse tipo de consulta, duas ou
mais consultas precisam ser executadas, onde, a primeira consulta
que irá localizar a informação desejada, mas que é na segunda
consulta que irá encontrar a informação que será utilizada para
comparação, e dessa forma, realizarmos o cálculo do resultado desta
comparação, nos trazendo assim a informação requerida. A utilização
de subconsultas é uma abordagem que nos fornece grande capacidade
de incorporarmos consultas umas nas outras. Vejamos como é a
sintaxe de acordo com a Listagem 3.

Listagem 3. Sintaxe da subquery padrão.

SELECT <columnA, columnB, ..., columnN>


FROM <table>
WHERE expression operator
(SELECT <columnA, columnB, ..., columnN>
FROM <table>
WHERE <condition>
)

Como podemos perceber, a sintaxe apresentada no código anterior é a


estrutura base em mais de uma consulta, onde uma se torna um termo
comparativo, enquanto que a outra nos traz a informação a ser
utilizada na comparação. Temos então algumas regras que precisamos
entender para o correto funcionamento dessas consultas:

• Toda subquery deve ser colocada entre parênteses;


• As subqueries precisam ser colocadas do lado direito do
operador de comparação;
• As subqueries não podem conter cláusulas de ORDERBY;
• As subqueries podem conter mais de uma subquery.

Existem três tipos de subqueries:

1. A mais simples é a subconsulta de uma única linha, também


conhecida como subquery single-row;
2. A subquery de várias linhas, conhecida como subquery
multiple-row;
3. A subquery de múltiplas colunas, conhecida como subquery
multiple columns.

A seguir apresentaremos alguns exemplos de utilização dessas


subquerys e de alguns métodos que também são empregados em
outras linguagens de banco de dados, como podemos citar o PL/SQL, da
Java.

Para os exemplos utilizaremos a base de dados de testes oferecida pela


Microsoft, que é a AdventureWorks2012_database e o SQL SERVER
2014 para realização das consultas. Os links para download estarão
presentes no fim do artigo caso tenham interesse em ter esse material
em mãos para acompanhar e aprimorar os seus conhecimentos.

Subquery de linha única (subquery single-


row)

Subconsultas de linha única podem nos retornar zero ou uma linha


para a instrução SQL exterior. Podemos então aplicar uma
subconsulta em uma cláusula WHERE, ou uma cláusula HAVING, ou
uma cláusula FROM presente numa instrução SELECT. Neste tipo de
consulta podemos utilizar os operadores necessários para retornar as
informações solicitadas em uma única linha. Como por exemplo,
poderíamos fazer a seguinte declaração para trazermos as
informações referentes a um determinado produto, como mostra o
código presente na Listagem 4.

Listagem 4. Consulta de linha única com a clausula WHERE.


SELECT
[ProductID]
,[Name]
,[ProductNumber]
,[Color]
FROM [AdventureWorks2012].[Production].[Product]
WHERE [ProductID] = (
SELECT [ProductID]
FROM [AdventureWorks2012].[Production].[Product]
WHERE [Name] = 'Blade'
)

De acordo com o exemplo apresentado pela Listagem 4, recuperamos


algumas informações referentes ao produto, fazendo uma busca a
mais na mesma tabela para trazer os dados de um registro específico,
que seria o número de identificação do produto, mas sendo feita a
pesquisa na segunda instrução a partir do nome do produto, que no
nosso caso foi [name] = ‘Blade’. Veremos agora o que acontece nela,
separando-a em duas partes: apresentaremos a primeira parte de
acordo com o código da Listagem 5.

Listagem 5. Primeira parte da instrução utilizando WHERE.

SELECT [ProductID]
FROM [AdventureWorks2012].[Production].[Product]
WHERE [Name] = 'Blade'

A subconsulta apresentada anteriormente é executada primeiro e


após obter o resultado, ela retorna o id do produto para a linha cujo
[Name] é ‘Blade’. O [ProductID] para esta linha é 316, o que é então
passado para a cláusula WHERE da consulta externa (nossa consulta
principal), a qual vemos no código da Listagem 6.

Listagem 6. Instrução principal da consulta.

SELECT
[ProductID]
,[Name]
,[ProductNumber]
,[Color]
FROM [AdventureWorks2012].[Production].[Product]
WHERE [ProductID] = 316
Utilizando operadores de comparação em
subqueries de linha única

No exemplo que apresentamos na Listagem 4 fizemos uso do


operador de igualdade (=) na cláusula WHERE. Além dele, outros
operadores de comparação podem ser utilizados com uma única
subconsulta, tais como o <>, > (Maior que), < (Menor que), >= (Maior
ou igual) e o <= (Menor ou igual). No exemplo que iremos apresentar
na Listagem 7 utilizaremos o operador de menor que na cláusula
WHERE da consulta externa. Nela usaremos a função AVG() que será
usada na subconsulta para obtermos a quantidade média das ordens e
que após isso, será passada para a cláusula WHERE da consulta
externa. O resultado final de toda a consulta é para obtermos os
campos [PurchaseOrderID], [DueDate], [OrderQty] e o [UnitPrice].

Listagem 7. Quantidade média de ordens.

SELECT [PurchaseOrderID]
,[DueDate]
,[OrderQty]
,[UnitPrice]
FROM [AdventureWorks2012].[Purchasing].[PurchaseOrderDetail]
WHERE [PurchaseOrderID] > (
SELECT AVG([PurchaseOrderID])
FROM [AdventureWorks2012].[Purchasing].[PurchaseOrderDetail]
WHERE [DueDate] = '2007-10-31'
)

Se isolarmos a consulta secundária e executarmos, obteremos como


resposta para a média do número de ordens o valor de 915, e com essa
informação sendo passada para a consulta principal, obtemos todas as
ordens com o [PurchaseOrderID] > 915.

Utilizando subqueries com cláusula


HAVING
A cláusula HAVING é usada para filtrar grupos de linhas de registros,
o que nos permite filtrarmos grupos de linhas com base no resultado
retornado por uma subconsulta. Vejamos de forma mais prática esta
questão de acordo com o código presente na Listagem 8.

Listagem 8. Utilizando a cláusula HAVING.

SELECT AVG([PurchaseOrderID])
,[VendorID]
,COUNT([VendorID])
FROM [AdventureWorks2012].[Purchasing].[PurchaseOrderHeader]
GROUP BY [VendorID]
HAVING AVG([PurchaseOrderID]) =
(SELECT AVG([PurchaseOrderID])
FROM [AdventureWorks2012].[Purchasing].[PurchaseOrderHeader]
where VendorID = 1690
)

Subqueries em uma cláusula FROM

Além das cláusulas HAVING e WHERE, também podemos criar


subconsultas com a cláusula FROM de uma consulta externa, vejamos
isso de acordo com o exemplo apresentado pela Listagem 9.

Listagem 9. Subconsulta com a cláusula FROM.

SELECT [ProductID]
FROM
(SELECT [ProductID]
FROM [AdventureWorks2012].[Production].[Product]
WHERE [ProductID] < 320) as TESTE

Na subconsulta apresentada no código anterior estamos interessados


apenas em trazer os IDs dos produtos que sejam menores que o id do
produto especificado, que no nosso caso, foi o [ProductID] = 320. Em
alguns momentos podemos ter problemas com a utilização dessas
subconsultas e por isso, termos o desprazer de receber notificações de
erros.

Aqui, estaremos vendo alguns erros que podemos enfrentar em uma


operação de simple row subquery. Nos nossos exemplos anteriores
vimos uma subconsulta que nos retorna sempre uma única linha, mas
se uma subconsulta nos retornar mais de uma linha, ocorrerá um
erro. No exemplo apresentado pela Listagem 10, tentaremos passar
uma subconsulta com várias linhas para o operador de igualdade (=),
presente na consulta externa.

Listagem 10. Tentativa de múltiplas linhas numa subquery de linha


única.

SELECT [ProductID]
,[Name]
FROM [AdventureWorks2012].[Production].[Product]
WHERE [ProductID] = (
SELECT ProductID
FROM [AdventureWorks2012].[Production].[Product]
WHERE [Name] LIKE '%a%'
)

Ao executarmos esta consulta no banco de dados, teremos um erro


com a mesma informação do código abaixo:

Msg 512, Level 16, State 1, Line 1


Subquery returned more than 1 value. This is not permitted when the
subquery follows =, !=, <, <= , >, >= or when the subquery is used as an
expression.

Como podemos observar, a informação que é apresentada é de que a


subquery tentou retornar mais que um valor e isto não é permitido
neste tipo de subquery. Veremos então quando isso é possível.

Utilizando subqueries de múltiplas linhas


(subquery multiple row)

Quando precisamos realizar consultas onde o resultado seja a


apresentação de várias linhas, podemos utilizar os operadores IN,
ANY ou ALL nas nossas consultas externas para lidar com uma
subconsulta que retorna várias linhas.

No caso do operador IN, ele é utilizado para verificar um valor dentro


de um conjunto de valores, onde esta lista de valores pode vir dos
resultados de uma subconsulta. Vejamos então de acordo com
a Listagem 11 como seria a sua utilização.

Listagem 11. Utilizando o operador IN.

SELECT [PurchaseOrderID]
,[VendorID]
,[OrderDate]
,[TotalDue]
FROM [AdventureWorks2012].[Purchasing].[PurchaseOrderHeader]
WHERE [VendorID] IN (
SELECT [VendorID] FROM
[AdventureWorks2012].[Purchasing].[PurchaseOrderHeader]
WHERE [EmployeeID] = 255

Percebam que utilizamos o operador IN para trazer todos os


fornecedores (instrução principal) com base na instrução secundária
que era a obtenção desse resultado para um determinado
empreendedor. Utilizando o operador NOT IN, teríamos o mesmo
resultado, com a diferença de que seriam retornados os fornecedores
de todos os empreendedores, menos para o que possui o id
[EmployeeID] = 255.

Utilizando o operador ANY com uma


subconsulta Multiple Row

No caso de utilizarmos o operador ANY, ele não restringe os


resultados numa lista, ele nos traz qualquer coisa, precisando colocar
um operador do tipo =, <>,>, <, <= ou >= antes de ANY na nossa
consulta. Apresentaremos um exemplo com o código da Listagem 12.

Listagem 12. Utilizando o operador ANY.

SELECT [PurchaseOrderID]
,[VendorID]
,[OrderDate]
,[TotalDue]
FROM [AdventureWorks2012].[Purchasing].[PurchaseOrderHeader]
WHERE [VendorID] = ANY (
SELECT [VendorID] FROM
[AdventureWorks2012].[Purchasing].[PurchaseOrderHeader]
WHERE [EmployeeID] = 255
)

Percebam que neste caso retornamos as ordens de compra para todos


os fornecedores, de acordo com o empreendedor de id 255.

Utilizando subqueries de múltiplas


colunas (multiple column subquery)

Como o próprio título nos informa, este é o tipo de consulta que será
realizado utilizando-se de duas ou mais colunas, dependendo da
granularidade necessária a nossa necessidade. Neste exemplo,
buscaremos por todas as informações referentes ao id da pessoa,
número de ordem de compra, número do pedido de compra e o id do
cliente, todas essas informações a partir de uma consulta com base
nas informações de [SalesOrderNumber] e [PurchaseOrderNumber],
primeiro verificando se eles existem, utilizando a cláusula EXISTS.
Vejamos então de forma prática como ficaria nossa consulta, como
apresentada Listagem 13.

Listagem 13. Utilizando multiple row subquery.

SELECT [SalesOrderID]
,[SalesPersonID]
,[SalesOrderNumber]
,[PurchaseOrderNumber]
,[CustomerID]
FROM [AdventureWorks2012].[Sales].[SalesOrderHeader]
WHERE EXISTS
(SELECT [SalesOrderNumber],[PurchaseOrderNumber]
FROM [AdventureWorks2012].[Sales].SalesOrderHeader
)
ORDER BY [SalesPersonID] DESC

Como apresentado de acordo com a Listagem 13, utilizamos uma


subconsulta utilizando a palavra-chave EXISTS, neste caso, a
subconsulta funciona basicamente como sendo um teste de
existência. A cláusula WHERE presente na consulta externa realiza
um teste para ver se as linhas retornadas pela subconsulta existem. A
subconsulta não produz nenhum resultado de fato, ela apenas retorna
um valor TRUE ou FALSE.

No nosso próximo exemplo, estaremos apresentando uma pesquisa


com a instrução IN onde informamos os departamentos que queremos
pesquisar com a verificação da existência de uma pessoa com o
primeiro nome igual a “Ken”, como apresentado pela Listagem 14.

Listagem 14. Execução de subquery com múltiplas colunas.

Select dep.Name, p.FirstName, p.LastName, p.MiddleName, p.BusinessEntityID


from HumanResources.Department dep, HumanResources.EmployeeDepartmentHistory
empDep, Person.Person p
where
dep.DepartmentID in(1,16,12)
AND exists (select * from HumanResources.Employee emp, Person.Person p2
where p.FirstName = p2.FirstName
AND p2.FirstName = 'Ken'
AND p2.Title LIKE 'Mr%'
)
AND dep.DepartmentID = empDep.DepartmentID

Como apresentado de acordo com a Listagem 14, criamos uma


pesquisa interna com duas colunas, sendo utilizados os campos, título
e primeiro nome do empregado da empresa, para que a partir daí
trouxéssemos os departamentos correspondentes.

Com isso chegamos ao fim deste artigo, onde tivemos a finalidade de


apresentarmos conceitos importantes com relação a utilização de
subconsultas em nossas consultas T-SQL. O que não podemos
esquecer é que essa não é a única maneira de filtrarmos nossos dados,
pois podemos usar ainda junções (joins), mas o mais importante é
sabermos quando usar cada uma delas.

Subconsulta com operação INSERT INTO

Podemos usar subconsultas também com a instrução de inserção de registros INSERT


INTO, e outras instruções de linguagem de manipulação de dados (DML). Com a
instrução INSERT INTO, os dados retornados por uma subconsulta são usados para
realizar a inserção de um registro em outra tabela.

Sintaxe:
INSERT INTO tabela (colunas)
(SELECT coluna(s)
FROM tabela(s)
WHERE coluna operador valor);

Exemplo: Vamos criar uma tabela de exemplo chamada de LivrosAssuntos, contendo


os campos ID, NomeLivro e NomeAssunto:

CREATE TABLE LivrosAssuntos (


ID TINYINT PRIMARY KEY AUTO_INCREMENT,
NomeLivro VARCHAR(50),
NomeAssunto VARCHAR(40)
)

Usando uma subconsulta, vamos popular esta nova tabela com os livros que tenham
mais de 600 páginas e seus respectivos assuntos:

INSERT INTO LivrosAssuntos (NomeLivro, NomeAssunto)


(SELECT L.NomeLivro, A,NomeAssunto
FROM tbl_livro L
INNER JOIN tbl_assunto A
ON L.IdAssunto = A.IdAssunto
WHERE L.NumPaginas >= 600);

Após executar o comando, efetuamos uma consulta à nova tabela para verificar se os
dados foram inseridos com sucesso:

SELECT * FROM LivrosAssuntos

Resultado:

3. Subconsulta com operação UPDATE

Também podemos usar uma subconsulta com uma instrução UPDATE, permitindo
atualizar os valores de registros de uma tabela com base no resultado retornado pela
subconsulta.

Sintaxe:

UPDATE tabela
SET coluna(s) = valor
WHERE coluna operador (SELECT coluna(s)
FROM tabela
WHERE coluna operador valor);

Exemplo: Vamos aumentar os preços dos livros da editora Microsoft Press em 12%
usando a declaração UPDATE. Como não sabemos o ID desta editora de memória,
vamos usar uma subconsulta para nos auxiliar:

UPDATE tbl_livro
SET PrecoLivro = PrecoLivro * 1.12
WHERE IdEditora =
(SELECT IdEditora
FROM tbl_Editora
WHERE NomeEditora = 'Microsoft Press');

A subconsulta retorna o ID da editora de acordo com o nome da editora fornecido, e


este ID é então empregado na consulta principal para aplicar a atualização nos valores
dos registros desejados.

4. Subconsulta com operação DELETE

Finalmente, podemos usar subconsultas em instruções DELETE, selecionando o valor a


ser excluído de uma tabela de acordo com o retorno da subconsulta.

Sintaxe:

DELETE FROM tabela


WHERE coluna operador (SELECT coluna
FROM tabela
WHERE coluna operador valor);

Exemplo: Desejamos excluir os livros publicados cujo assunto é “Dinossauros“, porém


não sabemos o ID deste assunto na tabela de livros. Suponha também que não
sabemos com exatidão se o nome do assunto é “Dinossauros”, “Dinossauro” (no
singular), “Dinos” ou algo do gênero – apenas os lembramos que o nome do assunto
começa com “Dino”:

DELETE FROM tbl_livro


WHERE IdAssunto =
(SELECT IdAssunto
FROM tbl_Assunto
WHERE NomeAssunto LIKE 'Dino%');

Subconsultas encadeadas

Podemos aninhar uma subconsulta dentro de outra subconsulta, da mesma maneira


que uma subconsulta é aninhada dentro de uma consulta principal. Neste caso, a
subconsulta mais interna é resolvida primeiro, depois a subconsulta intermediária, e
finalmente o resultado é repassado para a consulta principal.

Sintaxe básica:
SELECT coluna(s)
FROM tabela(s)
WHERE coluna operador (SELECT coluna
FROM tabela
WHERE coluna operador (SELECT coluna
FROM tabela
WHERE coluna operador valor);

Na prática, podemos pensar na subconsulta intermediária como fazendo o papel de


uma consulta principal para a subconsulta de nível mais interno, e, assim, o processo é
similar para quantos níveis de subconsultas forem aninhados.

Há um limite para a quantidade das subconsultas que podem ser aninhadas?

Isso depende muito da implementação de banco de dados que está sendo


empregada. Por exemplo, o SGBDR Microsoft SQL Server suporta até 32 níveis de
aninhamento (subconsulta dentro de subconsulta), porém esse limite pode ser
diferente em outros sistemas, muitas vezes por conta de restrições de memória
disponível ou da complexidade em si das consultas. Consulte sempre a documentação
do sistema em uso para saber mais a respeito.

Você também pode gostar