Comando SELECT

A sintaxe SQL é bem simples e flexível. Mas, as poucas regras devem ser seguidas a risca para que a sua consulta seja devidamente interpretada. Ela se utiliza de palavras chave (keywords) que junto com os outros elementos da linguagem formam as sentenças ou cláusulas (clauses) SQL. Um grupo de sentenças forma um comando (statement).

O comando SELECT é usado para obter dados de uma ou mais tabelas. Quando um SELECT obtém dados de mais de uma tabela diz-se que é uma 'junção' (join). O SQL Local suporta a seguinte sintaxe :
OBS: Para muitas pessoas, uma descrição geral de sintaxe como a mostrada abaixo pode confundir mais do que ajudar na compreensão do assunto. Se for este o seu caso, não se preocupe, o assunto será tratado por partes no decorrer deste trabalho e você terá, ao final, uma compreensão geral e completa.

SELECT [DISTINCT] column_list
FROM table_reference
[WHERE search_condition]
[ORDER BY order_list]
[GROUP BY group_list]
[HAVING having_condition]
[UNION select_expr]

As clausulas entre colchetes são opcionais.
'column_list' é a lista das colunas (separadas por vírgula) das quais se obtém os dados.
'table_reference' é uma referência a tabela (ou tabelas) de onde se obtém os dados.
'search_condition' é uma expressão condicional limitativa.
'order_list' é a lista de colunas para a classificação.
'group_list' é a lista de colunas para o agrupamento.
'having_condition' é uma expressão condicional limitativa.
'select_expr' é um comando Select.

Para começar nosso estudo, vamos utilizar uma tabela (de nome Funcionarios) destinada a manter registro dos funcionários de uma empresa, com a seguinte descrição :
OBS: A tabela Funcionarios.db (montada em Paradox 7) está anexa a este trabalho. Ela contém apenas 7 registros (linhas) e 4 campos (colunas), para agilizar nosso trabalho. E antes que alguém reclame de baixos salários vale dizer que os dados são inteiramente aleatórios e não guardam nenhuma correspondência com a realidade.

MATRICULA NOME                    FUNCAO                      SALARIO
--------- --------------------    -------------------- --------------
        1 Fernando Bom            Gerente                   1.500,00 
        2 Adriano Segundo         digitador                   780,00 
        3 Mauricio Bello          Digitador                   700,00 
        4 Sebastião Bola          Faxineiro                   400,00 
        5 Firmino Pinto           Digitador                   800,00 
        6 Carla Lerda             Secretária                  500,00 
        7 Madalena Bolha          Digitadora                  750,00 
Sintaxe básica do comando SELECT :

SELECT <NOMES das COLUNAS SEPARADAS POR VÍRGULA>
FROM <NOME da TABELA>
WHERE <CLAUSULA CONDICIONAL>
ORDER BY <CLAUSULA DE CLASSIFICAÇÃO>

OBS: A sintaxe mostrada é simplificada e tem finalidades didáticas. A sintaxe completa suporta várias possibilidades adicionais, algumas das quais, a medida que se tornarem necessárias, serão apresentadas ao aluno.
Um comando mínimo terá pelo menos duas cláusulas :

Exemplo 1:
SELECT NOME, FUNCAO, MATRICULA
FROM FUNCIONARIOS

Resultado :
NOME                 FUNCAO               MATRICULA
-------------------- -------------------- ---------
Fernando Bom         Gerente                      1
Adriano Segundo      digitador                    2
Mauricio Bello       Digitador                    3
Sebastião Bola       Faxineiro                    4
Firmino Pinto        Digitador                    5
Carla Lerda          Secretária                   6
Madalena Bolha       Digitadora                   7
Comentários :
1)Note que as colunas são mostradas na ordem em que nós solicitamos (colocamos a 
  MATRICULA, que era a primeira coluna, na última posição).
