3. Introdução à linguagem SQL
Nesta secção do capítulo, apresentamos os primeiros comandos SQL para criar e trabalhar com uma única tabela. Geralmente, fornecemos uma versão simplificada destes comandos. A sua sintaxe completa está disponível nos guias de referência do Firebird (ver Secção 2.2).
Uma base de dados é utilizada por pessoas com diferentes níveis de competências:
- o administrador da base de dados é geralmente alguém proficiente em SQL e bases de dados. São eles que criam as tabelas, uma vez que esta operação é normalmente realizada apenas uma vez. Com o tempo, podem precisar de modificar a estrutura. Uma base de dados é uma coleção de tabelas ligadas por relações. O administrador da base de dados define estas relações. Também concede permissões aos vários utilizadores da base de dados. Por exemplo, pode especificar que um determinado utilizador tem o direito de visualizar o conteúdo de uma tabela, mas não de a modificar.
- O utilizador da base de dados é a pessoa que dá vida aos dados. Dependendo das permissões concedidas pelo administrador da base de dados, irá adicionar, modificar e eliminar dados nas várias tabelas da base de dados. Também irá analisar os dados para extrair informações úteis para o bom funcionamento do negócio, da administração, etc.
Na secção 2.6, apresentámos o editor SQL da ferramenta [IB-Expert]. Esta é a ferramenta que iremos utilizar. Vamos rever alguns pontos:
- O Editor SQL pode ser acedido através da opção de menu [Ferramentas/Editor SQL] ou premindo a tecla [F12]

Isto abre uma janela [Editor SQL] onde podemos escrever um comando SQL:

A captura de ecrã acima é frequentemente representada pelo texto abaixo:
3.1. Tipos de dados do Firebird
Ao criar uma tabela, deve especificar o tipo de dados que uma coluna da tabela pode conter. Aqui, apresentamos os tipos de dados mais comuns do Firebird. Note que estes tipos de dados podem variar de um SGBD para outro.
inteiro no intervalo [-32768, 32767]: 4 | |
inteiro no intervalo [–2 147 483 648, 2 147 483 647]: -100 | |
número real com n dígitos, dos quais m são decimais NUMERIC(5,2): -100,23, +027,30 | |
número real aproximado a 7 dígitos significativos: 10,4 | |
número real aproximado a 15 dígitos significativos: -100,89 | |
Uma cadeia de caracteres com exatamente N caracteres. Se a cadeia armazenada tiver menos de N caracteres, é preenchida com espaços. CHAR(10): 'ANGERS ' (4 espaços à direita) | |
cadeia de até N caracteres VARCHAR(10): 'ANGERS' | |
uma data: '2006-01-09' (formato AAAA-MM-DD) | |
uma hora: '16:43:00' (formato HH:MM:SS) | |
data e hora: '2006-01-09 16:43:00' (formato AAAA-MM-DD HH:MM:SS) |
A função CAST() permite converter de um tipo para outro quando necessário. Para converter um valor V declarado como tipo T1 para o tipo T2, escreve-se: CAST(V,T2). É possível realizar as seguintes conversões de tipo:
- número para cadeia de caracteres. Esta conversão de tipo é implícita e não requer a utilização da função CAST. Assim, a operação 1 + '3' não requer a conversão do caractere '3'. O seu resultado é o número 4.
- DATE, TIME, TIMESTAMP para cadeias de caracteres e vice-versa. Assim
- TIMESTAMP para TIME ou DATE e vice-versa
Numa tabela, uma linha pode ter colunas sem valor. Dizemos que o valor da coluna é a constante NULL. Pode verificar a presença deste valor utilizando os operadores
IS NULL / IS NOT NULL
3.2. Criar uma tabela
Para aprender a criar uma tabela, começaremos por criar uma no modo [Design] com o IBExpert. Para tal, seguiremos o método descrito na secção 2.3. Isto irá criar a seguinte tabela:

Esta tabela será utilizada para registar os livros adquiridos por uma biblioteca. O significado dos campos é o seguinte:
Nome | Tipo | Restrição | Significado |
Esta tabela, que foi criada utilizando o assistente IBEXPERT, poderia ter sido criada diretamente utilizando instruções SQL. Para as visualizar, basta consultar o separador [DDL] da tabela:

