SQL
O que é SQL?
Para comunicar com os SGBDs, necessitamos de uma linguagem adequada. Surgiu assim o SQL, Structured Query Language, uma linguagem dedicada à interação com SGBDs, geralmente relacionais, e que está de acordo com as necessidades de inserir, atualizar, apagar e obter dados.
Dado que inicialmente a linguagem se chamava SEQUEL, hoje em dia são aceites como pronúncia tanto S-Q-L como SEQUEL.
Dado que toda a interação com o SGBD é feita através de SQL, a linguagem suporta um vasto leque de ações:
- Criação e gestão de tabelas (relações) e outros objetos
CREATE
,ALTER
,DROP
- Manipulação de dados (inserção, atualização e eliminação)
INSERT
,UPDATE
,DELETE
- Interrogação (query) de dados
SELECT
- Transações (para concorrência, como iremos ver mais à frente)
START TRANSACTION
,COMMIT
,ROLLBACK
- Gestão administrativa (como de privilégios de utilizadores)
GRANT
,REVOKE
Por norma, as keywords de SQL são escritas em maiúsculas, embora também funcione com minúsculas.
Exemplo: Loja
Para facilitar a explicação das cláusulas SQL e de outros conceitos, vamos introduzir um exemplo muito parecido ao mundo real.
Queremos modelar uma loja (intitulada "Frigu"), onde existem produtos, pedidos e clientes. Cada produto é identificado pelo seu código de barras, nome, preço e stock. Os clientes são identificados pelo seu número de cliente e nome. Finalmente, as encomendas são identificadas pelo número de encomenda, o cliente que a efetuou, o produto comprado, a quantidade deste e a data da operação.
Para simplificar, vamos considerar que cada encomenda tem um único tipo de produto;
não é possível comprar dois produtos diferentes na mesma encomenda.
Consideramos também que o preço de um produto não pode ser alterado, pois isso
afetaria as encomendas a ele associadas. Num programa real, deve-se guardar o preço
de compra juntamente com a encomenda.
Porquê preços inteiros em vez de double/float?
À primeira vista, decidiríamos usar um tipo de dados com suporte para casas decimais
para o preço de um produto. No entanto, e especialmente quando estamos a trabalhar
com dinheiro, devemos evitar estes tipos de dados visto que podem introduzir
erros de arredondamento.
Guardamos, assim, o preço em cêntimos, num inteiro.
Isto não é relevante no contexto da UC de Bases de Dados, mas é sempre importante num contexto real.
Restrições
Reparamos que alguns atributos do diagrama acima estão anotados com "PK" e "FK". Estas anotações correspondem, respetivamente, a "Primary Key" e "Foreign Key".
Uma Primary Key indica qual o campo que identifica uma entrada (row) na tabela.
Por outro lado, uma Foreign Key indica uma relação entre duas tabelas.
Por exemplo, no diagrama acima, temos uma foreign key entre o customer_id
da tabela
purchase
e o customer_id
da tabela customer
, para indicar que se referem ao mesmo.
Algo que vamos querer também é garantir que não existem valores null nas colunas,
exceto na coluna purchase_paid_date
da tabela purchase
.
Tipos de Dados em SQL
Quando criamos tabelas, temos de indicar o tipo de dados de cada coluna.
Em SQL, existem os seguintes tipos de dados, entre outros:
char(n)
(não recomendado): string de tamanho fixon
.varchar(n)
(recomendado): string de tamanho variável com, no máximo,n
caracteres. Dependendo do SGBD, pode existir um valor máximo paran
.text
(non standard): campo de texto, normalmente utilizado para grandes blocos de texto. Não pode ser utilizado como primary key.int
: tipo numérico mais utilizado. Guarda inteiros de 4 bytes, isto é, de -2147483648 a +2147483647.smallint
/bigint
: respetivamente, tipos numéricos de 2 e 8 bytes.unsigned int
(non standard): apenas disponível em MySQL/MariaDB, guarda valores inteiros sem sinal, isto é, não negativos.serial
/int auto_increment
: em PostgreSQL,serial
equivale a um inteiro que é incrementado a cada linha inserida. É normalmente utilizado para atribuir um identificador único a cada linha. Em MySQL/MariaDB, o equivalente éint auto_increment
.
numeric(p, d)
: representa um número exato, comp
dígitos no total ed
casas decimais, ou seja,p - d
dígitos antes da casa decimal. O tipodecimal(p, d)
é equivalente.float
: representa números com vírgula flutuante com valores entre aproximadamente 1E-37 e 1E+37, com precisão de, pelo menos, 6 casas decimais. É de realçar que podem ocorrer erros de arredondamento.double
: semelhante, mas suporta valores entre 1E-307 e 1E+308, com precisão de, pelo menos, 15 dígitos.
timestamp
: representa um conjunto data e horadate
: representa uma datatime
: representa uma hora
boolean
: representa um booleano (verdadeiro ou falso)
...e muitos outros, dependendo do SGBD.
Criação e Destruição de Objetos
Em SQL, temos três cláusulas que nos permitem criar, modificar e destruir vários tipos de objetos, como databases, tables, indexes, views, procedures, etc.
Regra geral, a sintaxe (simplificada) das cláusulas CREATE
, ALTER
e DROP
é a seguinte:
-- Criar um objeto
CREATE <type> <name> [optional arguments];
-- Modificar um objeto
ALTER <type> <name> <arguments>;
-- Destruir um objeto
DROP <type> <name>;
Vejamos como podemos criar ou destruir certos objetos:
Databases
Com estas cláusulas, podemos criar uma database ou eliminá-la. Também é possível modificar uma database, embora seja menos comum.
-- Cria uma base de dados chamada "frigu"
CREATE DATABASE frigu;
-- Elimina a base de dados chamada "frigu"
-- Caso não exista, dá erro
DROP DATABASE frigu;
-- Elimina a base de dados chamada "frigu" se esta existir
-- Caso não exista, ignora e completa com sucesso
DROP DATABASE IF EXISTS frigu;
Dica
Em PostgreSQL, é possível listar as bases de dados existentes com o comando \l
.
O equivalente em MySQL/MariaDB é SHOW DATABASES;
.
Tabelas
-- Cria uma tabela "product" de acordo com o exemplo
CREATE TABLE product (
product_code VARCHAR(255) NOT NULL,
product_name VARCHAR(255) NOT NULL,
price INT NOT NULL,
stock INT NOT NULL,
PRIMARY KEY(product_code)
);
-- Cria uma tabela "customer" de acordo com o exemplo.
-- SERIAL faz com que seja atribuído um valor
-- sequencial a customer_id para cada linha criada
CREATE TABLE customer (
customer_id SERIAL NOT NULL,
customer_name VARCHAR(255),
PRIMARY KEY(customer_id)
);
-- Cria uma tabela "purchase" de acordo com o exemplo.
-- As colunas com foreign keys têm de ter o mesmo tipo
-- em ambas as tabelas (serial é equivalente a int)
CREATE TABLE purchase (
purchase_id SERIAL NOT NULL,
customer_id INT NOT NULL,
product_code VARCHAR(255) NOT NULL,
purchase_quantity INT NOT NULL,
purchase_date TIMESTAMP NOT NULL,
purchase_paid_date TIMESTAMP,
PRIMARY KEY(purchase_id),
FOREIGN KEY(customer_id) REFERENCES customer(customer_id),
FOREIGN KEY(product_code) REFERENCES product(product_code)
);
Dica
Em PostgreSQL, é possível listar as tabelas existentes na base de dados atual com o comando \dt
.
Para ver as colunas de uma tabela, pode-se utilizar o comando \d <nome da tabela>
.
O equivalente em MySQL/MariaDB é SHOW TABLES;
e DESCRIBE <nome da tabela>
, respetivamente.
De notar que em PostgreSQL se usa SERIAL
, enquanto que em MySQL/MariaDB se usa
INT AUTO_INCREMENT
.
Quando criamos uma foreign key, podemos também definir regras
sobre o que fazer quando o valor é alterado/apagado. Por exemplo, o que fazer
se eliminarmos um produto? Queremos apagar todas as compras a si associadas?
Ou não permitimos que este seja eliminado?
Para isto, podemos usar as cláusulas ON DELETE <referential_action>
e
ON UPDATE <referential_action>
, onde <referential_action>
é uma de:
NO ACTION
(default) /RESTRICT
: produz um erro, pelo que não é possível completar a açãoCASCADE
: elimina as linhas correspondentes ao que foi apagado, ou atualiza os valores, se for o casoSET NULL
: coloca as colunas correspondentes a nullSET DEFAULT
: coloca as colunas correspondentes no valor default
É possível encontrar informação mais detalhada na
documentação de CREATE TABLE
do PostgreSQL.
Como exemplo, poderíamos ter definido a seguinte foreign key para a relação compra-produto, que atualizaria a compra caso o produto alterasse o seu código ou eliminaria a compra caso o produto fosse eliminado:
FOREIGN KEY(product_code) REFERENCES product(product_code)
ON UPDATE CASCADE ON DELETE CASCADE
Podemos também alterar tabelas. Imaginemos que, mais tarde, na vida da nossa aplicação, queremos adicionar a tal coluna à compra que indica qual foi o preço no momento da compra.
A cláusula ALTER TABLE
permite-nos fazer exatamente isto (e muito mais!).
-- Adiciona a coluna "purchase_price" à tabela "purchase"
ALTER TABLE purchase
ADD COLUMN purchase_price INT NOT NULL;
Finalmente, como vimos para as databases, podemos também eliminar tabelas:
-- Elimina a tabela "purchase".
-- Dá erro caso a tabela não exista
DROP TABLE purchase;
-- Elimina a tabela "purchase".
-- Ignora caso a tabela não exista
DROP TABLE IF EXISTS purchase;
Inserção de Dados em Tabelas
Podemos facilmente inserir dados em tabelas com a
cláusula INSERT
.
A sintaxe (simplificada) desta cláusula é
INSERT INTO <tabela> [opcionalmente, nome das colunas]
VALUES <valores a inserir>;
Caso não indiquemos as colunas, serão consideradas todas as colunas da tabela
pela ordem em que foram declaradas, ou as primeiras N
colunas caso só sejam
dadas N
colunas nos valores a inserir.
Sendo assim, vejamos alguns exemplos:
-- Inserir um produto
INSERT INTO product VALUES ('111111', 'Bolachas', 50, 10);
-- Inserir múltiplos produtos
INSERT INTO product VALUES ('222222', 'Napolitanas', 25, 15),
('333333', 'Leite com Chocolate', 100, 3);
-- product_code | product_name | price | stock
-- --------------+---------------------+-------+-------
-- 111111 | Bolachas | 50 | 10
-- 222222 | Napolitanas | 25 | 15
-- 333333 | Leite com Chocolate | 100 | 3
-- Inserir múltiplos clientes, indicando quais as colunas
-- que queremos preencher
INSERT INTO customer (customer_name) VALUES
('Diogo'), ('Tomás'), ('Rafa');
-- customer_id | customer_name
-- -------------+---------------
-- 1 | Diogo
-- 2 | Tomás
-- 3 | Rafa
Strings em SQL
Em PostgreSQL, as strings têm obrigatoriamente de utilizar single-quotes ('
), visto
que double-quotes ("
) são usadas para nos referirmos a objetos da base de dados (tabelas, colunas, etc.).
Em MySQL/MariaDB, pode-se usar tanto single-quotes como double-quotes para strings,
sendo que para referenciar objetos da base de dados, usa-se backticks (`
).
Mas então o que é que acontece se tentarmos inserir dados que não respeitam as restrições impostas? O SGBD vai rejeitar a nossa query!
-
Inserir duas linhas com o mesmo valor para a coluna da primary key:
INSERT INTO product VALUES ('123', 'Maçã', 10, 5); INSERT INTO product VALUES ('123', 'Banana', 8, 4); -- ERROR: duplicate key value violates unique constraint "product_pkey" -- DETAIL: Key (product_code)=(123) already exists.
-
Inserir uma linha que referencia um valor de outra tabela não existente:
INSERT INTO purchase (customer_id, product_code, purchase_quantity, purchase_date) VALUES (100, '222222', 3, now()); -- ERROR: insert or update on table "purchase" violates foreign key constraint "purchase_customer_id_fkey" -- DETAIL: Key (customer_id)=(100) is not present in table "customer".
Atualização de Dados em Tabelas
Após inserirmos dados (linhas) em tabelas, podemos querer efetuar-lhes alterações.
A cláusula UPDATE
permite-nos fazer exatamente isto: alterar os valores de uma ou mais linhas de uma tabela.
A sintaxe (simplificada) desta cláusula é:
UPDATE <tabela>
SET <coluna1> = <expressao1>,
<coluna2> = <expressao2>,
...
WHERE <condição>;
Atenção!
Caso não indiquemos uma condição com a cláusula WHERE
, o UPDATE
irá reescrever
sobre todas as linhas existentes na tabela.
Sendo assim, vejamos alguns exemplos:
-- Alterar o nome de um produto
UPDATE product SET product_name = 'Bolachas com Chocolate'
WHERE product_name = 'Bolachas';
-- Incrementar o stock de um produto por 5
UPDATE product SET stock = stock + 5
WHERE product_code = '222222';
Eliminação de Dados em Tabelas
Após inserirmos ou atualizarmos dados (linhas) em tabelas, podemos querer apagá-los.
A cláusula DELETE
permite-nos fazer exatamente isto: eliminar uma ou mais linhas de uma tabela.
A sintaxe (simplificada) desta cláusula é:
DELETE FROM <tabela> WHERE <condição>;
Atenção!
Caso não indiquemos uma condição com a cláusula WHERE
, o DELETE
irá apagar
todas as linhas existentes na tabela.
Sendo assim, vejamos alguns exemplos:
-- Apagar todas as linhas de uma tabela
DELETE FROM product;
-- Apagar todos os produtos com preço superior a 50 cêntimos
DELETE FROM product WHERE price > 50;
Interrogação de Dados
Chegamos finalmente à parte mais complexa de SQL: as interrogações (queries).
Para efetuar uma query, utilizamos a
cláusula SELECT
.
Como seria de esperar, a sintaxe desta cláusula é muito mais completa do que todas
as outras, visto que é a partir dela que podemos fazer qualquer interrogação
sobre os nossos dados.
Assim, uma sintaxe simplificada desta cláusula é:
SELECT <colunas> FROM <tabela>
-- todas as cláusulas daqui para baixo são opcionais
WHERE <condição>
GROUP BY <colunas>
ORDER BY <colunas> [ordem]
LIMIT <quantidade>;
-- e muitos outros...
Como podemos ver, é possível juntar a cláusula SELECT
a muitas outras cláusulas
para obtermos exatamente os dados que queremos.
De seguida, vamos ver algumas destas cláusulas a ser aplicadas independentemente por uma questão de simplicidade, mas numa situação real é bastante provável que estas apareçam todas juntas.
Cláusula WHERE
Já conhecemos esta cláusula da atualização e eliminação de dados em tabelas, mas vamos agora aprofundar o nosso estudo sobre a mesma.
A sintaxe da cláusula WHERE
é, trivialmente,
WHERE <condição>
Podemos ter também condições compostas, isto é, com operadores lógicos AND
e OR
, por exemplo.
Vejamos os seguintes exemplos:
-- Obtém o nome e stock dos produtos com preço superior
-- ou igual a 100 cêntimos (1€)
SELECT product_name, stock FROM product
WHERE price >= 100;
-- Obtém todas as colunas dos produtos com preço entre
-- 20 e 80 cêntimos
SELECT * FROM product
WHERE price BETWEEN 20 AND 80;
-- Obtém o código dos produtos com preço superior a
-- 30 cêntimos e stock inferior a 5
SELECT product_code FROM product
WHERE price > 30 AND stock < 5;
-- Obtém todas as colunas dos produtos com preço
-- igual a 20, 50, 80 ou 90 cêntimos
SELECT * FROM product
WHERE price IN (20, 50, 80, 90);
Filtros que Dependem de Outras Tabelas
Além dos exemplos relativamente simples apresentados acima, podemos também juntas duas queries para efetuar um filtro mais avançado.
-- Obtém todas as colunas das compras cujos produtos
-- tenham um preço superior a 50 cêntimos
SELECT * FROM purchase
WHERE product_code IN (
SELECT product_code FROM product
WHERE price > 50
);
Aviso de Desempenho
É de notar que intercalar queries desta forma pode causar problemas
de desempenho, pelo que é recomendada a utilização de outra cláusula,
o JOIN
, que iremos ver mais abaixo.
Cláusula LIKE
Por vezes, queremos filtrar por um campo de texto, mas por uma expressão e não por um valor exato. Por exemplo, podemos querer todos os produtos que contenham "Bolacha" no nome.
A cláusula LIKE
permite-nos criar expressões simples para efetuar estes filtros.
A sintaxe é, simplesmente,
<string/coluna> LIKE <padrão>
Este padrão é uma string normal que pode conter dois caracteres especiais, %
e _
:
%
: corresponde a uma sequência de zero ou mais caracteres_
: corresponde a qualquer carácter uma e apenas uma vez
Como é evidente, caso o padrão não contenha nenhum destes dois caracteres, as strings são apenas comparadas por igualdade.
Vejamos, então, alguns exemplos:
Input | Padrão | Resultado |
---|---|---|
abc |
abc |
true |
abc |
a% |
true |
abc |
_b_ |
true |
abc |
c |
false |
abc |
c% |
false |
abc |
%c_ |
false |
abc |
%c% |
true |
Maiúsculas e Minúsculas
No standard SQL, a cláusula LIKE
é sensível a maiúsculas e minúsculas.
Em PostgreSQL, existe uma extensão, ILIKE
, que funciona de igual modo, mas ignora
maiúsculas e minúsculas.
Escaping do padrão
Se quisermos procurar uma string que contenha mesmo %
ou _
, podemos fazer
escape destes caracteres, como seria de esperar: 5\%
.
Cláusula GROUP BY
Podemos querer agrupar várias linhas de uma tabela que têm o mesmo valor para uma coluna. Por exemplo, podemos querer saber quantas unidades de cada produto já se venderam.
Por exemplo, considerando que temos os seguintes dados na tabela das compras (algumas colunas omitidas para simplificar):
customer_id |
product_code |
purchase_quantity |
---|---|---|
1 | 222222 | 3 |
2 | 222222 | 6 |
1 | 111111 | 7 |
-- Obter a soma das quantidades de todas as compras,
-- agrupadas por produto
SELECT product_code, SUM(purchase_quantity) AS quantity
FROM purchase
GROUP BY product_code;
-- product_code | quantity
-- --------------+----------
-- 222222 | 9
-- 111111 | 7
Podemos claramente ver que foram somadas as duas linhas que se referiam ao mesmo produto, independentemente do cliente que efetuou a compra.
Cláusula ORDER BY
Esta cláusula permite-nos definir regras de ordenação para as nossas queries. Podemos ordenar por números, texto, etc e indicar se queremos ordem crescente ou decrescente.
A sintaxe desta cláusula é
ORDER BY <coluna1> <ordem1>, <coluna2> <ordem2>, ...
sendo que <ordemX>
é ASC
(valor por omissão) ou DESC
.
Considerando os seguintes valores para a tabela product
,
product_code |
product_name |
price |
stock |
---|---|---|---|
111111 | Bolachas | 50 | 10 |
222222 | Napolitanas | 25 | 15 |
333333 | Leite com Chocolate | 100 | 3 |
444444 | Amendoins | 100 | 1 |
vejamos o seguinte exemplo:
-- Ordenar por preço crescente
SELECT * FROM product ORDER BY price;
-- product_code | product_name | price | stock
-- --------------+---------------------+-------+-------
-- 222222 | Napolitanas | 25 | 15
-- 111111 | Bolachas | 50 | 10
-- 333333 | Leite com Chocolate | 100 | 3
-- 444444 | Amendoins | 100 | 1
-- Ordenar por preço decrescente
SELECT * FROM product ORDER BY price DESC;
-- product_code | product_name | price | stock
-- --------------+---------------------+-------+-------
-- 333333 | Leite com Chocolate | 100 | 3
-- 444444 | Amendoins | 100 | 1
-- 111111 | Bolachas | 50 | 10
-- 222222 | Napolitanas | 25 | 15
-- Ordenar por preço decrescente, desempatar por ordem alfabética
SELECT * FROM product ORDER BY price DESC, product_name;
-- product_code | product_name | price | stock
-- --------------+---------------------+-------+-------
-- 444444 | Amendoins | 100 | 1
-- 333333 | Leite com Chocolate | 100 | 3
-- 111111 | Bolachas | 50 | 10
-- 222222 | Napolitanas | 25 | 15
Ordenação de Caracteres Acentuados
Quando estamos a ordenar caracteres com acentos, a sua ordem vai depender da collation definida nas definições da base de dados, ou se o SGBD suportar, da coluna.
Cláusula LIMIT
Como o nome indica, esta cláusula limita as linhas que nos são apresentadas ao número que indicamos.
A sintaxe desta cláusula é
LIMIT <quantidade>
Por exemplo, juntando com a cláusula ORDER BY
(o que não é necessário):
-- Obter os dois primeiros produtos por ordem alfabética
SELECT * FROM product
ORDER BY product_name
LIMIT 2;
Junção de Queries
Podemos juntar duas (ou mais) queries de três maneiras: união, interceção ou diferença.
Para facilitar os exemplos, tomemos a seguinte situação, em que temos três tabelas,
person
, teacher
e student
, respetivamente:
Person:
person_name |
birthday |
person_city |
---|---|---|
Diogo | 2002-03-06 | Lisbon |
Rafa | 2002-08-05 | Leiria |
Tomás | 2002-12-18 | Lisbon |
João | 1999-04-16 | Porto |
André | 1999-08-30 | Aveiro |
Teacher:
person_name |
class_id |
---|---|
João | Comp-22 |
André | SO-22 |
André | SD-22 |
Student:
person_name |
class_id |
---|---|
Diogo | BD-22 |
Rafa | BD-22 |
Tomás | BD-22 |
Diogo | SO-22 |
João | BD-19 |
Linhas duplicadas
Nestes três modos, a cláusula DISTINCT
é aplicada automaticamente, pelo que
não são apresentadas linhas duplicadas.
Para mostrar todas as linhas, podemos usar explicitamente a cláusula ALL
.
Cláusula UNION
Esta cláusula junta os valores das colunas indicadas, isto é, efetua a união entre os dois conjuntos.
Por exemplo:
(SELECT person_name FROM student)
UNION
(SELECT person_name FROM teacher);
-- person_name
-- -------------
-- Rafa
-- Tomás
-- Diogo
-- João
-- André
Cláusula INTERSECT
Esta cláusula mostra os valores em comum das colunas indicadas, isto é, efetua a interceção entre os dois conjuntos.
Por exemplo:
(SELECT person_name FROM student)
INTERSECT
(SELECT person_name FROM teacher);
-- person_name
-- -------------
-- João
Cláusula EXCEPT
Esta cláusula mostra os valores das colunas indicadas que estão na primeira query mas não na segunda, isto é, efetua a subtração entre os dois conjuntos.
Por exemplo:
(SELECT person_name FROM student)
EXCEPT
(SELECT person_name FROM teacher);
-- person_name
-- -------------
-- Rafa
-- Tomás
-- Diogo
Cláusula JOIN
De forma a tirarmos proveito do modelo relacional, vamos querer efetuar queries
em várias tabelas simultaneamente e juntar os dados daí obtidos.
Por exemplo, na nossa loja, Frigu, temos uma tabela com todas as compras.
No entanto, esta tabela apenas guarda o identificador do cliente e do produto.
Como podemos fazer para obter também o nome do produto e do cliente?
Utilizando a cláusula JOIN
!
Existem os seguintes tipos de joins (a notação entre parêntesis é opcional):
[ INNER ] JOIN
: Mostra as linhas com valores em comum em ambas as tabelasLEFT [ OUTER ] JOIN
: Mostra todas as linhas da tabela da esquerda e os valores correspondentes, caso existam, da tabela da direita.RIGHT [ OUTER ] JOIN
: Mostra todas as linhas da tabela da direita e os valores correspondentes, caso existam, da tabela da esquerda.FULL [ OUTER ] JOIN
: Mostra todas as linhas de ambas as tabelas, efetuando a correspondência dos valores caso existam.
Dependendo do tipo de join, este pode ter um comportamento, não necessariamente exclusivo, de:
- Mapeamento: Os valores existentes numa tabela são aumentados com um mapeamento para valores de outra tabela (e.g. na tabela de compras, podemos mapear o id do cliente para obter o resto das suas informações, como o seu nome).
- Filtragem: Certas linhas de uma tabela não são selecionadas visto que não têm correspondência noutra tabela (e.g. juntamos uma tabela de alunos com uma tabela de notas, pelo que os alunos sem notas são aparecem).
- Multiplicação: Certas linhas de uma tabela aparecem mais de uma vez, visto que têm várias correspondências noutra tabela (e.g. juntamos uma tabela de alunos com uma tabela de inscrições, pelo que os alunos inscritos a mais de uma disciplina aparecem várias vezes).
Inner Join
É provavelmente o tipo de join mais frequente, que é usado para juntar tabelas sem deixar valores em falta.
Voltando ao exemplo da loja, Frigu, podemos obter então a lista de compras juntamente com o nome dos respetivos clientes e produtos.
SELECT purchase.purchase_id, product.product_name,
customer.customer_name, purchase.purchase_quantity
FROM purchase
INNER JOIN product ON product.product_code = purchase.product_code
INNER JOIN customer ON customer.customer_id = purchase.customer_id;
-- purchase_id | product_name | customer_name | purchase_quantity
-- -------------+---------------------+---------------+-------------------
-- 1 | Napolitanas | Diogo | 3
-- 2 | Napolitanas | Tomás | 6
-- 3 | Bolachas | Diogo | 7
-- 4 | Leite com Chocolate | Tomás | 2
-- 5 | Leite com Chocolate | Diogo | 1
Outer Join
Visto que a estrutura da base de dados do exemplo da loja, Frigu, não permite
associações não existentes, temos de utilizar outro exemplo. Consideremos assim
duas tabelas, city
e weather
, com os seguintes dados:
City:
city_name |
location |
---|---|
Lisbon | (38,-9) |
Porto | (41,-8) |
Brussels | (50,4) |
Weather:
city_name |
temperature |
---|---|
Lisbon | 23 |
Brussels | 14 |
Kyiv | 15 |
-- Left Outer Join: Incluir todas as linhas da tabela 'weather'
SELECT weather.city_name, weather.temperature,
city.city_name, city.location
FROM weather
LEFT OUTER JOIN city ON weather.city_name = city.city_name;
-- city_name | temperature | city_name | location
-- -----------+-------------+-----------+----------
-- Lisbon | 23 | Lisbon | (38,-9)
-- Brussels | 14 | Brussels | (50,4)
-- Kyiv | 15 | |
-- Right Outer Join: Incluir todas as linhas da tabela 'city'
SELECT weather.city_name, weather.temperature,
city.city_name, city.location
FROM weather
RIGHT OUTER JOIN city ON weather.city_name = city.city_name;
-- city_name | temperature | city_name | location
-- -----------+-------------+-----------+----------
-- Lisbon | 23 | Lisbon | (38,-9)
-- | | Porto | (41,-8)
-- Brussels | 14 | Brussels | (50,4)
-- Full Outer Join: Incluir todas as linhas das tabelas 'weather' e 'city'
SELECT weather.city_name, weather.temperature,
city.city_name, city.location
FROM weather
FULL OUTER JOIN city ON weather.city_name = city.city_name;
-- city_name | temperature | city_name | location
-- -----------+-------------+-----------+----------
-- Lisbon | 23 | Lisbon | (38,-9)
-- Brussels | 14 | Brussels | (50,4)
-- Kyiv | 15 | |
-- | | Porto | (41,-8)
Natural Join
Comportamento Inesperado
Atenção ao utilizar o NATURAL JOIN
, visto que este pode ter comportamento
inesperado.
O NATURAL JOIN
efetua um join em todas as colunas com nome igual,
pelo que se tivermos duas colunas com o mesmo nome mas não relacionadas,
vamos obter dados incorretos.
O NATURAL JOIN
permite efetuar joins de uma forma mais simples, sendo apenas
um alias para as cláusulas mais expressivas.
Por exemplo, considerando a loja Frigu, as seguintes queries são equivalentes:
SELECT customer.customer_name, purchase.purchase_quantity
FROM purchase
INNER JOIN customer ON customer.customer_id = purchase.customer_id;
-- é equivalente a
SELECT customer.customer_name, purchase.purchase_quantity
FROM purchase
NATURAL JOIN customer;
Para o OUTER JOIN
, o mesmo funciona:
SELECT weather.city_name, weather.temperature,
city.city_name, city.location
FROM weather
LEFT OUTER JOIN city ON weather.city_name = city.city_name;
-- é equivalente a
SELECT weather.city_name, weather.temperature,
city.city_name, city.location
FROM weather
NATURAL LEFT OUTER JOIN city;
SELECT weather.city_name, weather.temperature,
city.city_name, city.location
FROM weather
RIGHT OUTER JOIN city ON weather.city_name = city.city_name;
-- é equivalente a
SELECT weather.city_name, weather.temperature,
city.city_name, city.location
FROM weather
NATURAL RIGHT OUTER JOIN city;
SELECT weather.city_name, weather.temperature,
city.city_name, city.location
FROM weather
FULL OUTER JOIN city ON weather.city_name = city.city_name;
-- é equivalente a
SELECT weather.city_name, weather.temperature,
city.city_name, city.location
FROM weather
NATURAL FULL OUTER JOIN city;