2)São mostradas somente as colunas que nós solicitamos, a coluna Salário não aparece
  porque não foi solicitada.
3.Você pode testar os exemplos apresentados aquí usando um dos vários softwares
  de SQL. Veja na Introdução como utilizar o SQL Explorer do Delphi.

Exemplo 2:
SELECT *
FROM FUNCIONARIOS

Resultado :
MATRICULA NOME                 FUNCAO               SALARIO
--------- -------------------- -------------------- --------------
        1 Fernando Bom         Gerente                   5.500,00 
        2 Adriano Segundo      digitador                   780,00 
        3 Mauricio Bello       Digitador                   700,00 
        4 Sebastião Bola       Faxineiro                   400,00 
        5 Firmino Pinto        Digitador                   800,00 
        6 Carla Lerda          Secretária                  500,00 
        7 Madalena Bolha       Digitadora                  750,00 
Comentários :
1)O '*' é uma forma abreviada de indicar a seleção de todas as colunas da tabela. 
  A ordem das colunas obedece a ordem natural da tabela. Se quizermos mudar a ordem das
  colunas não poderemos usar o '*'.

Normalmente, queremos obter de uma tabela apenas certos registros (linhas) que satisfaçam a determinadas condições. A cláusula WHERE tem justamente esta finalidade.

Exemplo 3:
SELECT *
FROM FUNCIONARIOS
WHERE FUNCAO = "Digitador"

Resultado :
MATRICULA NOME                 FUNCAO                      SALARIO
--------- -------------------- -------------------- --------------
        3 Mauricio Bello       Digitador                   700,00 
        5 Firmino Pinto        Digitador                   800,00 
Comentários :
1)Há dois funcionários com a função Digitador, ambos foram mostrados.
2)E a Madalena Bolha ? (ela não é Digitador, ela é Digitadora, por isso não foi mostrada);
3)E o Adriano Segundo ?(ele não é Digitador, ele é digitador, por isso não foi mostrado);

Exemplo 4:
SELECT *
FROM FUNCIONARIOS
WHERE FUNCAO = "digitador"

Resultado :
MATRICULA NOME                 FUNCAO                      SALARIO
--------- -------------------- -------------------- --------------
Comentário : Não há nenhum funcionario com função digitador(em minúsculas).

Exemplo 5:
select * from funcionarios where funcao = "Digitador"

Resultado :
MATRICULA NOME                 FUNCAO                      SALARIO
--------- -------------------- -------------------- --------------
        3 Mauricio Bello       Digitador                   700,00 
        5 Firmino Pinto        Digitador                   800,00 
Comentários :
1) O SQL é insensível a caixa, pode estar em minúsculas ou maiúsculas que o resultado é o mesmo...
2) Quanto aos dados a coisa é diferente : Digitador e digitador não são considerados iguais.
3) Você pode colocar as diferentes cláusulas em linhas diferentes, ou lançar na mesma linha. 

Antes que sejamos chamados de machistas, vamos tentar incluir também a Madalena Bolha em nossa listagem :

Exemplo 6:
select *
from funcionarios
where funcao like "Digitador%"

Resultado :
MATRICULA NOME                 FUNCAO                      SALARIO
--------- -------------------- -------------------- --------------
        3 Mauricio Bello       Digitador                   700,00 
        5 Firmino Pinto        Digitador                   800,00 
        7 Madalena Bolha       Digitadora                  750,00 
Comentários :
1) O SQL seleciona todos os funcionários cuja função comece com Digitador.
2) O '%' indica que quaisquer caracteres adicionais devem ser aceitos. Se houvesse
   Digitadorzinho, Digitadoreiro e etc...eles seriam selecionados.

É, mas e o Adriano Segundo ? Só porque algum digitador digitou "digitador" ao invés de digitar "Digitador" ele vai ficar de fora da lista de Digitadores ?

Exemplo 6:
select *
from funcionarios
where UPPER(funcao) like UPPER("Digitador%")