O código SQL utilizado para criar a tabela [BIBLIO] é o seguinte:
- linha 1: proprietário Firebird - indica o nível de dialeto SQL utilizado
- linha 2: específico do Firebird - especifica o conjunto de caracteres utilizado
- linhas 6–14: padrão SQL: cria a tabela BIBLIO definindo o nome e o tipo de dados de cada uma das suas colunas.
- linha 16: padrão SQL: cria uma restrição especificando que a coluna TITLE não permite duplicados
- linha 17: padrão SQL: especifica que a coluna [ID] é a chave primária da tabela. Isto significa que não podem existir duas linhas na tabela com o mesmo ID. Isto é semelhante à restrição [UNIQUE NOT NULL] na coluna [TITLE] e, na verdade, a coluna TITLE poderia ter servido como chave primária. A tendência atual é utilizar chaves primárias que não têm um significado específico e são geradas pelo SGBD.
A sintaxe do comando [CREATE TABLE] é a seguinte:
CREATE TABLE tabela (nome_coluna1 tipo_coluna1 restrição_coluna1, nome_coluna2 tipo_coluna2 restrição_coluna2, ..., nome_colunaN tipo_colunaN restrição_colunaN, outras restrições) | |||||||||
cria a tabela table com as colunas especificadas
|
A tabela [BIBLIO] também poderia ter sido criada com a seguinte instrução SQL:
Vamos demonstrar isto. Vamos abrir esta consulta num editor SQL (F12) para criar uma tabela a que chamaremos [BIBLIO2]:

Após a execução, é necessário confirmar a transação para ver o resultado na base de dados:

Assim que isso estiver feito, a tabela aparece na base de dados:

Ao clicar duas vezes no seu nome, podemos visualizar a sua estrutura:

Podemos ver a definição que criámos para a tabela [BIBLIO2]
3.3. Eliminar uma tabela
A instrução SQL para eliminar uma tabela é a seguinte:
DROP TABLE tabela | |
Elimina [tabela] |
Para eliminar a tabela [BIBLIO2] que acabámos de criar, executamos agora o seguinte comando SQL:

e confirmamos com [Commit]. A tabela [BIBLIO2] é eliminada:

3.4. Preenchimento de uma tabela
Vamos inserir uma linha na tabela [BIBLIO] que acabámos de criar:

Confirme a adição da linha com [Commit] e, em seguida, clique com o botão direito do rato na linha adicionada:

e, como mostrado acima, copie a linha inserida para a área de transferência como uma instrução SQL INSERT. Em seguida, abra qualquer editor de texto e cole o que acabámos de copiar. Obtemos o seguinte código SQL:
INSERT INTO BIBLIO (ID,TITRE,AUTEUR,GENRE,ACHAT,PRIX,DISPONIBLE) VALUES (1,'Candide','Voltaire','Essai','18-OCT-1985',140,'o');
A sintaxe de uma instrução SQL INSERT é a seguinte:
insert into table [(coluna1, coluna2, ..)] values (valor1, valor2, ....) | |
adiciona uma linha (valor1, valor2, ...) à tabela. Estes valores são atribuídos à coluna1, coluna2, ... caso existam; caso contrário, às colunas da tabela pela ordem em que foram definidas. |
Para inserir novas linhas na tabela [BIBLIO], iremos digitar as seguintes instruções INSERT no editor SQL. Iremos executar e confirmar estas instruções uma a uma. Iremos utilizar o botão [Nova Consulta] para avançar para a próxima instrução INSERT.
Após confirmar [Commit] as várias instruções SQL, obtemos a seguinte tabela:
![]() |
3.5. Consultar uma tabela
3.5.1. Introdução
No editor SQL, digite o seguinte comando:

e execute-o. Obtemos o seguinte resultado:

A instrução SELECT é utilizada para recuperar dados de tabelas de bases de dados. Esta instrução possui uma sintaxe muito rica. Aqui, iremos concentrar-nos na sintaxe para consultar uma única tabela. Abordaremos a consulta de várias tabelas simultaneamente mais tarde. A sintaxe da instrução SQL [SELECT] é a seguinte:
SELECT [ALL|DISTINCT] [*|expressão1 alias1, expressão2 alias2, ...] FROM tabela | |
exibe os valores de expressão1 para todas as linhas da tabela. expressão1 pode ser uma coluna ou uma expressão mais complexa. O símbolo * denota todas as colunas. Por predefinição, são exibidas todas as linhas da tabela (ALL). Se DISTINCT estiver presente, as linhas selecionadas idênticas são exibidas apenas uma vez. Os valores de expressão1 são exibidos numa coluna intitulada expressão1 ou alias1, caso este último tenha sido utilizado. |
Exemplos:



