usando sql geração de relatorios parte 2

Upload: edison-silva-pinto

Post on 16-Oct-2015

45 views

Category:

Documents


0 download

TRANSCRIPT

  • 8/4/2014 DevMedia - Verso para impresso

    http://www.devmedia.com.br/websys.5/webreader_print.asp?cat=2&artigo=4194&revista=impressao_95#a-4194 1/14

    DevMedia - Verso para impresso

    Usando linguagem SQL paragerao de relatrios - Parte 2

    Vises e Consultas Aninhadas

    De que se trata o artigo

    Uso de linguagem SQL para criao de relatrios, desde recursos bsicos at os mais

    avanados. Este o segundo artigo de uma srie e neste texto apresento tcnicas de uso de

    vises e consultas aninhadas, que do s consultas um grande poder de seleo de dados. A

    discusso deste tema auxilia o leitor iniciante em SQL a criar consultas e relatrios mais

    sofisticados, que funcionam de maneira parecida com a parametrizao de dados. O artigo

    mostra tambm como tcnicas diferentes tm impacto significativo na performance de toda

    consulta.

    Em que situao o tema til

    Este artigo se destina ao leitor que trabalha com bancos de dados, seja ele administrador,

    arquiteto ou desenvolvedor, e apresenta recursos da linguagem SQL que o auxiliam na

    execuo de tarefas comuns como a criao de consultas e/ou relatrios.

    Resumo Devman

    O artigo trata de tcnicas de seleo de dados que so largamente utilizadas em consultas

    SQL: uso de vises e o uso de consultas aninhadas.

    Avalia o uso de consultas aninhadas nas diferentes clusulas de uma declarao de seleo

    de dados e ao final mostra como significativo o impacto na performance causado por

    pequenas variaes na estratgia de seleo de dados.

    Este o segundo artigo de uma srie que pretende apresentar recursos da linguagem SQL

    que ajudam na criao de relatrios. Repetindo o que escrevi no primeiro artigo:

    consenso entre os especialistas que SQL no a melhor linguagem para criao de

    consultas complexas. Apesar de esta ser uma verdade inegvel, muito comum nos

    depararmos com situaes em que ele, o SQL, a nica ferramenta que temos mo para

    criao de um relatrio. Isso ocorre muitas vezes por questo de custos, j que nem sempre

    economicamente vivel optar pela compra de uma soluo especializada. Este cenrio muito

    comum no dia-a-dia das empresas e, no fim das contas, somos forados a us-lo.

    Por esta razo, eu considero importante que todo profissional de banco de dados conhea os

    principais recursos do SQL para atender as solicitaes dos seus usurios.

    Neste sentido, importante relembrarmos uma definio bsica ao se trabalhar com

  • 8/4/2014 DevMedia - Verso para impresso

    http://www.devmedia.com.br/websys.5/webreader_print.asp?cat=2&artigo=4194&revista=impressao_95#a-4194 2/14

    consultas SQL que a definio de expresso SQL. A definio que temos para o termo

    expresso bastante simples: uma expresso retorna um valor. Os tipos utilizados em uma

    expresso variam bastante, cobrindo diferentes tipos de dados como string, numeric e

    boolean. Na verdade, quase tudo que inserirmos depois do SELECT ou FROM em um

    comando SQL pode ser considerado uma expresso.

    Caso voc queria encontrar um item ou grupo de itens em particular em seu banco de dados,

    voc precisar fazer uso de uma ou mais condies em suas queries. Podemos definir

    condies em consultas SQL utilizando a clusula where.

    Vimos no artigo anterior vrios recursos teis da linguagem SQL que nos ajudam na criao

    de relatrios. Neste texto atual, apresentaremos recursos da linguagem SQL para gerao de

    objetos especiais para manipulao dos dados, como vises e consultas aninhadas. O leitor

    com alguma familiaridade com SQL j deve ter visto tais recursos, por isso acrescentaremos

    aqui um estudo de performance para auxili-lo a explorar estas tcnicas de forma otimizada.

    Este material foi extrado de uma palestra sobre recursos oferecidos pelo DB2, porm iremos

    priorizar nestes artigos os recursos e as sintaxes que sejam padro ANSI SQL, ou seja, que

    esto ou deveriam estar disponveis em qualquer sistema gerenciador de bancos de dados

    (SGBD).

    Neste artigo, vamos falar do uso dos seguintes recursos SQL:

    Vises;

    Consultas aninhadas na clusula WHERE;

    Consultas aninhadas na clusula FROM;

    Consultas aninhadas na clusula SELECT.

    Na teoria de banco de dados, uma viso (view) consiste de uma consulta armazenada

    acessvel como uma tabela virtual em um banco de dados relacional ou um conjunto de

    documentos em um banco de dados orientado a documentos, composto pelo conjunto de

    resultados de uma consulta. Ao contrrio de tabelas comuns (tabelas base) em um banco de

    dados relacional, uma viso no faz parte do esquema fsico: uma tabela virtual dinmica

    computada a partir de dados no banco de dados. Alterar os dados em uma tabela altera os

    dados mostrados nas invocaes subsequentes da viso. Em alguns bancos de dados NoSQL

    as vises so a nica forma de consulta a dados.

    Vises podem oferecer vantagens sobre tabelas nos seguintes aspectos:

    Vises podem representar apenas um subconjunto dos dados contidos em uma tabela;

    Vises podem juntar e simplificar vrias tabelas em uma tabela virtual nica;

    Vises podem funcionar como tabelas agregadas, atravs dos mecanismos de agregao do

    banco de dados (sum, average, etc.) e apresenta os resultados calculados como parte dos

    dados;

    Vises podem esconder a complexidade dos dados, por exemplo, uma viso poderia se

    chamar Vendas2000 ou Vendas2001, particionando transparentemente a tabela bsica real;

    Vises ocupam muito pouco espao de armazenamento, o banco de dados contm apenas a

    definio de uma viso e no uma cpia de todos os dados que ele apresenta;

    Dependendo do SGBD utilizado, vises podem fornecer a segurana extra;

    Vises podem limitar o grau de exposio de uma ou mais tabelas para o mundo exterior.

    Assim como funes (function, na programao) podem fornecer abstrao, usurios do

    banco de dados podem criar abstraes usando vises. Em outro paralelo com as funes, os

    usurios do banco de dados podem manipular vises aninhadas, assim, uma viso pode

    agregar dados de outras vises. Sem o uso de vises a normalizao de bases de dados alm

    da segunda forma normal se tornaria muito mais difcil. Vises podem tornar mais fcil a

  • 8/4/2014 DevMedia - Verso para impresso

    http://www.devmedia.com.br/websys.5/webreader_print.asp?cat=2&artigo=4194&revista=impressao_95#a-4194 3/14

    criao de junes sem perdas de decomposio.

    Assim como as linhas em uma tabela base no tm qualquer ordem definida, as linhas

    disponveis atravs de uma viso no aparecem com qualquer classificao padro.

    A viso uma tabela relacional e o modelo relacional define uma tabela como um conjunto de

    linhas. Uma vez que as tabelas no so ordenadas - por definio - as linhas de uma viso

    tambm no so ordenadas. Portanto, uma clusula ORDER BY na definio de exibio no

    tem sentido.

    O padro SQL ANSI 2003 no permite que uma clusula ORDER BY em uma consulta SQL na

    instruo CREATE VIEW, assim como no permitida na instruo CREATE TABLE. No

    entanto, dados ordenados podem ser obtidos a partir de uma viso, da mesma forma como

    qualquer outra tabela - como parte de uma instruo de consulta.

    No entanto, alguns SGBDs (como Oracle e SQL Server) permitem uma viso a ser criada com

    uma clusula ORDER BY em uma subquery, afetando como os dados so exibidos.

    Em uma consulta SQL ao banco de dados, uma subconsulta (consulta aninhada dentro de

    outra consulta) uma consulta que utiliza os valores da consulta externa em sua clusula

    WHERE. A subconsulta avaliada uma vez para cada linha processada pela consulta externa.

    Voc pode usar uma subconsulta para os seguintes fins:

    Definir um conjunto de linhas que precisam ser inseridos em uma tabela;

    Definir um conjunto de resultados que ser usado para criao de uma viso;

    Definir um ou mais valores de uma instruo de atualizao (UPDATE);

    Fornecimento de valores para as clusulas WHERE, HAVING e START WITH para instrues

    SELECT, UPDATE e DELETE.

    Base de dados e consideraes Continuaremos a usar a mesma base de dados apresentada no primeiro artigo.

    Este banco de dados contm dados fictcios de demanda de produtos farmacuticos. A

    Figura 1 mostra o diagrama desta base.

    [abrir imagem em janela]

    Figura 1. Modelo de dados da base de exemplo.

    Vises As vises (do ingls views) so talvez os objetos mais comuns nos bancos de dados, mais at

  • 8/4/2014 DevMedia - Verso para impresso

    http://www.devmedia.com.br/websys.5/webreader_print.asp?cat=2&artigo=4194&revista=impressao_95#a-4194 4/14

    do que as prprias tabelas. De certo modo, as vises so tabelas virtuais, ou seja, elas se

    parecem com tabelas, mas as vises buscam dados que esto fisicamente armazenados em

    uma ou mais tabelas. Para o usurio, as vises se parecem tanto com as tabelas que fica

    difcil dizer com qual objeto se est trabalhando.

    J para os administradores de bancos de dados (DBAs), as vises so um excelente recurso

    para gerenciamento da segurana dos dados. Afinal, elas so objetos que funcionam somente

    para leitura, ou em outras palavras, no se consegue alterar dados atravs de vises (existem

    tipos especiais de vises que so exceo, mas no sero discutidas aqui). Alm disso, o

    administrador pode criar vises de modo a restringir o acesso apenas a determinados campos

    da tabela, impedindo por exemplo a leitura de campos especiais. Portanto, se voc no o

    DBA do banco de dados com o qual costuma trabalhar, provavelmente voc j acessou vises

    pensando que lia tabelas.

    As vises funcionam como declaraes de SELECT precompiladas, que so executadas

    quando o objeto correspondente solicitado. Virtualmente, qualquer SELECT pode ser salvo

    como viso. Basta para isso usar a sintaxe CREATE VIEW nome AS, seguida da declarao

    em questo.

    Uma exceo importante que as vises no aceitam a clusula ORDER BY. At existem

    recursos para se burlar esta limitao, mas a entramos no terreno das famosas gambiarras.

    O melhor mesmo respeitar o limite, a menos que seja realmente necessrio.

    Outra situao muito comum de uso de vises a de guardar de uma maneira simples

    consultas e/ou subconsultas que so executadas com muita frequncia.

    Esta abordagem interessante porque pode simplificar bastante o trabalho dos

    desenvolvedores, reduzindo a complexidade das declaraes SQL.

    Vamos ver um exemplo. Digamos que uma consulta muito frequente no nosso banco de

    dados seja a identificao do ranking de produtos mais vendidos no ms anterior. Esta

    consulta usada para uma srie de relatrios e, para no termos que reescrev-la em todos

    os relatrios, vamos trat-la como uma viso.

    Primeiramente vamos definir esta viso. Precisamos criar uma consulta de agregao que

    calcule a demanda por produto ocorrida no ms passado. Para isso, vamos precisar das

    tabelas de demanda e de produto (veja na Figura 1). Usaremos a funo de agregao SUM()

    para totalizar a demanda e agruparemos os dados por produtos. Para tornar a viso mais til,

    importante incluir o cdigo dos produtos, uma vez que todas as junes com outras tabelas

    e/ou vises sero feitas atravs destes cdigos.

    Como as vises no consideram ordenao de dados (e nem devem faz-lo), vamos

    esquecer a questo do ranking e apresentar apenas a demanda de cada produto.

    Para filtrar as datas, precisamos definir algo que seja genrico, do contrrio vamos ter que

    criar uma nova viso a cada mudana do ms.

    Existem muitas maneiras de se criar este filtro e cada SGBD traz seus prprios registros e

    funes para trabalhar com datas. (Veja informaes adicionais a este respeito na seo

    LEITURA RECOMANDADA).

    Eu preferi construir este filtro usando recursos o mais prximos possveis do padro ANSI

    SQL, ento usamos funes escalares como MONTH(), YEAR(), RIGHT() e a funo CAST(),

    que usada para converso de tipos de dados. Usaremos tambm o registro especial

    CURRENT_TIMESTAMP, que retorna a hora atual do sistema com preciso de (pelo menos)

    milissegundos, alm de estar implementado numa grande variedade de SGDBs relacionais,

    como DB2, ORACLE, SQL SERVER, FIREBIRD e POSTGRES.

    A ideia selecionar datas que estejam entre o dia 1 e o ltimo dia do ms anterior. Como

    no muito simples identificar o ltimo dia de cada ms, fica mais fcil dizer que a data

  • 8/4/2014 DevMedia - Verso para impresso

    http://www.devmedia.com.br/websys.5/webreader_print.asp?cat=2&artigo=4194&revista=impressao_95#a-4194 5/14

    desejada tem que ser maior ou igual ao 1 dia do ms anterior e menor do que o 1 dia do ms

    atual. Algo do tipo "Data >= 01/MesAnterior AND Data < 01/MesAtual".

    Usando as funes e registros de sistema citados acima, o filtro de datas ficar parecido com

    o apresentado na Listagem 1.

    Listagem 1. Parametrizao do filtro de datas do ms anterior.

    Data >= CAST(YEAR(CURRENT_TIMESTAMP) AS VARCHAR) -- ano

    +RIGHT('0'+CAST((MONTH(CURRENT_TIMESTAMP)-1) AS VARCHAR),2) -- mes

    + '01' -- dia

    AND

    Data < CAST(YEAR(CURRENT_TIMESTAMP) AS VARCHAR) -- ano

    + RIGHT('0'+CAST((MONTH(CURRENT_TIMESTAMP)) AS VARCHAR),2) -- mes + '01' - dia

    O leitor mais atento vai observar que a lgica da Listagem 1 tem uma falha: no funciona

    durante o ms de Janeiro. Na realidade, este problema com datas mais fcil de se resolver

    usando funes definidas pelo usurio (ou UDF), pois vai facilitar inclusive o entendimento da

    declarao SQL, j que esta lgica vai estar encapsulada numa funo. Porm as UDFs sero

    estudadas apenas no prximo artigo da srie. Por isso voltaremos a esta questo quando

    tratarmos do assunto. Por hora, vamos considerar que esta soluo adequada.

    Colocando tudo para funcionar e definindo a consulta como uma viso, temos o SQL

    apresentado na Listagem 2.

    Listagem 2. Criao da viso com demanda por produto no ms anterior.

    CREATE VIEW vwDemandaProdutoMesPassado AS

    SELECT P.pkProduto, P.Produto, SUM(T.ValorReal) AS Demanda

    FROM tblDemanda T

    INNER JOIN tblProduto P ON T.fkProduto = P.pkProduto

    WHERE T.Data >= CAST(YEAR(CURRENT_TIMESTAMP) AS VARCHAR)

    + RIGHT('0'+CAST(MONTH(CURRENT_TIMESTAMP) - 1 AS VARCHAR), 2)

    + '01'

    AND T.Data < CAST(YEAR(CURRENT_TIMESTAMP) AS VARCHAR)

    + RIGHT('0'+CAST(MONTH(CURRENT_TIMESTAMP) AS VARCHAR) , 2)

    + '01'

    GROUP BY P.pkProduto, P.Produto;

    A sentena da Listagem 2 para definio de uma viso pode ser executada em diferentes

    SGBDs, uma vez que usamos recursos padro ANSI SQL.

    Agora vamos usar esta viso na gerao de um relatrio simples: ranking com os 10

    produtos mais vendidos no ms anterior.

    Como a viso j traz estas informaes, basta fazermos a ordenao dos dados e

    apresentarmos apenas os 10 primeiros registros.

    Porm, como comentamos no artigo anterior, cada SGBD usa um recurso diferente para fazer

    esta exibio. A Listagem 3 mostra a declarao SQL deste relatrio no DB2.

    No SQL SERVER, a mudana seria pequena, j que teramos que usar o SELECT TOP para

    esta seleo (veja Listagem 4).

    Listagem 3. Sintaxe do DB2 para apresentao dos produtos mais vendidos.

    SELECT Produto, Demanda

    FROM vwDemandaProdutoMesPassado

    ORDER BY Demanda DESC

    FETCH FIRST 10 ROWS ONLY

  • 8/4/2014 DevMedia - Verso para impresso

    http://www.devmedia.com.br/websys.5/webreader_print.asp?cat=2&artigo=4194&revista=impressao_95#a-4194 6/14

    Listagem 4. Sintaxe do SQL Server para apresentao dos produtos mais vendidos.

    SELECT TOP 10 Produto, Demanda

    FROM vwDemandaProdutoMesPassado

    ORDER BY Demanda DESC

    Como as consultas mostradas nas Listagens 3 e 4 fazem a mesma coisa (s que em SGBDs

    diferentes), o resultado obviamente ser idntico, como vemos na Tabela 1.

    Existem vises que usam recursos especiais para melhorar a performance, por exemplo, no

    caso de vises que manipulam grandes volumes de dados. Estas vises costumam combinar a

    gravao de dados (fisicamente) num objeto especial, separado das tabelas fonte, e criar uma

    estratgia de indexao destes dados.

    O nome deste tipo de viso varia conforme o fornecedor do SGBD, mas as estratgias de

    melhoria de performance so parecidas: no DB2, elas so chamadas de Materialized Query

    Tables, no ORACLE so Materialized Views e no SQL SERVER so Indexed Views.

    A sintaxe de implementao destas vises varia conforme o SGBD e por esta razo no

    iremos nos estender neste assunto. O leitor interessado pode consultar os links apresentados

    na seo REFERNCIAS.

    [abrir imagem em janela]

    Tabela 1. Resultado da consulta das Listagens 3 e 4.

    Consultas aninhadas na clusula WHERE Quando usamos consultas dentro de outras consultas, damos a elas o nome de consultas

    aninhadas ou tambm subconsultas.

    Este um recurso largamente utilizado em SQL e multiplica consideravelmente o poder da

    linguagem.

    Pode-se usar consultas aninhadas em praticamente todas as clusulas SQL. Como nosso

    objetivo aqui a gerao de relatrios, vamos nos ater ao uso de consultas aninhadas em

    declaraes de SELECT, mas fica subentendido que estes recursos tambm podem ser

    usados em declaraes de INSERT, UPDATE e mesmo DELETE.

    Vamos a um primeiro exemplo, usando uma consulta aninhada na clusula WHERE.

    Considere que faremos um relatrio que identifica quais cidades tiveram demanda na data de

    24/dezembro/2010 exclusivamente para os trs produtos mais vendidos nos ltimos 30 dias.

    Precisamos montar este SQL agrupando os dados de demanda por cidade. Para isso, vemos

  • 8/4/2014 DevMedia - Verso para impresso

    http://www.devmedia.com.br/websys.5/webreader_print.asp?cat=2&artigo=4194&revista=impressao_95#a-4194 7/14

    na Figura 1 que precisaremos das tabelas de demanda, brick e cidade (j que a tabela de

    demanda no tem dados por cidade e sim por brick, como explicamos no ltimo artigo).

    A entra o problema: no queremos TODA demanda ocorrida nestas cidades, mas sim s a

    demanda dos trs produtos mais vendidos. Identificar estes produtos fcil, basta usar a viso

    vwDemandaProdutoMesPassado que criamos anteriormente e ela nos retorna a lista de

    cdigos de produto desejados. O que precisamos fazer usar esta lista como um novo filtro na

    clusula WHERE (veja Listagem 5).

    Listagem 5. Usando consulta aninhada para filtrar produtos com maior demanda.

    SELECT C.Cidade, SUM(T.ValorReal) AS Demanda

    FROM tblDemanda T

    INNER JOIN tblBrick B ON T.fkBrick = B.pkBrick

    INNER JOIN tblCidade C ON B.fkCidade = C.pkCidade

    WHERE T.Data = '2010-12-24'

    AND T.fkProduto IN (SELECT pkProduto

    FROM vwDemandaProdutoMesPassado

    ORDER BY Demanda DESC

    FETCH FIRST 3 ROWS ONLY)

    GROUP BY C.Cidade

    ORDER BY 1

    Como se v na Listagem 5, temos uma consulta dentro da outra, mas muito fcil identific-

    las. Eu usei a sintaxe do DB2 para seleo dos 3 melhores produtos (FETCH FIRST n ROWS

    ONLY), mas a converso desta sintaxe para outros SGBDs bastante simples, como

    mostramos no primeiro artigo da srie.

    Vamos a outro exemplo. No primeiro artigo da srie, mostramos uma consulta SQL para

    criao de um relatrio de demanda dos produtos Eupressin Cpr 20 Mg X 30 e o Norvasc Cpr

    10 Mg X 30 por estado da regio Sul no primeiro trimestre de 2011. Observe que temos trs

    filtros, um para as datas do 1 trimestre/2011, outro para os produtos Eupressin Cpr 20 Mg X

    30 e o Norvasc Cpr 10 Mg X 30 e o terceiro que queremos um relatrio de demanda por

    Estado, mas s aqueles que pertencem regio Sul.

    Como explicamos anteriormente, precisaremos de seis tabelas (veja Figura 1): de demanda,

    produtos, bricks, cidades, estados e regies.

    Na clusula SELECT, precisamos apenas dos campos Estado, Produto e da somatria da

    demanda.

    Mas existe um detalhe interessante neste relatrio que no apresentamos naquela

    oportunidade. Como o relatrio deve exibir um agrupamento por estado e por produto,

    precisamos destas duas tabelas como parte da clusula FROM. Mas no precisamos da tabela

    de regies ali, porque esta tabela s usada para filtro e no na clusula SELECT. Portanto,

    este um exemplo perfeito de quando podemos substituir junes por consultas aninhadas e

    obter um resultado mais eficaz. Esta declarao mostrada na Listagem 6.

    Listagem 6. Demanda por estado, usando consultas aninhadas.

    SELECT E.Estado, P.Produto, SUM(T.ValorReal) AS Demanda

    FROM tblDemanda T

    INNER JOIN tblBrick B ON T.fkBrick = B.pkBrick

    INNER JOIN tblCidade C ON B.fkCidade = C.pkCidade

    INNER JOIN tblEstado E ON C.fkEstado = E.pkEstado

    INNER JOIN tblProduto P ON T.fkProduto = P.pkProduto

    WHERE T.Data BETWEEN '2011-01-01' AND '2011-03-31'

    AND E.fkRegiao = (SELECT R.pkRegiao

  • 8/4/2014 DevMedia - Verso para impresso

    http://www.devmedia.com.br/websys.5/webreader_print.asp?cat=2&artigo=4194&revista=impressao_95#a-4194 8/14

    FROM tblRegiao R

    WHERE R.Regiao = 'SUL')

    AND P.Produto IN ('EUPRESSIN CPR 20 MG X 30',

    'NORVASC CPR 10 MG X 30')

    GROUP BY E.Estado, P.Produto;

    Consultas aninhadas na clusula FROM Em muitos relatrios, necessrio cruzar os dados de uma consulta com os dados de outro

    objeto, como uma tabela ou viso. Nestes casos, uma soluo usar consultas aninhadas na

    clusula FROM.

    Para decidir se voc deve criar a consulta aninhada no FROM ou em outra clusula

    qualquer, voc tem que analisar se a consulta aninhada precisa retornar campos que sero

    usados na clusula SELECT. Neste caso, ela tem que fazer parte da clusula FROM.

    Vamos ver um exemplo onde precisamos de um relatrio que tenha a demanda total no ms

    passado para os 5 principais produtos, que vamos chamar de Demanda, a demanda total do

    ms passado, chamada DemandaTotal, e a participao do produto na demanda total, que

    demos o nome de Participacao. Este ltimo campo calculado como a razo entre demanda

    do produto e demanda total (que nos d a frmula Participacao = Demanda/DemandaTotal *

    100).

    A estratgia aqui usar a viso vwDemandaProdutoMesPassado para termos a demanda

    dos 5 principais produtos e combin-la com uma subconsulta para termos a demanda total de

    todos os produtos.

    E por que necessrio usarmos uma consulta aninhada na clusula FROM? Ora, porque

    precisamos destes dois campos para calcularmos a participao dos produtos na demanda

    total!

    Veja a seguir como fica esta declarao (Listagem 7) e o seu resultando (Tabela 2) .

    Listagem 7. Participao dos 5 principais produtos na demanda total.

    SELECT S.Produto, S.Demanda, Q.DemandaTotal,

    S.Demanda/Q.DemandaTotal * 100 AS Participacao

    FROM vwDemandaProdutoMesPassado S,

    (SELECT SUM(X.Demanda) AS DemandaTotal

    FROM vwDemandaProdutoMesPassado X) Q

    ORDER BY S.Demanda DESC

    FETCH FIRST 5 ROWS ONLY;

    [abrir imagem em janela]

    Tabela 2. Resultado da consulta da Listagem 7.

    Com um pouco de ateno, muito fcil entender o que faz a consulta da Listagem 7. A

    sintaxe clara, as informaes esto todas no lugar certo e a declarao em si

    razoavelmente simples (ou curta, se preferir).

    Veja que usamos a viso vwDemandaProdutoMesPassado duas vezes na clusula FROM,

  • 8/4/2014 DevMedia - Verso para impresso

    http://www.devmedia.com.br/websys.5/webreader_print.asp?cat=2&artigo=4194&revista=impressao_95#a-4194 9/14

    uma diretamente e a segunda como parte de um subconsulta.

    Agora imagine como ficaria esta declarao (Listagem 7) se ns no tivssemos criado esta

    viso (veja novamente a Listagem 2)! A simplicidade e facilidade de leitura que mencionamos

    seriam totalmente perdidas.

    Esta consulta ilustra bem o uso de consultas aninhadas na clusula FROM, mas esta no a

    nica maneira de escrev-la. Existem outras estratgias para se criar este relatrio e

    voltaremos a falar deste exemplo ao longo desta srie de artigos.

    Consultas aninhadas na clusula SELECT Tambm possvel usar subconsultas na clusula SELECT, mas este um caso bastante

    especial e deve ser usado com muito cuidado.

    Em geral, usamos a subconsulta nesta clusula quando precisamos execut-la tomando

    como parmetros valores que so retornados pela consulta mais externa. Ou seja, a

    subconsulta na clusula SELECT reexecutada para registro da consulta externa! Portanto,

    fica evidente que essa estratgia um tanto perigosa e tem potencial para "derrubar" o seu

    servidor de banco de dados.

    Vejamos um exemplo mostrado por Joe Celko no seu livro "SQL for Smarties" (veja seo

    REFERNCIAS). Ele mostra uma tcnica de clculo de ranqueamento de registros usando

    uma consulta aninhada na clusula SELECT. A subconsulta faz uma contagem do nmero de

    registros que tem valor maior ou igual ao valor do registro da consulta principal. A Listagem 8

    adapta a declarao SQL da Listagem 7, incluindo a coluna "Ranking".

    E na Tabela 3 temos o relatrio correspondente.

    Listagem 8. Participao dos 5 principais produtos com Ranking.

    SELECT S.Produto, S.Demanda, Q.DemandaTotal,

    S.Demanda/Q.DemandaTotal * 100 AS Participacao,

    (SELECT COUNT(*)

    FROM vwDemandaProdutoMesPassado X

    WHERE X.Demanda >= S.Demanda) AS Ranking

    FROM vwDemandaProdutoMesPassado S,

    (SELECT SUM(X.Demanda) AS DemandaTotal

    FROM vwDemandaProdutoMesPassado X) Q

    ORDER BY S.Demanda DESC

    FETCH FIRST 5 ROWS ONLY;

    [abrir imagem em janela]

    Tabela 3. Resultado da consulta da Listagem 8.

    Note que a Listagem 8 traz duas consultas aninhadas, uma na clusula FROM, que j usamos

    na Listagem 7, mais a nova subconsulta na clusula SELECT. Observe tambm que esta

    subconsulta usa um filtro, comparando o valor de demanda do registro da consulta aninhada,

    identificado com o apelido X, com o valor do registro atual da consulta externa, identificado

  • 8/4/2014 DevMedia - Verso para impresso

    http://www.devmedia.com.br/websys.5/webreader_print.asp?cat=2&artigo=4194&revista=impressao_95#a-4194 10/14

    com o apelido S.

    Esta comparao quem garante a contagem correta dos registros, identificando assim a

    ordenao que desejamos. Mas lembre-se: a contagem, isto , a subconsulta, repetida para

    cada registro da consulta externa.

    Esta tcnica de ranqueamento antiga, usada com recursos padro ANSI SQL-92.

    Atualmente temos outros recursos para conseguir estes resultados. Mas, como voc j deve

    ter desconfiado, voltaremos a falar desta questo num dos prximos artigos desta srie.

    Um segundo exemplo: relatrio para calcular saldo em estoque.

    Para este exemplo, vamos usar uma tabela especial: a tabela de movimentos de estoque,

    definida na Listagem 9, onde os valores positivos indicam entradas no estoque e os valores

    negativos so as sadas do estoque.

    Listagem 9. Criao da tabela de Movimento de Estoque

    CREATE TABLE prod.tblMovtoEstoque (

    fkProduto INT NOT NULL,

    Data IDATE NOT NULL,

    Qtd INT NOT NULL,

    CONSTRAINT pkMovtoEstoque PRIMARY KEY (fkProduto, Data));

    INSERT INTO prod.tblMovtoEstoque VALUES

    (221720, '20100101', 1000),

    (221720, '20100102', -100),

    (221720, '20100103', -150),

    (221720, '20100104', -200),

    (221720, '20100105', -50),

    (221720, '20100106', -100),

    (578840, '20100105', 1000),

    (578840, '20100106', -80),

    (578840, '20100107', -150),

    (578840, '20100108', -250),

    (578840, '20100109', 500),

    (578840, '20100110', -350);

    Como a inteno calcular o saldo dirio por produto, a consulta precisa identificar todas as

    entradas e sadas de cada produto at a data em questo. E no podemos esquecer que os

    dados precisam obrigatoriamente estar ordenados na forma desejada. Considerando tudo

    isso, usamos uma consulta aninhada que calcula a coluna Saldo, como se v na declarao

    SQL da Listagem 10.

    A seguir, temos o resultado desta consulta na Tabela 4, em que destacamos em negrito as

    linhas que identificam o incio do balano para cada produto.

    Listagem 10. Saldo em Estoque por Produto

    SELECT fkProduto, Data, Qtd, (SELECT SUM(Qtd)

    FROM prod.tblMovtoEstoque S

    WHERE S.fkProduto = T.fkProduto

    AND S.Data

  • 8/4/2014 DevMedia - Verso para impresso

    http://www.devmedia.com.br/websys.5/webreader_print.asp?cat=2&artigo=4194&revista=impressao_95#a-4194 11/14

    Tabela 4. Resultado da consulta da Listagem 10.

    De maneira geral, quando voc imaginar que precisa usar consultas aninhadas na clusula

    SELECT, fique atento a trs condies:

    se sua subconsulta precisa mesmo interagir com os registros da consulta externa;

    atendida a condio anterior, verifique se esta subconsulta pode ou no ser substituda por

    uma juno de tabelas;

    se passar nos dois primeiros testes, fique atento com o nmero de registros retornados pela

    consulta externa.

    Lembre-se que uma consulta aninhada na clusula SELECT ser executada para cada

    registro retornado pela consulta externa. Ento imagine, por exemplo, uma consulta que

    retorne 10.000 registros. Mesmo que o otimizador de consultas do seu SGBD seja muito bom,

    muito provvel que esta consulta cause impacto significativo no seu servidor!

    Consideraes sobre performance Quando criamos relatrios, alguns cuidados especiais precisam ser tomados para garantir

    que os ndices existentes nas tabelas sejam usados adequadamente.

    Muitas vezes um pequeno deslize pode ter efeitos catastrficos. Por exemplo: a incluso de

    um campo com cdigo do produto na viso que definimos parece algo irrelevante, mas no .

    Todos os cdigos deste banco so indexados, o que uma boa prtica em termos de

    manuteno de bancos de dados. Graas existncia do campo cdigo nesta viso, qualquer

    juno ou filtro poder usar diretamente ndices existentes no banco, melhorando a

    performance.

    Vamos analisar mais um destes casos. Voltemos definio da viso que vimos no incio

    deste artigo. L, o filtro dos dados era feito atravs do campo data, com algo parecido com

    "Data >= 01/MesAnterior AND Data < ; 01/MesAtual".

    Vamos fazer trs testes aqui mudando apenas a clusula WHERE e veremos como isso afeta

    a performance da consulta. Observe que os trs casos estudados so verses de uma mesma

    consulta, ou seja, elas vo retornar exatamente o mesmo resultado, selecionando dados em

    uma tabela com pouco mais de 2 milhes de registros. Eu executei estes testes no SQL

    SERVER 2008 e mostraremos a seguir o grfico do plano de execuo de cada uma das

  • 8/4/2014 DevMedia - Verso para impresso

    http://www.devmedia.com.br/websys.5/webreader_print.asp?cat=2&artigo=4194&revista=impressao_95#a-4194 12/14

    consultas.

    No primeiro caso, faremos a consulta usando parmetros fixos (ou escalares se preferir). A

    Listagem 11 mostra esta declarao.

    Esta consulta no deve ser salva como uma viso, j que ela traz datas fixas. Mas este

    exemplo ilustra bem a questo de performance. Veja na Figura 2 o plano de execuo e no

    destaque o custo estimado desta rvore, que de 0,865938.

    No segundo teste, vamos considerar a verso parametrizada que usamos inicialmente,

    fazendo uso do registro de sistema CURRENT_TIMESTAMP e outras funes padro ANSI

    SQL, apresentadas novamente na Listagem 12. Note que a consulta da Listagem 12 sempre

    busca dados do ms anterior ao ms corrente e isso que precisamos.

    [abrir imagem em janela]

    Figura 2. Plano de execuo da consulta da Listagem 11.

    Listagem 11. Datas fixas

    SELECT P.Produto, SUM(T.ValorReal) AS Demanda

    FROM tblDemanda T

    INNER JOIN tblProduto P ON T.fkProduto = P.pkProduto

    WHERE T.Data >= '08/01/2011'

    AND T.Data < '09/01/2011'

    GROUP BY P.Produto

    Listagem 12. Datas parametrizadas

    SELECT P.Produto, SUM(T.ValorReal) AS Demanda

    FROM tblDemanda T

    INNER JOIN tblProduto P ON T.fkProduto = P.pkProduto

    WHERE T.Data >= CAST(YEAR(CURRENT_TIMESTAMP) AS VARCHAR)

    + RIGHT('0'+CAST(MONTH(CURRENT_TIMESTAMP) - 1 AS VARCHAR), 2)

    + '01'

    AND T.Data < CAST(YEAR(CURRENT_TIMESTAMP) AS VARCHAR)

    + RIGHT('0' + CAST(MONTH(CURRENT_TIMESTAMP) AS VARCHAR) , 2)

    + '01'

    GROUP BY P.Produto

    Ela at parece um pouco complexa, mas no final das contas estamos comparando os valores

    do campo DATA com dois valores calculados. Note na Figura 3 que o plano de execuo

    bastante parecido com o da Figura 2, apesar dela ter um custo bem maior: 1,77682 (105%

    maior que o custo da primeira consulta).

    Mas claro que o filtro usado no a nica opo de parametrizao. No prximo teste,

    filtramos os dados comparando o ms e o ano de cada registro. Parece uma melhoria, j que a

    lgica do filtro bem mais simples, como se v na Listagem 13.

  • 8/4/2014 DevMedia - Verso para impresso

    http://www.devmedia.com.br/websys.5/webreader_print.asp?cat=2&artigo=4194&revista=impressao_95#a-4194 13/14

    [abrir imagem em janela]

    Figura 3. Plano de execuo da consulta da Listagem 12.

    Listagem 13. Comparao por ms

    SELECT P.Produto, SUM(T.ValorReal) AS Demanda

    FROM tblDemanda T

    INNER JOIN tblProduto P ON T.fkProduto = P.pkProduto

    WHERE MONTH(T.Data) = (MONTH(CURRENT_TIMESTAMP) - 1 )

    AND YEAR(T.Data) = (YEAR(CURRENT_TIMESTAMP) )

    GROUP BY P.Produto

    Esta aparente simplicidade terrivelmente enganosa, porque aqui fazemos duas operaes

    para cada registro: calcular o ms e o ano do campo DATA. Considerando que a tabela

    grande (mais de 2 milhes de registros), esta operao certamente vai ter um custo alto.

    E de fato isso que acontece, pois o custo total de execuo desta rvore sobe para

    9,94425 (veja no quadro em destaque da Figura 4)! Este nmero 5,5 vezes maior do que o

    custo da consulta anterior e 11,5 vezes maior que a consulta com datas fixas.

    Para concluir esta seo, apresentamos a compilao de todos os testes na Tabela 5.

    [abrir imagem em janela]

    Figura 4. Plano de execuo da consulta da Listagem 13.

    [abrir imagem em janela]

    Tabela 5. Compilao dos testes de performance.

  • 8/4/2014 DevMedia - Verso para impresso

    http://www.devmedia.com.br/websys.5/webreader_print.asp?cat=2&artigo=4194&revista=impressao_95#a-4194 14/14

    Pode parecer redundante este comentrio, mas a importncia do tema o permite: sempre

    tome cuidado com as declaraes SQL que escreve. Pequenas alteraes podem ter grandes

    impactos em matria de performance!

    Concluso Vimos aqui como criar e usar vises e como lidar com consultas aninhadas.

    Estes so recursos muito poderosos e que esto disponveis h muito tempo. Mas eles

    ampliam em muito a gama de relatrios que podemos criar com declaraes SQL e o leitor

    interessado precisa aprender a us-los corretamente.

    No prximo artigo, vamos estudar recursos programveis da linguagem SQL, como funes

    definidas pelo usurio (UDFs), discutindo tanto as funes escalares como as tabulares.

    Espero que tenha gostado das informaes apresentadas. At breve!

    Wagner Crivelini

    Engenheiro formado pela UNICAMP, consultor em TI com 15 anos de experincia, particularmente

    em projetos de Business Intelligence. Atualmente trabalha na IBM, onde atua como DBA em projeto

    internacional.