Resultado :
MATRICULA NOME                 FUNCAO                      SALARIO
--------- -------------------- -------------------- --------------
        2 Adriano Segundo      digitador                   780,00 
        3 Mauricio Bello       Digitador                   700,00 
        5 Firmino Pinto        Digitador                   800,00 
        7 Madalena Bolha       Digitadora                  750,00 
Comentários :
1) UPPER() é uma das funções SQL. Ela transforma todos os caracteres em maiúsculas.
2) Podíamos usar LOWER() que é a função correspondente para minúsculas...

Exemplo 7:
select *
from funcionarios
where UPPER(funcao) like UPPER("Digitador%")
order by nome

Resultado :
MATRICULA NOME                 FUNCAO                      SALARIO
--------- -------------------- -------------------- --------------
        2 Adriano Segundo      digitador                   780,00 
        5 Firmino Pinto        Digitador                   800,00 
        7 Madalena Bolha       Digitadora                  750,00 
        3 Mauricio Bello       Digitador                   700,00 
Comentários :
1) A cláusula ORDER BY nos permite ordenar nossa query. No caso pelo NOME do funcionario.
2) Antes, quando não usávamos ORDER BY, a ordem era a ordem natural da tabela.

 

Exemplo 8:
select *
from funcionarios
where UPPER(funcao) like UPPER("Digitador%")
order by salario desc

Resultado :
MATRICULA NOME                 FUNCAO                      SALARIO
--------- -------------------- -------------------- --------------
        5 Firmino Pinto        Digitador                   800,00 
        2 Adriano Segundo      digitador                   780,00 
        7 Madalena Bolha       Digitadora                  750,00 
        3 Mauricio Bello       Digitador                   700,00 
Comentários :
1) A palavra chave DESC permite uma classificação em ordem descendente.
2) ASC é o equivalente para ordem ascendente. Por ser default não precisa ser indicada.

Expressões e Operadores

As expressões podem conter praticamente qualquer tipo de variáveis, strings, numéricas ou booleanas.
Os operadores podem ser de vários tipos : aritméticos, lógicos, de comparação, etc...
Como de praxe, vamos direto a parte prática.

Operadores :
Aritméticos :  +  –  *  /
De Comparação :  <  >  =  <>  >=  =<  IS NULL  IS NOTNULL
Lógicos :  AND   OR   NOT
Concatenação de Strings :  ||

Continuando com o exemplo da nossa tabela Funcionários, suponhamos que a empresa pretenda promover um reajuste geral de 10% nos salários de seus funcionários.

Exemplo 9:
SELECT MATRICULA,NOME,SALARIO AS SALARIO_ATUAL, SALARIO*1.10 funcionarios."SALARIO REAJUSTADO"
FROM FUNCIONARIOS
Resultado :
MATRICULA NOME                 SALARIO_ATUAL  SALARIO REAJUSTADO
--------- -------------------- -------------  ------------------
        1 Fernando Bom              5.500,00            6.050,00
        2 Adriano Segundo             780,00              858,00
        3 Mauricio Bello              700,00              770,00
        4 Sebastião Bola              400,00              440,00
        5 Firmino Pinto               800,00              880,00
        6 Carla Lerda                 500,00              550,00
        7 Madalena Bolha              750,00              825,00