Nos exemplos acima, atribuímos aliases (TÍTULO_DO_LIVRO, PREÇO_DE_COMPRA) às colunas solicitadas.
3.5.2. Exibir linhas que satisfazem uma condição
SELECT .... WHERE condição | |
Apenas as linhas que satisfazem a condição são apresentadas |
Exemplos


Um dos livros tem o género «novel» e não «Novel». Utilizamos a função UPPER, que converte uma cadeia de caracteres para maiúsculas, para obter todos os romances.

Podemos combinar condições utilizando operadores lógicos
AND lógico | |
OU lógico | |
Negação lógica |



![]()

![]() |

3.5.3. Exibir linhas numa ordem específica
Às sintaxes anteriores, pode adicionar uma cláusula ORDER BY especificando a ordem de exibição desejada:
SELECT .... ORDER BY expressão1 [asc|desc], expressão2 [asc|desc], ... | |
As linhas de resultado da seleção são apresentadas na ordem de 1: ordem ascendente (asc / ascendente, que é o padrão) ou descendente (desc / descendente) da expressão1 2: se a expressão1 for igual, a exibição baseia-se nos valores da expressão2 etc. |
Exemplos:





3.6. Eliminar linhas de uma tabela
DELETE FROM tabela [WHERE condição] | |
Elimina as linhas da tabela que satisfazem a condição. Se não for especificada nenhuma condição, todas as linhas são eliminadas. |
Exemplos:

Os dois comandos abaixo são executados um após o outro:

3.7. Modificar o conteúdo de uma tabela
update table set coluna1 = expressão1, coluna2 = expressão2, ... [onde condição] | |
Para as linhas da tabela que satisfazem a condição (todas as linhas, se não houver condição), a coluna1 é definida com o valor da expressão1. |
Exemplos:
Escrevemos todos os géneros com maiúscula:

Verificamos:
![]()
Exibir os preços:

O preço dos romances aumenta 5%:
Vamos verificar:

3.8. Atualização permanente de uma tabela
Quando são feitas alterações numa tabela, o Firebird aplica-as, na verdade, a uma cópia da tabela. Estas alterações podem então ser tornadas permanentes ou revertidas utilizando os comandos COMMIT e ROLLBACK.
COMMIT | |
Torna permanentes as atualizações feitas nas tabelas desde o último COMMIT. |
ROLLBACK | |
Reverte todas as alterações feitas nas tabelas desde o último COMMIT. |
Um COMMIT é executado implicitamente nas seguintes ocasiões: a) Ao sair do Firebird b) Após cada comando que afete a estrutura das tabelas: CREATE, ALTER, DROP. |
Exemplos
No editor SQL, pode restaurar a base de dados para um estado conhecido, confirmando todas as operações realizadas desde o último COMMIT ou ROLLBACK:
Recuperamos a lista de títulos:

Eliminar um título:
Verificação:

O título foi eliminado com sucesso. Agora vamos reverter todas as alterações feitas desde o último COMMIT / ROLLBACK:
Verificação:

O título eliminado reapareceu. Agora, vamos recuperar a lista de preços:
![]()
Vamos definir todos os preços como zero.
Vamos verificar os preços:
![]()
Vamos reverter as alterações feitas na base de dados:
e verifique os preços novamente:
![]()
Restaurámos os preços originais.
3.9. Adicionar linhas de uma tabela para outra
É possível adicionar linhas de uma tabela para outra quando as suas estruturas são compatíveis. Para demonstrar isto, vamos começar por criar uma tabela [BIBLIO2] com a mesma estrutura que [BIBLIO].
No explorador de bases de dados do IBExpert, clique duas vezes na tabela [BIBLIO] para aceder ao separador [DDL]:

Nesta guia, encontrará a lista de instruções SQL utilizadas para gerar a tabela [BIBLIO]. Copie todo este código para a área de transferência (CTRL-A, CTRL-C). Em seguida, abra uma ferramenta chamada [Script Executive] que permite executar uma lista de instruções SQL:

Abre-se um editor de texto, no qual podemos colar (CTRL-V) o texto previamente copiado para a área de transferência:

Uma lista de comandos SQL é frequentemente chamada de script SQL. O [Script Executive] permite-nos executar esse script, enquanto o editor SQL só permitia a execução de um único comando de cada vez. O script SQL atual cria a tabela [BIBLIO]. Vamos fazer com que crie uma tabela chamada [BIBLIO2]. Para isso, basta alterar [BIBLIO] para [BIBLIO2]:
Vamos executar este script utilizando o botão [Executar Script] abaixo:

O script é executado:

e podemos ver a nova tabela no explorador da base de dados:

Se clicarmos duas vezes em [BIBLIO2] para verificar o seu conteúdo, verificamos que está vazia, o que é normal:

Uma variante da instrução SQL INSERT permite inserir linhas de uma tabela noutra:
INSERT INTO tabela1 [(coluna1, coluna2, ...)] SELECT coluna1, coluna2, ... FROM tabela2 WHERE condição | |
As linhas da tabela2 que satisfazem a condição são adicionadas à tabela1. As colunas coluna1, coluna2, ... da tabela2 são atribuídas, por ordem, às colunas coluna1, coluna2, ... na tabela1 e, por isso, devem ser de tipos compatíveis. |
Voltemos ao editor SQL:

e executar a seguinte instrução SQL:
o que insere em [BIBLIO2] todas as linhas de [BIBLIO] correspondentes a um romance. Após executar a instrução SQL, vamos confirmá-la com um [Commit]:
Agora, vamos visualizar os dados na tabela [BIBLIO2]:

3.10. Eliminar uma tabela
DROP TABLE tabela | |
elimina a tabela |
Exemplo: Eliminar a tabela BIBLIO2
Confirme a alteração:
No explorador da base de dados, atualize a visualização da tabela:

Vemos que a tabela [BIBLIO2] foi eliminada:

3.11. Modificar a estrutura de uma tabela
ALTER TABLE tabela [ ADD nome_coluna1 tipo_coluna1 restrição_coluna1] [ALTER nome_da_coluna2 TYPE tipo_da_coluna2] [DROP nome_da_coluna3] [ADD restrição] [DROP CONSTRAINT nome_da_restrição] | |
permite adicionar (ADD), modificar (ALTER) e eliminar (DROP) colunas de tabelas. A sintaxe nome_da_coluna1 tipo_da_coluna1 restrição_da_coluna1 é a mesma que a de CREATE TABLE. Também é possível adicionar ou eliminar restrições de tabelas. |
Exemplo: Execute os dois comandos SQL seguintes sequencialmente no editor de SQL
No explorador da base de dados, vamos verificar a estrutura da tabela [BIBLIO]:

As alterações foram aplicadas. Vamos ver como o conteúdo da tabela mudou:

A nova coluna [NB_PAGES] foi criada, mas não tem valores. Vamos eliminar esta coluna:
Vamos verificar a nova estrutura da tabela [BIBLIO]:

A coluna [NB_PAGES] desapareceu de facto.
3.12. Vistas
É possível ter uma vista parcial de uma tabela ou de várias tabelas. Uma vista comporta-se como uma tabela, mas não contém dados. Os seus dados são extraídos de outras tabelas ou vistas. Uma vista tem várias vantagens:
- Um utilizador pode estar interessado apenas em determinadas colunas e linhas de uma determinada tabela. A vista permite-lhe ver apenas essas linhas e colunas.
- O proprietário de uma tabela pode querer conceder apenas acesso limitado a outros utilizadores. Uma vista permite-lhe fazê-lo. Os utilizadores que tiver autorizado terão apenas acesso à vista que tiver definido.
3.12.1. Criar uma vista
CREATE VIEW nome_da_vista AS SELECT coluna1, coluna2, ... FROM tabela WHERE condição [ COM OPÇÃO DE VERIFICAÇÃO ] | |
cria a vista view_name. Trata-se de uma tabela com a estrutura coluna1, coluna2, ... da tabela e, no que diz respeito às linhas, as linhas da tabela que satisfazem a condição (todas as linhas, caso não haja condição) | |
Esta cláusula opcional especifica que as inserções e atualizações na vista não devem criar linhas que a vista não possa selecionar. |
Nota A sintaxe de CREATE VIEW é, na verdade, mais complexa do que a apresentada acima e permite, em particular, a criação de uma vista a partir de várias tabelas. Para tal, a instrução SELECT precisa apenas de referenciar várias tabelas (ver o capítulo seguinte).
Exemplos
Criamos uma vista a partir da tabela biblio que inclui apenas romances (seleção de linhas) e apenas as colunas título, autor e preço (seleção de colunas):
No explorador da base de dados, atualize a vista (F5). É apresentada uma vista:

Podemos visualizar a instrução SQL associada à vista. Para tal, clique duas vezes na vista [ROMANS]:

Uma vista é semelhante a uma tabela. Tem uma estrutura:

e conteúdo:

Uma vista é utilizada como uma tabela. É possível executar consultas SQL nela. Aqui estão alguns exemplos para experimentar no editor SQL:

O novo romance está visível na vista [ROMANS]?

Vamos adicionar algo que não seja um romance à tabela [BIBLIO]:
SQL> insert into biblio(id,titre,auteur,genre,achat,prix,disponible) values (11,'Poèmes saturniens','Verlaine','Poème','02-sep-92',200,'o');
Vamos verificar a tabela [BIBLIO]:

Vamos verificar a vista [ROMANS]:

O livro adicionado não está na vista [ROMANS] porque não tinha upper(genre)='ROMAN'.
3.12.2. Atualizar uma vista
Pode atualizar uma vista tal como faria com uma tabela. Todas as tabelas das quais os dados da vista são extraídos são afetadas por esta atualização. Aqui estão alguns exemplos:
SQL> insert into biblio(id,titre,auteur,genre,achat,prix,disponible) values (13,'Le Rouge et le Noir','Stendhal','Roman','03-oct-92',110,'o')


Eliminamos uma linha da vista [ROMANS]:


A linha eliminada da vista [NOVELS] também foi eliminada da tabela [BIBLIO]. Vamos agora aumentar o preço dos livros na vista [NOVELS]:
Vamos verificar na [NOVELS]:

Qual foi o impacto na tabela [BIBLIO]?

Os preços dos romances foram, de facto, aumentados em 5% também na tabela [BIBLIO].
3.12.3. Eliminar uma vista
DROP VIEW nome_da_vista | |
elimina a vista denominada |
Exemplo
No explorador da base de dados, pode atualizar a vista (F5) para ver que a vista [ROMANS] desapareceu:

3.13. Utilização de funções de grupo
Existem funções que, em vez de atuarem em cada linha de uma tabela, atuam em grupos de linhas. Trata-se essencialmente de funções estatísticas que nos permitem calcular a média, o desvio padrão, etc., dos dados de uma coluna.
SELECT f1, f2, .., fn FROM tabela [ WHERE condição ] | |
Calcula as funções estatísticas fi em todas as linhas da tabela que satisfazem a condição. |
SELECT f1, f2, .., fn FROM tabela [ WHERE condição ] [ GROUP BY expr1, expr2, ..] | |
A palavra-chave GROUP BY divide as linhas da tabela em grupos. Cada grupo contém as linhas para as quais as expressões expr1, expr2, ... têm o mesmo valor. Exemplo: GROUP BY género agrupa livros do mesmo género. A cláusula GROUP BY autor,género agruparia livros com o mesmo autor e o mesmo género. A condição WHERE remove primeiro as linhas da tabela que não satisfazem a condição. Em seguida, os grupos são formados pela cláusula GROUP BY. As funções de agregação são então calculadas para cada grupo de linhas. |
SELECT f1, f2, .., fn FROM tabela [ WHERE condição ] [ GROUP BY expressão] [ COM a condição do grupo] | |
A cláusula HAVING filtra os grupos formados pela cláusula GROUP BY. Por isso, está sempre associada à presença da cláusula GROUP BY. Exemplo: GROUP BY género HAVING género!='NOVELA' |
As funções estatísticas disponíveis são as seguintes:
média da expressão | |
número de linhas para as quais a expressão tem um valor | |
número total de linhas na tabela | |
Valor máximo da expressão | |
Mínimo da expressão | |
soma da expressão |
Exemplos
![]()
Preço médio? Preço máximo? Preço mínimo?
![]()