Comentários :
1)Utilizamos um campo virtual (ou campo calculado) 'SALARIO REAJUSTADO' para mostrar o valor do novo salário de cada funcionário, salário este calculado com a expressão SALARIO*1,15.Note que não se criou nenhum campo novo, persistente, na tabela, apenas se montou em memória um campo calculado. Também não se reajustou o salário de ninguém, apenas exibiu-se quais seriam os valores dos salários reajustados.
2)Utilizamos, pela primeira vez, aliases (apelidos ou nomes substitutos) para nomes de campos. O campo SALARIO foi mostrado sob o alias SALARIO_ATUAL e o salario reajustado foi mostrado com o nome SALARIO REAJUSTADO.
Observe que SALARIO_ATUAL é uma palavra só, mas SALARIO REAJUSTADO contém duas palavras. Neste último caso foi necessário usar o nome da tabela seguido de ponto e do alias envolto em aspas para levar em conta que se trata de um nome com duas palavras. Se não tivessemos utilizado um alias, o título da última coluna seria SALARIO*1,10, o que, certamente, não é muito conveniente.
Pode-se usar alias também para os nomes de tabelas, o que mostraremos em exercícios posteriores.
3)A palavra reservada AS designando um nome substituto é optativa e pode ser dispensada, como fizemos para o campo calculado.
4)Você pode usar quaisquer expressões aritméticas válidas. A precedência natural dos operadores aritméticos + - * / pode ser alterada utilizando parênteses. Algumas implementações aceitam o operador MOD ou (%) para obter-se o resto de uma divisão.

Para mais um exemplo com expressões, suponhamos que a empresa, além do salário, pague um prêmio fixo de R$ 50,00 a todos os funcionários e que haja um desconto único de 10% de INSS também para todos os funcionários. Não há outros descontos. Vamos calcular todos os valores para obter o Líquido a pagar.

Exemplo 10:
SELECT MATRICULA,NOME,SALARIO SALARIO_MENSAL, "R$50.00" PREMIO, SALARIO+50 BRUTO,
(SALARIO+50)*0.10 INSS, (SALARIO+50)-(SALARIO+50)*0.10 LIQUIDO 
FROM FUNCIONARIOS
Resultado :
MATRICULA NOME             SALARIO_MENSAL   PREMIO       BRUTO    INSS   LIQUIDO
--------- ---------------- --------------  -------  ---------- ------- ---------
        1 Fernando Bom           5.500,00    50.00    5.550,00  555,00  4.995,00
        2 Adriano Segundo          780,00    50.00      830,00   83,00    747,00
        3 Mauricio Bello           700,00    50.00      750,00   75,00    675,00
        4 Sebastião Bola           400,00    50,00      450,00   45,00    405,00
        5 Firmino Pinto            800,00    50,00      850,00   85,00    765,00
        6 Carla Lerda              500,00    50.00      550,00   55,00    495,00
        7 Madalena Bolha           750,00    50,00      800.00   80,00    720,00

Comentários :
1) Se a expressão da última coluna fosse (SALARIO+50)*0.90, obteríamos o mesmo resultado.
2) Isto é apenas um exercício. Naturalmente que o cálculo de uma Folha de Pagamento real é bem mais complexo.

Olhando a 'Folha de Pagamento' acima, o responsável pelo pagamento do pessoal iria, sem dúvida, desejar uma coluna adicional contendo o acumulado da Folha que, ao final lhe informaria o Total a ser dispendido. Ele iria querer ver algo assim :

MATRICULA NOME             SALARIO_MENSAL   PREMIO     BRUTO   INSS  LIQUIDO  ACUMULADO
--------- ---------------- --------------  -------  -------- ------ -------- ----------
        1 Fernando Bom           5.500,00    50.00  5.550,00 555,00 4.995,00   4.995,00
        2 Adriano Segundo          780,00    50.00    830,00  83,00   747,00   5.742,00
        3 Mauricio Bello           700,00    50.00    750,00  75,00   675,00   6.417,00
        4 Sebastião Bola           400,00    50,00    450,00  45,00   405,00   6.822,00
        5 Firmino Pinto            800,00    50,00    850,00  85,00   765,00   7.587,00
        6 Carla Lerda              500,00    50.00    550,00  55,00   495,00   8.082,00
        7 Madalena Bolha           750,00    50,00    800.00  80,00   720,00   8.802,00

Esta determinação não é possível com um único comando SQL Local já que vc só pode utilizar em um campo calculado, valores do registro atual, ou seja, ao processar a matrícula 4 o SQL só 'vê' os campos do Sebastião Bola, não existe um campo totalizador para guardar o acumulado da Folha.
Este é um problema similar ao da Conta Corrente bancária em que se deseja, lançar, dia a dia, os valores de Crédito e Débido e mostrar, a cada dia, o saldo da conta.
Além da possibilidade evidente de se prover a tabela com um campo persistente para o Saldo, existem algumas soluções que fogem ao escopo deste trabalho. O exemplo só foi mostrado para alertar para uma das limitações mais comuns de campos calculados e que pode conduzir a resultados erroneos.

 

Funções

O SQL local tem as seguintes funções :
Funções Aritméticas agregativas (agregate functions) : 
   SUM(), para totalizar todos os valores numéricos em uma coluna;
   AVG(), para calcular a média aritmética de todos os valores numéricos não nulos em uma coluna
   MIN(), para determinar o valor mínimo em uma coluna
   MAX(), para determinar o valor máximo em uma coluna
   COUNT(), para fornecer a contagem do número de valores em uma coluna, satisfazendo a um
            determinado critério.
Os parâmetros podem conter expressões, por ex:
  SUM( alhos + bugalhos ), onde alhos e bugalhos são campos numéricos
  SUM( Campo1 * 10 ), onde Campo1 é numérico 
Funções de String (string functions) : 
  UPPER(), coloca toda a string em maiúsculas
  LOWER(), coloca toda a string em minúsculas
  TRIM(), para remover as repetições de um caracter especificado da esquerda, da direita ou
  de ambos os lados da string
  SUBSTRING() para criar uma substring de uma string.
  Exemplo :  SELECT SUBSTRING(NOME FROM 1 TO 4) FROM CLIENTES
Função de Data :
  EXTRACT () para extrair o ano, o mês, o dia, a hora, os minutose ou os segundos de um campo
             tipo date ou datetime.
  Exemplo :  SELECT EXTRACT(YEAR FROM DATA_ENTRADA) FROM FUNCIONARIOS

 

Exemplo 11:
SELECT COUNT(*)NUM_DE_FUNCIONARIOS 
FROM FUNCIONARIOS
Resultado :
NUM_DE_FUNCIONARIOS 
------------------- 
                  7 
Pode-se usar qualquer coluna que o resultado será o mesmo.
Exemplo 12:
SELECT COUNT(MATRICULA)NUM_DE_FUNCIONARIOS 
FROM FUNCIONARIOS
Resultado :
NUM_DE_FUNCIONARIOS 
------------------- 
                  7 
Exemplo 13:
SELECT COUNT(*)NUM_DE_FUNCIONARIOS 
FROM FUNCIONARIOS
WHERE SALARIO > 600
Resultado :
NUM_DE_FUNCIONARIOS 
------------------- 
                  5 
Exemplo 14:
SELECT SUM(SALARIO)TOTAL_DE_SALARIOS, MAX(SALARIO) MAIOR_SALARIO, MIN(SALARIO) MENOR SALARIO,
AVG(SALARIO) SALARIO_MEDIO
FROM FUNCIONARIOS
Resultado :
TOTAL_DE_SALARIOS   MAIOR_SALARIO   MENOR SALARIO    SALARIO_MEDIO
-----------------   -------------   -------------    -------------
         9.430,00        5.500,00          400,00         1.347,14
Comentários :
1) A função AVG() desconsidera valores nulos acaso existentes.
Min() e Max() podem ser usadas também com campos de caracteres.
Exemplo 15:
SELECT MIN(NOME), MAX(NOME) 
FROM FUNCIONARIOS
Resultado :
MIN OF NOME         MAX OF NOME    
-----------------   -------------- 
Adriano Segundo     Sebastião Bola 

Para o exemplo já utilizado anteriormente (Exemplo 10) em que a empresa, além do salário, paga um prêmio fixo de R$ 50,00 a todos os funcionários e há um desconto único de 10% de INSS, vamos calcular o Total Liquido a pagar.