Preço médio de um romance? Preço máximo?
![]()
Quantos livros de banda desenhada?
![]()
Quantos romances custam menos de 100 F?
![]()

Quantos livros existem e qual é o preço médio por livro para livros do mesmo género?
SQL> select upper(genre) GENRE,avg(prix) PRIX_MOYEN,count(*) NOMBRE from biblio group by upper(genre)

A mesma pergunta, mas apenas para livros que não sejam romances:
SQL>
select upper(genre) GENRE,avg(prix) PRIX_MOYEN,count(*) NOMBRE
from biblio
group by upper(genre)
having upper(GENRE)!='ROMAN'
![]()
A mesma consulta, mas apenas para livros com menos de 150 F:
SQL>
select upper(genre) GENRE,avg(prix) PRIX_MOYEN,count(*) NOMBRE
from biblio
where prix<150
group by upper(genre)
having upper(GENRE)!='ROMAN'
![]()
A mesma consulta, mas mantemos apenas os grupos com um preço médio dos livros >100 F
SQL>
select upper(genre) GENRE, avg(prix) PRIX_MOYEN,count(*) NOMBRE
from biblio
group by upper(genre)
having avg(prix)>100
![]()
3.14. Criação de um script SQL para uma tabela
O SQL é uma linguagem padrão que pode ser utilizada com muitos SGBDs. Para poder mudar de um SGBD para outro, é útil exportar uma base de dados ou simplesmente certos elementos da mesma sob a forma de um script SQL que, quando executado novamente noutro SGBD, será capaz de recriar os elementos exportados no script.
Aqui, iremos exportar a tabela [BIBLIO]. Selecionemos a opção [Extrair Metadados]:

Note que deve estar na base de dados da qual pretende exportar elementos. A opção inicia um assistente:
![]() |
onde gerar o script SQL:
| |
nome do ficheiro, se a opção [Ficheiro] estiver selecionada | |
o que exportar | |
Botões para selecionar (->) ou desmarcar (<-) os objetos a exportar |
Se quiséssemos exportar toda a base de dados, marcaríamos a opção [Extract All] acima. Queremos apenas exportar a tabela BIBLIO. Para tal, utilizando [4], selecionamos a tabela [BIBLIO] e, utilizando [2], especificamos um ficheiro:

Se pararmos por aqui, apenas a estrutura da tabela [BIBLIO] será exportada. Para exportar o seu conteúdo, precisamos de utilizar o separador [Tabelas de dados]:
![]() |
Utilize [1] para selecionar a tabela [BIBLIO]:
![]() |
Utilize [2] para gerar o script SQL:

Vamos aceitar o prompt. Isto permite-nos visualizar o script gerado no ficheiro [biblio.sql]:
- As linhas 1 a 3 são comentários
- As linhas 5 a 12 são SQL específico do Firebird
- as linhas restantes são SQL padrão que deve ser executável num SGBD que suporte os tipos de dados declarados na tabela BIBLIO.
Vamos executar este script no Firebird para criar uma tabela BIBLIO2 que será um clone da tabela BIBLIO. Para tal, utilize [Script Executive] (Ctrl-F12):

Vamos carregar o script [biblio.sql] que acabámos de gerar:

Modifique-o para manter apenas as partes relativas à criação da tabela e à inserção de linhas. A tabela é renomeada para [BIBLIO2]:
CREATE TABLE BIBLIO2 (
ID INTEGER NOT NULL,
TITRE VARCHAR(30) NOT NULL,
AUTEUR VARCHAR(20) NOT NULL,
GENRE VARCHAR(30) NOT NULL,
ACHAT DATE NOT NULL,
PRIX NUMERIC(6,2) DEFAULT 10 NOT NULL,
DISPONIBLE CHAR(1) NOT NULL
);
INSERT INTO BIBLIO2 (ID, TITRE, AUTEUR, GENRE, ACHAT, PRIX, DISPONIBLE) VALUES (2, 'Les fleurs du mal', 'Baudelaire', 'POèME', '1978-01-01', 120, 'n');
...
COMMIT WORK;
Vamos executar este script:
![]() | ![]() |
Podemos verificar no explorador de bases de dados que a tabela [BIBLIO2] foi criada e que possui a estrutura e o conteúdo esperados:
![]() | ![]() |