Exemplo 16:
SELECT SUM((SALARIO+50)*0.90)LIQUIDO 
FROM FUNCIONARIOS
Resultado :
            LIQUIDO 
------------------- 
           8.802,00 

 

Junção (Join) de Tabelas

Para analisar junções, ou seja, consultas abrangendo mais de uma tabela, vamos utilizar 3 tabelas : Clientes,
Mercadorias, Vendas. Estas tabelas fazem parte do sistema de contrôle de uma loja de artigos de informática.


OBS:
1)As tabelas, montadas em Paradox 7, estão anexas a este trabalho e são mostradas a seguir.
2)Novamente, os valores das tabelas não tem nenhum compromisso com a realidade, servem apenas para exemplo.

3)As implementações mais elaboradas de SQL têm comandos próprios tais como JOIN, e suas qualificações OUTER, INNER, RIGHT, LEFT etc... Estes comandos não serão tratados aquí por não serem suportados pelo SQL-92. Por ser um trabalho essencialmente prático, não nos ocuparemos tampouco dos conceitos da teoria dos conjuntos por trá destas designações.

SELECT * FROM CLIENTES
COD_C NOME            SEXO ENDERECO           BAIRRO    TELEFONE   RENDA_MENSAL 
----- ------------    ---- ---------------    --------  ---------- ------------ 
1     MARCOS POSTO    M    R. Tortuosa,22     ALEGRIA   765-4321       5.500,00 
2     FERNANDO NANDO  M    R. da Lama, 31     TRISTEZA  775-7771         500,00 
3     MAURICIO MAURO  M    Tv. Transversa,11  BELEZA    765-3333       2.200,00 
4     MARINA MAURA    F    R. Descida, 27     ALEGRIA   765-8765       3.000,00 
5     MIRIAM SOLO     F    Ld.do Escorrega,2  TRISTEZA  777-7272       1.000,00 
6     THEREZA RIOS    F    R. da Praça, 15    BELEZA    787-7877       3.000,00 
SELECT * FROM MERCADORIAS
COD_M DESCRICAO                ESTOQUE       CUSTO          PRECO 
----- -----------------------  -------    ---------      -------- 
1     PC PENTIUM II-200              5     1.100,00      1.450,00 
2     PC PENTIUM I-166              12       900,00      1.100,00 
3     FAX-MODEM US.R.                6       140,00        180,00 
4     IMPRESSORA HP 690              3       400,00        600,00 
5     KIT MULTIMIDIA ADVEN           4       190,00        220,00 
6     PC PENTINUM II-233             2     1.300,00      1.600,00 
SELECT * FROM VENDAS
COD_C COD_M     DATA  QUANTIDADE 
----- ----- --------  ---------- 
3     1     15/10/98           1 
2     2     15/10/98           2 
1     5     16/10/98           1 
1     4     16/10/98           1 
5     5     18/10/98           2 
4     5     20/10/98           1 

O responsável pelo Estoque de nossa empresa fez a seguinte consulta ao nosso BD.

Exemplo 17:
SELECT V.COD_M, DESCRICAO,ESTOQUE ESTOQUE_INICIAL, SUM(QUANTIDADE) VENDAS, 
ESTOQUE-SUM(QUANTIDADE) ESTOQUE_ATUAL
FROM VENDAS V, MERCADORIAS M
WHERE V.COD_M = M.COD_M
GROUP BY  DESCRICAO,V.COD_M, ESTOQUE
Resultado :
COD_M DESCRICAO         ESTOQUE_INICIAL VENDAS ESTOQUE_ATUAL 
----- ---------         --------------- ------ ------------- 
    4 IMPRESSORA HP 690               3      1             2 
    5 KIT MULTIMIDIA ADVEN            4      4             0 
    2 PC PENTIUM I-166               12      2            10 
    1 PC PENTIUM II-200               5      1             4 
Comentários :
1) Observe o uso de aliases para tabelas (M para Mercadorias e V para Vendas) e de colunas 
   (ESTOQUE_INICIAL) para  ESTOQUES e  ESTOQUE_ATUAL para o campo calculado. Na  realidade,
   só utilizamos o alias de tabelas para o campo COD_M(Código da Mercadoria), já que este é
   o único campo em que os nomes coincidem. Por uma  questão  de  padronização,  poderíamos
   estender o alias a todos os campos.
2) Observe que o resultado (conjunto resultado) está classificado pela DESCRIÇÃO.  Quando se
   tem a cláusula Group By sem a cláusula Order By,o resultado é classificado na mesma ordem
   do Group By.
3) Na realidade queríamos a query agrupada apenas pela DESCRICAO, mas as regras nos obrigam a
   colocar todos os campos 'simples' na cláusula GROUP BY. No caso atual isto é indiferente  
   pois para cada DESCRICAO só existe um valor para COD_M e ESTOQUE. A  coisa  pode  ser  bem
   diferente se não for este o caso...
4) Vale repetir, ESTOQUE_ATUAL é um campo  virtual (não pertence a nenhuma tabela). Assim, não
   há nenhuma modificação em dados permanentes sendo este campo, apenas informativo, ajudando,
   por exemplo, na previsão de novas compras. Outro aspecto sôbre campos virtuais que as vezes
   conduz a resultados errôneos é o fato de que só podemos  utilizar valores (permanentes)  do
   registro atual na geração dos valores deste campo. Na  tabela de vendas acima, podemos  ver
   que há 3 vendas do Kit Multimídia (dias 16, 18 e 20/10/98).Não teríamos,  por exemplo, como
   demonstrar dia a dia, o estoque remanescente deste item,  por  não  termos  como transmitir
   o  saldo calculado de um lançamento para o lançamento  seguinte. (Veja, no exemplo seguinte
   mais uma limitação deste tipo de campo).

 

O responsável pelo Depto. Financeiro iria preferir a seguinte consulta :
Exemplo 18:
SELECT V.COD_M, DESCRICAO, SUM(QUANTIDADE*PRECO) R$_VENDAS, SUM(QUANTIDADE*CUSTO) 
R$_CUSTOS, SUM(QUANTIDADE*PRECO)-SUM(QUANTIDADE*CUSTO) R$_LUCRO, 
SUM(QUANTIDADE*(PRECO-CUSTO))*100/SUM(QUANTIDADE*CUSTO)  LUCRO_PERC
FROM VENDAS V, MERCADORIAS M
WHERE M.COD_M =V.COD_M
GROUP BY DESCRICAO, V.COD_M
Resultado :
COD_M  DESCRICAO             R$_VENDAS  R$_CUSTOS  R$_LUCRO           LUCRO_PERC
-----  ---------             ---------  ---------  --------   ------------------
    4  IMPRESSORA HP 690        600,00     400,00    200,00                   50
    5  KIT MULTIMIDIA ADVEN     880,00     760,00    120,00     15,7894736842105
    2  PC PENTIUM I-166       2.200,00   1.800,00    400,00     22,2222222222222
    1  PC PENTIUM II-200      1.450,00   1.100,00    350,00     31,8181818181818
Comentários :
1) Não se assuste com a aparente  complexidade  das fórmulas matemáticas. Lucro nada mais  é
   (pelo menos aquí neste exercício, onde não estamos preocupados com uma  apuração real de 
   lucro que certamente envolve outros fatores)que a diferença Vendas-Custo,Lucro Percentual
   é esta diferença expressa como percentagem do Custo. Seria mais fácil se pudëssemos fazer
   R$_VENDAS-R$_CUSTOS p/ calcular o Lucro,mas não podemos,já que estes campos são virtuais.
   e não podemos usar valores de campos virtuais para calcular valores de outros campos.
2) Observe também o imenso trem de casas decimais em alguns valores de lucro percentual. Não
   temos, no SQL Local, nenhuma forma prática de formatar estes valores. Todas as linguagens
   de programação possuem componentes (ou funções)que permitem fazer a formatação,resolvendo
   este tipo de problema.

O Setor de Vendas, por outro lado, estaria interessado em dados para fazer o faturamento das vendas realizadas e poderia fazer a seguinte consulta :

Exemplo 18:
SELECT C.NOME,C.TELEFONE,SUM(V.QUANTIDADE*M.PRECO) VALOR_COMPRAS,C.RENDA_MENSAL
FROM VENDAS V, MERCADORIAS M, CLIENTES C
WHERE C.COD_C = V.COD_C AND V.COD_M = M.COD_M
GROUP BY C.NOME,C.TELEFONE, C.RENDA_MENSAL
Resultado :
NOME            TELEFONE     VALOR_COMPRAS     RENDA_MENSAL 
----            --------     -------------     ------------ 
FERNANDO NANDO  775-7771          2.200,00           500,00 
MARCOS POSTO    765-4321            820,00         5.500,00 
MARINA MAURA    765-8765            220,00         3.000,00 
MAURICIO MAURO  765-3333          1.450,00         2.200,00 
MIRIAM SOLO     777-7272            440,00         1.000,00 
Comentários :
1)Utilizamos dados de 3 tabelas diferentes.
2)O valor da RENDA_MENSAL é apenas informativo. Talvez para orientar o Setor de Vendas sôbre 
possíveis pedidos de parcelamento
3)O telefone, bom... O telefone deve ser para marcar o dia da cobrança...

 

Sub Query

Podemos usar o resultado de uma subquery como argumento para a condição em uma cláusula Where. Note que o valor retornado pela subquery deve ser único, senão ocorrerá um erro...

Queremos verificar as vendas de kits multimidia. Não sabemos (ou não queremos usar ) o código da mercadoria.

Exemplo 19:
SELECT V.DATA,V.COD_M,M.DESCRICAO,V.QUANTIDADE 
FROM VENDAS V, MERCADORIAS M
WHERE V.COD_M = M.COD_M AND
V.COD_M = (SELECT COD_M FROM MERCADORIAS
WHERE DESCRICAO = "KIT MULTIMIDIA ADVEN" ) 
Resultado :
DATA     COD_M DESCRICAO              QUANTIDADE 
----     ----- ---------              ---------- 
16/10/98     5 KIT MULTIMIDIA ADVEN            1 
18/10/98     5 KIT MULTIMIDIA ADVEN            2 
20/10/98     5 KIT MULTIMIDIA ADVEN            1 

Quando não sabemos a descrição completa, podemos usar LIKE (desde que a subquery retorne um único valor).

Exemplo 20:
SELECT V.DATA,V.COD_M,M.DESCRICAO,V.QUANTIDADE
FROM VENDAS V, MERCADORIAS M
WHERE V.COD_M = M.COD_M AND
V.COD_M = (SELECT COD_M FROM MERCADORIAS
WHERE DESCRICAO LIKE "IMPRESSORA%" ) 
Resultado :
DATA     COD_M DESCRICAO            QUANTIDADE 
----     ----- ---------            ---------- 
16/10/98     4 IMPRESSORA HP 690             1 
Só há um item começando com IMPRESSORA.

O próximo comando provoca um erro pois o retorno da subquery não é único.

Exemplo 21 :
SELECT V.DATA,V.COD_M,M.DESCRICAO,V.QUANTIDADE
FROM VENDAS V, MERCADORIAS M
WHERE V.COD_M = M.COD_M AND
V.COD_M = (SELECT COD_M FROM MERCADORIAS
WHERE DESCRICAO LIKE "PC%" ) 
Provoca o seguinte erro : "Single row subquery produced more than one row..."