SEFAZ-BA SGF DTI GETEC Secretaria da Fazenda do Estado da Bahia Superintendência da Gestão Fazendária Diretoria de Tecnologia da Informação Gerência de Tecnologia BOAS PRÁTICAS DE PROGRAMAÇÃO TRANSACT-SQL Versão 01.06.00 Salvador (Ba), maio de 2017 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 CONTROLE DE VERSÃO Versão Data 01.01.00 29/12/2006 Responsável Equipe AD (Padrões) Histórico 1. Substituição de “Departamento” por “Diretoria” no cabeçalho; 2. Padronização da capa; 3. Transferência do Manual de Padrões Banco para o Manual de Boas Práticas de Programação SQL do conteúdo que provê orientações sobre quando e como utilizar as views de linked server; 4. Criação do tópico “TABELAS DE HISTÓRICO” e inclusão neste tópico da recomendação para armazenar as datas de início e fim de vigência nas tabelas de histórico, quando fizer sentido. 1. Incluir conteúdo relacionado aos recursos do SQL Server 2005 (Tabelas Particionadas, Novos Tipos de Dados, Service Broker, Campos XML). 01.02.00 15/01/2009 Equipe AD (Padrões) 01.03.00 18/04/2011 Equipe AD (Padrões) 1. Inclusão do item “Controle de Execução de Scripts” no tópico “OBSERVAÇÕES”. 01.04.00 01/10/2012 Equipe AD (Padrões) 1. Inclusão do item “SET NOCOUNT ON” no tópico “OBSERVAÇÕES”. Equipe AD (Padrões) 1. Alteração do item "Cursores" no tópico "OBSERVAÇÕES"; 2. Inclusão do item “CLÁUSULA CATCH” no tópico “OBSERVAÇÕES”. Equipe AD (Padrões) 1. Inclusão do tópico "TRATAMENTO DE ERROS" 2. Alteração no tópico "OBSERVAÇÕES" , no item Cursores. 3. Alteração no tópico "REFERÊNCIAS" 01.05.00 20/05/2015 01.06.00 27/08/2015 Página 2 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 ÍNDICE 1. OBJETIVO ..................................................................................................................................... 4 2. TABELAS TEMPORÁRIAS ........................................................................................................ 4 3. TRANSAÇÕES .............................................................................................................................. 7 4. ÍNDICES ......................................................................................................................................... 9 5. PLANOS DE EXECUÇÃO ......................................................................................................... 11 6. VISÕES......................................................................................................................................... 14 7. VERTICALIZAÇÃO DE TABELAS ........................................................................................ 16 8. FUNÇÕES DEFINIDAS PELO USUÁRIO .............................................................................. 18 9. REFERÊNCIA CRUZADA ........................................................................................................ 20 10. TABELAS DE HISTÓRICO .................................................................................................. 22 11. TABELAS PARTICIONADAS .............................................................................................. 23 12. NOVOS TIPOS DE DADOS DO SQLSERVER 2005 ......................................................... 24 13. SERVICE BROKER ............................................................................................................... 24 14. CAMPOS XML ...................................................................................................................... 25 15. TRATAMENTO DE ERROS ................................................................................................. 26 15.1. TRATAMENTO COM @@ERROR .................................................................................... 26 15.2. TRATAMENTO COM TRY...CATCH ................................................................................ 26 16. OBSERVAÇÕES ..................................................................................................................... 29 17. CONCLUSÃO ......................................................................................................................... 31 18. REFERÊNCIAS ...................................................................................................................... 31 Página 3 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 1. 29/05/2017 OBJETIVO Este documento reúne um conjunto de técnicas de programação em Transact-SQL que devem ser adotadas para garantir um bom desempenho das aplicações no ambiente SEFAZ. 2. TABELAS TEMPORÁRIAS Recomendação: Não utilizar tabela temporária em estado de transação. Quando uma tabela temporária é construída, são gerados bloqueios em tabelas do catálogo (sysobjects, sysindexes e syscolumns) no TEMPDB. O TEMPDB é um banco compartilhado por todos os usuários de um servidor SQL Server e é um recurso extremamente utilizado na geração de tabelas temporárias, cursores, etc. Se uma tabela temporária for criada em estado de transação, os bloqueios serão mantidos até o final da transação, bloqueando outros usuários e comprometendo a performance do servidor. Tabelas temporárias podem ser substituídas por joins ou por tabelas derivadas em grande parte das situações, evitando os bloqueios no TEMPDB e melhorando a performance das aplicações. Seguem alguns exemplos de substituição de tabelas temporárias por joins e tabelas derivadas: Com tabelas temporárias: (solução não recomendada) CREATE TABLE #tipo_operacao ( cod_operacao int des_operacao char(20) ) not null, not null Resumo das operações: 2 criação/destruição de objetos 1 atualização 1 consulta INSERT INTO #tipo_operacao SELECT * FROM tipo_operacao WHERE cod_operacao > 10 SELECT * FROM operacao O INNER JOIN #tipo_operacao TO ON (O.cod_operacao = TO.cod_operacao) DROP TABLE #tipo_operacao Com JOIN: (solução recomendada) SELECT cod_operacao, des_operacao FROM operacao O INNER JOIN tipo_operacao TO ON (O.cod_operacao = TO.cod_operacao) WHERE TO.cod_operacao > 10 Página 4 Resumo das operações: 1 consulta 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 A seguir, analisaremos um exemplo mais trabalhado, semelhante à geração de relatórios. Neste exemplo, a solução apresentada é o uso de uma tabela derivada que nada mais é do que a substituição de uma tabela na cláusula FROM por uma consulta. O exemplo abaixo, assim como outros exemplos e tópicos abordados podem ser encontrados no site http://www.sql-server-performance.com Com tabelas temporárias: (solução não recomendada) -- CREATE OUR TEMPORARY TABLE CREATE TABLE #Temp_Example ( [CategoryID] INT NOT NULL, [Category_Count] INT NOT NULL ) Resumo das operações: 2 criação/destruição de objetos 1 atualização 1 consulta -- INSERT THE VALUES WE LATER NEED INTO THE TEMP TABLE INSERT INTO #Temp_Example (CategoryID, Category_Count) SELECT C.CategoryID, COUNT(*) AS Category_Count FROM Categories C INNER JOIN Products P ON C.CategoryID = P.CategoryID GROUP BY C.CategoryID, C.CategoryName -- JOIN ON THE TEMP TABLE TO GET OUR VALUES SELECT C.CategoryID, C.CategoryName, P.ProductName, P.UnitPrice, #Temp_Example.Category_Count FROM Categories C INNER JOIN Products P ON C.CategoryID = P.CategoryID INNER JOIN #Temp_Example ON C.CategoryID = #Temp_Example.CategoryID ORDER BY C.CategoryName -- DROP TEMPORARY TABLE DROP TABLE #Temp_Example Com tabela derivada: (solução recomendada) SELECT C.CategoryID, C.CategoryName, P.ProductName, P.UnitPrice, CT.Category_Count FROM Categories C INNER JOIN Products P ON C.CategoryID = P.CategoryID INNER JOIN ( SELECT C.CategoryID, COUNT(*) AS Category_Count FROM Categories C INNER JOIN Products P ON C.CategoryID = P.CategoryID GROUP BY C.CategoryID, C.CategoryName )CT ON C.CategoryID = CT.CategoryID ORDER BY C.CategoryName Resumo das operações: 1 consulta Página 5 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 De forma geral, o número de operações para implementação de uma solução com tabelas temporárias é maior e as operações são mais custosas (criação e exclusão de objetos, atualização de dados, etc.), logo, evite o uso de tabelas temporárias e procure trabalhar com joins e tabelas derivadas sempre que possível. Uma outra alternativa é utilizar o tipo table para armazenar os dados temporários de forma menos custosa. Para maiores informações sobre tabelas derivadas, consulte “Derived Tables” no Books Online. O tipo de dados table pode ser encontrado em “Table Data Type” também no Books Online. Informações sobre performance de tabelas temporárias X tabelas derivadas podem ser encontradas em http://www.sql-server-performance.com Página 6 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 3. TRANSAÇÕES Recomendação: Procurar definir as transações com o menor escopo possível. Transações são utilizadas para garantir que um conjunto de comandos SQL seja executado como uma unidade atômica de código. Isto significa que todos os comandos devem ser executados juntos ou nenhum deles poderá ser executado. Além disso, o banco deve garantir que os comandos de uma transação não interfiram no resultado de outras transações que estejam executando concorrentemente. Para garantir estas premissas, o custo para banco de dados pode ser altíssimo, por isso, as transações devem ser definidas com bastante critério. Só precisam estar em estado de transação comandos que devem ser executados de maneira conjunta e não podem ser executados separados. Comandos que podem ser executados de forma independente não devem ser executados em estado de transação. Exemplos comando2 Transações grandes: (solução não recomendada) comando1 comando4 comando3 bloqueio Tabela comando2 comando1 bloqueio comando4 comando3 comando6 comando2 comando5 comando1 comando4 comando3 Transações pequenas: (solução recomendada) comando2 comando1 comando2 comando1 Tabela comando2 comando2 comando1 comando2 comando1 comando1 Página 7 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 O resultado da definição de transações com escopo muito grande é que uma quantidade menor de processos poderá executar concorrentemente e a performance das aplicações diminuirá consideravelmente. Por outro lado, se as transações forem definidas com escopo pequeno, mais transações vão poder ser executadas concorrentemente e a performance das aplicações será melhor. Observações sobre finalização de transações: a. Dentro de um trigger, todo ROLLBACK deve ser acompanhado por um RETURN, de forma a garantir que o trigger será finalizado naquele ponto. Se um RETURN não for executado, o trigger executará o ROLLBACK e continuará seguindo o fluxo normal de execução, o que pode levar a inconsistências no banco de dados. b. Dentro de stored procedures, todo ROLLBACK deve ser acompanhado por um RAISERROR, garantindo que a aplicação será informada de que houve uma falha na operação, dando o tratamento adequado. Se o RAISERROR não estiver presente, a aplicação intepretará o resultado como sucesso e não fará nenhum tipo de tratamento. Exemplos Errado: Correto: CREATE PROC up_errada AS ... IF @@error = 0 commit transaction ELSE rollback transaction ... CREATE PROC up_correta AS ... IF @@error = 0 commit transaction ELSE BEGIN rollback transaction raiserror 50001 ‘Erro na procedure!!!’ END ... Para maiores informações sobre transações, consulte “Transactions” no Books Online. Informações sobre bloqueios (locks) podem ser encontradas no site http://www.sql-server-performance.com Página 8 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 4. ÍNDICES Recomendação: Utilizar índices clustered em tabelas pouco atualizadas e índices nonclustered nas demais. a) Definição de Índice Índices são estruturas de dados utilizadas para otimizar consultas em bancos de dados. Os índices evitam a necessidade de uma leitura extensiva (table scan) em uma tabela para localizar um ou mais registros. A figura abaixo ilustra a estrutura de um índice (ix_usuario01) criado para o campo cod_usuario que armazena o código do usuário: 1 5 10 ... cod_usuario 1 2 3 4 5 6 7 8 9 10 ... nome Ana Mario Joao Jose Paula Marcos Joana Simão Pedro Fernando ... Com o índice acima, uma consulta como select nome from usuário where cod_usuario = 7 seguiria o caminho indicado pelas seta tracejada, ou seja, escolheria o apontador do índice que mais se aproximasse do registro desejado e realizaria uma pequena busca na tabela de usuário para encontrar o registro. Se o índice não existisse, o SQL Server seguiria o caminho indicado pela seta pontilhada, ou seja, faria uma busca extensiva na tabela de usuário até encontrar o registro desejado. As principais conseqüências da falta de um índice adequado seriam o aumento no tempo da consulta e um aumento dos bloqueios na tabela de usuário. b) Tipos de Índice Existem 2 tipos de índices, Clustered e Nonclustered: Nos índices Clustered existe uma relação direta entre a ordenação do índice e a ordenação física dos dados da tabela. Essa organização maximiza a performance de consultas que retornam dados ordenados pelo(s) campo(s) do índice (exemplo 1) e consultas que trabalham com intervalos de valores (exemplo 2). Exemplo 1 - Índice clustered no campo cod_usuario: SELECT cod_usuario, nom_usuario FROM usuario ORDER BY cod_usuario Exemplo 2 - Índice clustered no campo dtc_atualizacao SELECT nom_usuario FROM usuario WHERE dtc_atualizacao BETWEEN ‘2003/04/01’ AND ‘2003/07/01’ Página 9 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 Apesar dos benefícios citados, os índices clustered geram um custo adicional durante a atualização porque os dados precisam ser reordenados fisicamente a cada atualização. Em virtude disso, índices clustered devem ser criados somente em tabelas que não são atualizadas ou que tenham uma freqüência de atualização muito baixo. Obs.: Pelo fato de exigir uma ordenação física dos dados, somente um índice clustered pode ser criado para cada tabela. Nos índices Nonclustered não existe uma relação direta entre a ordenação do índice e a ordenação física dos dados da tabela. Existe um custo em operações de atualização de tabelas que utilizem índices nonclustered, mas o overhead é muito menor do que ocorre com os índices clustered. A criação de índices nonclustered deve ser avaliada pelo desenvolvedor seguindo as recomendações sobre planos de execução documentadas neste manual. Obs.: Pelo fato de não exigir uma ordenação física dos dados, vários índices nonclustered podem ser criados para cada tabela. c) Chaves Primárias x Índices Secundários Quando uma constraint do tipo chave primária é criada para uma tabela, o SQL Server cria automaticamente um índice na tabela. Este índice por padrão é CLUSTERED, portanto, quando não houver necessidade de criação de um índice do tipo clustered, é importante especificar explicitamente a cláusula NOCLUSTERED no comando ALTER TABLE. Os exemplos a seguir ilustram o fato: ALTER TABLE usuario ADD CONSTRAINT ix_usuario01 PRIMARY KEY (cod_usuario) Será criado automaticamente um índice chamado ix_usuario01 do tipo clustered. ALTER TABLE usuario ADD CONSTRAINT ix_usuario01 PRIMARY KEY NONCLUSTERED (cod_usuario) Será criado automaticamente um índice chamado ix_usuario01 do tipo nonclustered. Além da chave primária podem ser criados diversos índices por tabela, chamados de índices secundários. Vale ressaltar que quanto maior for a quantidade de índices em uma tabela, maior será o overhead de atualização. Índices secundários são criados por padrão como nonclustered, devendo ser usada a cláusula CLUSTERED somente quando for avaliada a necessidade de criação de um índice do tipo clustered. Para maiores informações sobre índices, consulte “Indexes” no Books Online. Página 10 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 5. PLANOS DE EXECUÇÃO Recomendação: Analisar periodicamente os planos de execução dos comandos SQL, avaliando a necessidade de criação de índices para otimização. A partir da versão 7.0, o SQL Server disponibilizou uma ferramenta para avaliação do plano de execução de comandos SQL, em modo gráfico. Esta ferramenta está embutida no Query Analyzer (antigo ISQL/W) e será detalhada a seguir. Vamos tomar como exemplo uma tabela “usuario” que contém a estrutura abaixo: cod_usuario nom_usuario int varchar(30) not null not null Esta tabela já contém um índice primário no campo cod_usuario. No primeiro exemplo será executada a seguinte consulta no banco: SELECT * FROM usuario Para gerar o plano de execução estimado dessa consulta, basta selecionar o(s) comando(s) em questão e usar CTRL+L. O plano de execução da consulta acima é o seguinte: Neste caso, o plano estimado indica que houve um table scan, ou seja, uma varredura em toda a tabela para retornar os registros desejados. O table scan é uma operação que deve ser evitada porque consome uma quantidade grande de recursos para ser efetivada, diminuindo a performance das consultas. Na consulta acima houve table scan por dois motivos: Não foi determinado um filtro (join, cláusula WHERE ou HAVING); A lista de campos não foi colocada na consulta e foi usado * para retornar todos os campos. Vamos modificar um pouco a consulta, supondo que só será necessário o campo cod_usuario: SELECT cod_usuario FROM usuario O plano de execução da consulta passa a ser o seguinte: Página 11 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 Para uma visão detalhada do que ocorreu em cada etapa, basta apontar com o mouse para o ícone da etapa desejada. Um resumo da etapa será exibido com informações sobre a operação realizada, o custo de I/O, CPU e dados adicionais. (figura ao lado) Neste caso foi realizado um Index Scan devido aos seguintes motivos: Não foi determinado um filtro (isto justifica o scan); A lista de campos foi colocada na consulta (neste caso só o campo cod_usuario). Como existe um índice no campo cod_usuario, o índice é utilizado, mas ainda permanece o scan (varredura). A situação é melhor do que a primeira consulta porque o table scan foi transformado em index scan, ou seja, a varredura deixou de ser na tabela e passou a ser no índice. De qualquer forma, esta ainda não é uma situação ideal, como veremos adiante. Vamos adicionar um filtro à consulta e observar o resultado do plano de execução: SELECT cod_usuario FROM usuario WHERE cod_usuario >1000 Plano estimado: Com o filtro o index scan passou a ser index seek, ou seja, o otimizador trabalhará somente com o conjunto de registros especificado no filtro. Note que o index seek consumiu menos recursos de I/O e CPU do que o index scan. Essa diferença pode ser ainda maior quando a tabela consultada tiver um número elevado de registros. O index seek é a operação ideal na maioria dos casos e deve ser um dos objetivos de toda otimização de consulta. Obs.: Nem sempre o index seek pode ser alcançado numa otimização. Se o número de registros que você está pesquisando for muito grande em relação ao total de registros da tabela, o otimizador tende a fazer um index scan ou table scan, independentemente da existência de filtros e índices. Outros fatores que contribuem para a execução de scans são o uso de funções (datepart, convert, etc) na cláusula WHERE e o uso indevido de Página 12 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 aspas em comparações (exemplo: WHERE num_inscicao_estad = ‘334’, sendo num_inscricao_estad um campo do tipo INT. Vamos analisar agora um exemplo com mais um campo na consulta: SELECT cod_usuario, nom_usuario FROM usuario WHERE cod_usuario >1000 Plano estimado: No plano de execução da consulta acima, surgiu uma nova etapa chamada Bookmark Lookup, indicando que, apesar da pesquisa no índice, campos adicionais correspondentes aos registros procurados serão lidos diretamente da tabela. Esta operação gera um custo adicional para a consulta. Para eliminar esta etapa teremos que criar um índice que contenha todos os campos desejados e todos os campos do filtro. Isto nem sempre é possível porque consultas complexas utilizam muitos campos tanto na cláusula SELECT quanto na cláusula WHERE. Nestes casos o lookup é inevitável. Existem outros tipos de operações que podem aparecer no plano de execução estimado, mas as operações citadas acima são as que mais interferem na peformance das consultas no banco de dados. Para maiores informações sobre planos de execução, procure “Execution Plan” no Books Online. Página 13 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 6. VISÕES Recomendações: Utilizar visões para consultas com algum grau de complexidade e quando existir a possibilidade de reutilização; Verificar o código da visão antes de utilizá-la numa consulta. Uma visão (ou view) é um mecanismo usado pelo servidor de banco de dados para encapsular o código de uma consulta. Esse encapsulamento traz 2 grandes benefícios: simplicidade de uso e possibilidade de reutilização de código. Apesar disso, a complexidade de uma consulta pode ser camuflada através de uma visão, logo, a clareza fica comprometida. De uma forma geral, visões devem ser utilizadas para consultas com um certo grau de complexidade e que possam ser reutilizadas no mesmo sistema ou por uma equipe de desenvolvedores. Criar visões indiscriminadamente adiciona complexidade ao ambiente desnecessariamente, portanto, é importante ter bom senso para decidir se uma visão deve ou não ser criada. É importante também verificar o código da visão antes de utilizá-la numa consulta, para ter uma noção da complexidade da consulta como um todo. Visões Indexadas Recomendação: Utilizar visões indexadas em consultas complexas e quando a tabela for atualizada com pouca freqüência. Quando uma visão é criada, seu código é gravado no banco de dados e executado a cada chamada à visão. Para visões mais complexas, com diversos joins e filtros, o tempo da consulta pode ser consideravelmente alto, prejudicando a performance da aplicação. Para minimizar esse problema, a partir do SQL Server 2000, foi adicionado o conceito de Visão Indexada (Indexed View). Para criar uma visão indexada, é necessária a criação de um índice clustered em uma visão. O grande benefício das visões indexadas é o fato do resultado da consulta que gera a visão ficar armazenado em disco, permitindo a criação de índices. Apesar disso, visões indexadas não devem ser criadas em tabelas com atualizações constantes ou pesadas, pois a interação entre a tabela fonte e a visão ficará comprometida. Para maiores informações sobre visões indexadas, consulte “Indexed Views” no Books Online. Visões de Linked Server Recomenda-se utilizar visões de linked server quando todas as situações abaixo ocorrerem: Quando for necessário prover acesso a tabelas de outros servidores; A visão de linked server servirá para garantir a independência das aplicações em relação aos nomes dos servidores Quando o volume de dados transferido for pequeno; A adoção de visões de linked server para volumes grandes de dados acarreta um tráfego indesejado entre os servidores e aumenta os riscos de perda de conexão. Página 14 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 Quando a dependência do servidor remoto for pequena. Para essa dependência ser pequena, a visão de linked server envolvida deve ser chamada com uma freqüência baixa (seja através de um trigger, seja através de uma stored procedure). As funcionalidades críticas devem evitar o uso de visões de linked server, pois estas representariam um fator a mais de risco para a aplicação. Observações: Quando houver um procedimento de alta complexidade no servidor remoto com retorno reduzido para o servidor de origem, pode-se utilizar chamadas remotas a procedures em lugar de views de linked server. Quando houver um procedimento e este não for de alta complexidade, o analista deve escolher entre uma chamada remota a procedure (caso a lógica do procedimento dependa de objetos do servidor remoto), uma visão chamada de uma procedure local (caso o procedimento possa estar inteiramente no servidor local) ou replicação (em último caso). A decisão sobre utilizar views de linked server, chamadas remotas a stored procedures, replicação ou, ainda, múltiplas conexões deve ser sempre alinhada com a GETEC. Características: Não podem ter qualquer filtro; O número de campos retornados deve ser o menor possível; Deve existir no máximo uma visão de linked server por tabela; Cada visão de linked server só pode referenciar uma única tabela. Um dos seguintes nomes deve ser utilizado como nome do servidor remoto: “CORPORATIVO” (para o SSED03W, SSED09W e SSED20W) e “FINANCEIRO” (para o SSED26W, SSED09W_02 e SSED20W_02). Caso haja a necessidade de acesso via linked server (visão ou stored procedure), para um servidor diferente dos citados, a GETEC deve ser consultada. Página 15 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 7. VERTICALIZAÇÃO DE TABELAS Recomendação: Utilizar uma estrutura vertical para armazenar consolidados. Algumas tabelas, entre elas boa parte dos consolidados, estão modeladas com uma estrutura semelhante a que segue: ano 2000 2001 2002 2003 ... val01 86 23 49 76 ... val02 56 65 74 15 ... val03 54 78 95 12 ... val04 89 50 47 34 ... ... ... ... ... ... ... Nesta estrutura existe uma chave (o ano) e uma coluna para cada mês. Quando se deseja somar todos os valores de um determinado ano, basta localizar o registro a partir da chave e somar os campos val01, val02, val03, etc. Por exemplo, para somar os valores correspondentes ao ano 2003 seria necessário executar a seguinte consulta: SELECT val01 + val02 + ... FROM consolidado WHERE ano = 2003 Apesar de funcionar bem para consultas desse tipo, esta estrutura possui uma série de limitações: Quando o valor de um determinado mês ainda é desconhecido (por exemplo, os valores de 2003 correspondentes aos meses de agosto, setembro, outubro...), há desperdício de espaço em disco pois está sendo reservado espaço para armazenar um valor que ainda não existe. Para consultas que fogem ao padrão citado, a estrutura acima é deficiente do ponto de vista de performance. Se for necessário, por exemplo, consolidar os resultados de dezembro de 2002 a fevereiro de 2003 seria necessária uma consulta mais complexa com CASE, IFs, cursores, ou o uso de variáveis. Além disso, como o período a ser consultado pode variar, seria necessária a utilização de consultas dinâmicas, degradando ainda mais a performance. A criação de índices nesse caso fica limitada à chave (ano), pois, para otimizar filtros nos campos val01, val02, etc, seria necessário criar índices para todas as combinações possíveis dos campos e isso geraria um overhead de atualização altíssimo. Página 16 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 Uma alternativa para atender às diversas demandas de consultas em consolidados desse tipo é utilizar uma estrutura vertical semelhante a que segue: dtr 200207 200208 200209 200210 200211 200212 200301 200302 200303 ... val 77 54 13 89 104 63 76 15 12 ... Nesta estrutura o período é representado por uma data reduzida, composta por ano e mês, permitindo que consultas de períodos possam ser realizadas de forma direta, como no exemplo abaixo: SELECT val FROM consolidado WHERE dtr BETWEEN 200212 AND 200302 As principais vantagens deste modelo são: Melhor aproveitamento de espaço: os espaço em disco só será ocupado quando o valor de um determinado mês for conhecido. Não existirão, por exemplo, registros para agosto, setembro, outrubro, novembro e dezembro de 2003 porque estes valores ainda não foram calculados; Facilidade de otimização: com poucos índices é possível otimizar a maioria das consultas no formato citado. Um índice para o campo dtr seria usado para consultas diretas (dtr = xxxxyy) ou por períodos (dtr between xxxxyy and aaaabb). Outro índice, em val, garantiria consultas rápidas por intervalos de valores (val > n, por exemplo). Observação sobre datas reduzidas: O modelo para datas reduzidas (ano+mês = AAAAMM) traz benefícios em consultas por períodos e minimiza a necessidade de operações aritméticas durante a execução das consultas. Apesar disso, é importante que seja sempre avaliado como as datas serão representadas para evitar redundâncias. A estrutura vertical pode ser utilizada não só em consolidados, mas em diversas outras tabelas que sigam o modelo acima. O modelo vertical deve ser utilizado como padrão, podendo ser usada uma abordagem horizontal em casos específicos. Dúvidas a respeito da escolha do modelo a ser utilizado devem ser discutidas com a GETEC. Página 17 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 8. FUNÇÕES DEFINIDAS PELO USUÁRIO Recomendação: Desenvolver funções com códigos simples, para funcionalidades genéricas ou para substituir determinados tipos de visões ou stored procedures. O SQL Server possui um conjunto de funções, chamadas BUILT-IN Functions, amplamente utilizadas para programação em Transact-SQL. Alguns exemplos dessas funções são convert, getdate, datepart, dateadd, datediff, left, right, max, count, etc. Essas funções não podem ser modificadas. A partir da versão 2000 do SQL Server, tornou-se possível a criação de Funções Definidas pelo Usuário (User-Defined Functions) onde a lógica da função é determinada pelo desenvolvedor. Existem 3 tipo de funções definidas pelo usuário: Escalares Tabulares “Inline” Tabulares “Multistatement” Funções escalares retornam um valor pontual, enquanto as funções tabulares retornam uma tabela. Seguem alguns exemplos dessas funções: Exemplo 1 Função Escalar que retorna somente a data a partir de um campo SMALLDATETIME ou DATETIME. Recomendação: Utilizar funções escalares para substituir rotinas simples e genéricas como up_dia_util, conversão de moeda, etc. CREATE FUNCTION uf_somente_data (@data_hora datetime) RETURNS CHAR(10) AS BEGIN DECLARE @data CHAR(10) SELECT @data = LEFT(CONVERT(CHAR(10),@data_hora,101),10) RETURN(@data) END Depois de criada, a função poderia ser utilizada da seguinte forma: SELECT dbo.uf_somente_date(dtc_atualizacao) FROM bd_contribuinte..contribuinte_inscrito WHERE num_inscricao_estad = 334 Exemplo 2 Função Tabular “Inline” que retorna informações sobre os usuários de um determinado grupo passado como parâmetro. Recomendação: Utilizar funções tabulares inline para substituir visões que necessitem de passagem de parâmetros. Valem as mesmas recomendações sobre visões descritas neste documento. Segue... Página 18 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 CREATE FUNCTION uf_usarios_grupo (@cod_grupo int) RETURNS table AS RETURN ( SELECT nom_usuario, dtc_criacao FROM usuario WHERE cod_grupo = @cod_grupo ) Perceba a mudança na sintaxe da função tabular. A cláusula RETURNS deve obrigatoriamente ser preenchida pelo tipo table e os delimitadores de bloco (BEGIN e END) não são utilizados. A função acima é inline, ou seja, o retorno da função se resume ao resultado de comando SELECT especificado pela cláusula RETURN( ). Depois de criada, a função poderia ser utilizada da seguinte forma: SELECT nom_usuario, dtc_criacao FROM uf_usuarios_grupo(17652) É possível ainda a criação de funções tabulares “multistatement”, ou seja, o código da função será composto por um conjunto de comandos que preencherão uma tabela a ser retornada pela função. O uso de funções desse tipo deve ser negociado com a GETEC. Funções definidas pelo usuários trazem flexibilidade à programação em Transact-SQL, mas devem ser utilizadas com cautela, pois, assim como as visões, as funções encapsulam um código Trasact-SQL e conseqüentemente camuflam sua complexidade. Criar códigos complexos para funções pode gerar problemas sérios de performance, portanto, é essencial que as funções sejam desenvolvidas com códigos simples, de preferência quando a funcionalidade puder ser reaproveitada por diversas aplicações. Para maiores informações sobre funções definidas pelo usuário, consulte “User-Defined Functions” no Books Online. Página 19 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 9. REFERÊNCIA CRUZADA Determinados tipos de aplicações precisam, em alguns casos, gerar relatórios de referência cruzada, como no exemplo abaixo: Resultado Desejado Faturamento ano bimestre valor 1990 1990 1990 1990 1991 1991 1991 1991 1 2 3 4 1 2 3 4 1,1 1,2 1,3 1,4 2,1 2,2 2,3 2,4 ano 1990 1991 semestre 1 2,3 4,3 semestre 2 2,7 4,7 Problema: A tabela de faturamento citada, armazena o faturamento em milhões de uma determinada empresa, a cada bimestre. Foi solicitado um relatório que cruzasse o faturamento por semestre, a cada ano. Solução 1 (solução não recomendada) Para chegar ao resultado desejado é comum a utilização de cursores e atualizações em tabelas temporárias, aumentando o custo da consulta. CREATE TABLE #ref_cruzada ( ano SMALLINT semestre1 DECIMAL(5,2) semestre2 DECIMAL(5,2) ) NOT NULL, NULL, NULL INSERT INTO #ref_cruzada (ano) SELECT DISTINCT ano FROM tab_fonte UPDATE #ref_cruzada SET semestre1 = SUM(F.valor) FROM faturamento F INNER JOIN #ref_cruzada R ON F.ano = R.ano WHERE F.bimestre <= 2 Resumo das operações: 2 criação/destruição de objetos 3 atualização 1 consulta UPDATE #ref_cruzada SET semestre2 = SUM(F.valor) FROM faturamento F INNER JOIN #ref_cruzada R ON F.ano = R.ano WHERE F.bimestre >= 3 Segue... Página 20 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 SELECT ano, semestre1, semestre2 FROM #ref_cruzada DROP TABLE #ref_cruzada Comentário: a solução acima é extremamente custosa pois é necessário criar uma tabela temporária, executar uma série de atualizações para preenchê-la, ler a tabela para retornar o resultado e depois destruí-la. A quantidade de atualizações ainda poderá aumentar se o número de colunas da tabela de referência cruzada aumentar. Solução 2 (solução recomendada) SELECT ano, SUM(CASE bimestre WHEN 1 THEN Valor WHEN 2 THEN Valor ELSE 0 END) AS semestre1, SUM(CASE bimestre WHEN 3 THEN Valor WHEN 4 THEN Valor ELSE 0 END) AS semestre2 Resumo das operações: FROM vendas 1 consulta GROUP BY Ano Comentário: o segredo da solução acima é utilizar o comando CASE em conjunto com a função SUM para consolidar os resultados de cada semestre. A solução se resume a uma consulta e é muito menos custosa que a primeira. Para maiores informações sobre referência cruzada, consulte “Cross-tab Reports” no Books Online. Página 21 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 10.TABELAS DE HISTÓRICO Armazenar na mesma tabela dados freqüentemente atualizados e dados históricos é uma política bastante perigosa do ponto de vista de performance. O ideal é armazenar dados históricos em uma tabela separada, gerenciando a transferência de dados entre as tabelas envolvidas. Vale ressaltar que esta solução só é viável quando existe uma clara distinção entre o que é histórico (somente leitura) e o que é atualizado. Recomenda-se armazenar nas tabelas de histórico a data de inclusão de cada uma de suas linhas, como forma de diferenciá-las. Com o objetivo de maximizar a performance das consultas às tabelas de histórico, recomenda-se também armazenar as datas de início e fim de vigência, desde que faça sentido para essas tabelas o conceito de vigência oficial. Em certas situações, pode-se conhecer apenas a data de inclusão de uma linha em uma tabela de histórico, que não corresponderá necessariamente à data oficial de fim de vigência dos dados em questão. Existindo as datas de início e fim de vigência oficial em uma tabela de histórico, bastará que se use na cláusula WHERE das consultas uma expressão do tipo “@dtc_pesquisada between dtc_inicio_vigencia and dtc_fim_vigencia”, o que dispensará o uso de subselects para obter (deduzir, derivar) a data de início de vigência em uma linha diferente e, portanto, melhorará a performance dessas consultas. Página 22 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 11.TABELAS PARTICIONADAS (Solução de particionamento oferecida pelo SQL Server 2005) Os dados de tabelas e índices particionados são divididos em unidades que podem ser difundidas por mais de um grupo de arquivos em um banco de dados. Os dados são particionados horizontalmente, de forma que os grupos de linhas são mapeados em partições individuais. A tabela (ou índice) é tratada como uma única entidade lógica quando são executadas consultas ou atualizações nos dados. Todas as partições de um único índice ou de uma única tabela devem residir no mesmo banco de dados. Tabelas ou índices particionados suportam todas as operações suportadas por tabelas e índices convencionais. Durante a análise para decisão de particionar ou não um objeto, devemos levar em consideração o tamanho atual e o desempenho esperado das consultas. A utilização de objetos particionados diminui a complexidade de manutenção de objetos com altíssima taxa de crescimento que normalmente causam baixa performance em consultas devido ao grande volume de dados. Existem dois fatores a considerar ao planejar uma função de partição: a coluna cujos valores determinam como uma tabela é particionada, conhecida como coluna de particionamento, e o intervalo de valores da coluna de particionamento para cada partição. Esse intervalo de valores determina o número de partições que compõem sua tabela. Uma tabela poderá ter 1.000 partições, no máximo. As opções que você tem para as colunas de particionamento e para o intervalo de valores são determinados, basicamente, pela extensão na qual seus dados podem ser agrupados de forma lógica, como por exemplo, por data; e, se este agrupamento lógico é adequado para gerenciar subconjuntos de dados. Resumo: Objetivo: Aumento de Performance Facilidade de administração Composição dos 2 fatores do particionamento: Função de particionamento (Partition Function) que define as fronteiras das partições; Distribuição física das partições (Patition Scheme) que determina onde cada partição será armazenada. Página 23 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 12.NOVOS TIPOS DE DADOS DO SQLSERVER 2005 Large-Value Data Types: Varchar(max), NVarchar(max), Varbinary(max); Permitem armazenar até 2GB de dados Substituem os antigos, Text, NText e Image, garantindo maior flexibilidade XML Permite o armazenamento de um documento ou fragmento XML em variáveis ou campos de tabelas; Vantagens: o Permite a criação de índices específicos para esse tipo de dado; o Suporta consultas através da linguagem X-Query o A estrutura do conteúdo XML pode ser validada Limitação: o O conteúdo XML original não é plenamente preservado. 13.SERVICE BROKER O Serviço Broker é responsável pelo processo de envio e recebimento de mensagens assíncronas em diferentes processos no SQL Server 2005. A linguagem TSQL é utilizada para o envio de mensagens para uma fila no banco de dados do remetente ou para outro banco de dados do SQL Server, ou outro servidor/instância remoto. No processo do Service Broker são envolvidas filas de espera, os diálogos e a ativação. Consultando filas Às vezes pode ser necessário inspecionar o conteúdo de uma fila como um todo. Você pode querer saber quantas mensagens a fila contém ou assegurar que o aplicativo processou todas as mensagens de um serviço que está prestes a se tornar off-line. Você pode precisar descobrir por que as mensagens não estão sendo processadas por um aplicativo. Para obter essas informações, use o nome da fila como a tabela de origem de uma instrução SELECT. Uma instrução SELECT em uma fila tem o mesmo formato que uma instrução SELECT em uma exibição ou uma tabela. NOTA: O Agente de Serviços é projetado para permitir que vários leitores de fila recebam mensagens eficientemente de uma fila. Porém, uma instrução SELECT em uma fila pode causar bloqueio. Ao usar uma instrução SELECT em uma fila, especifique a dica NOLOCK para evitar o bloqueio de aplicativos que usam a fila A seguir, está uma instrução SELECT de exemplo para descobrir o número de mensagens na fila ExpenseQueue: SELECT COUNT(*) FROM dbo.ExpenseQueue WITH (NOLOCK) ; Página 24 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 14.CAMPOS XML O padrão XML ( eXtensible Markup Language) é um meio de transferir informações de forma semi-estruturada, usa marcadores (tags) e atributos. Ele usa os marcadores apenas para delimitar os trechos de dados, deixando sua interpretação completamente à cargo da aplicação de front-end. O XML permite maior flexibilidade, auxilia na camada de transporte e permite a interoperabilidade entre diferentes plataformas. Adicionalmente, existem padrões de transformação para convertê-lo em um formato mais adequado (XSL) e para validar sua sintaxe (XSD). No SQL Server 2005, é possível especificar alguns métodos para consultar dados em estruturas XML. Os métodos de consulta em estruturas XML são: query() - Realiza consultas em estruturas XML que retornam XML; value() - Realiza consultas em estruturas XML que retornam tipos SQL; exist() - Realiza consultas para determinar a existência de um valor dentro de uma estrutura XML; nodes() - Utiliza para segmentar uma estrutura XML em vários registros O SQL Server 2005 possui o tipo de dados XML que pode ser utilizado para simplicar consideravelmente aplicações que possuam esse tipo de características. Nas versões anteriores do SQL Server era necessário armazenar códigos XML em campos do tipo varchar. Campos XML podem ser consultados via XQuery desta forma: Retorna o XML do campo t do XML, o [p="d"] é um filtro para a propriedade p do marcador part, retornará <t>NE</t>; select CampoTipoXML.Query('//prev//part[p="d"]//t') from TabelaComDadosXML Para retornar o valor do campo, sem o marcador envolvendo o texto, podemos usar a função Data, o exemplo abaixo retorna o conteúdo do marcador, retornará NE; select CampoTipoXML.Query('Data(//prev//part[p="d"]//t)') from TabelaComDadosXML Para acessar uma propriedade do marcador como a parX, você deve utilizar o @ para indicar o nome da propriedade; select CampoTipoXML.Query('Data(//prev//part[p="d"]//@parX)') TabelaComDadosXML from Atenção, você está trabalhando com campos XML, então o retorno é sempre em XML, para poder usar como texto em querys no SQL Server 2005 com tipos varchar por exemplo, você tem que converter o dado XML, em texto, usando por exemplo a função convert. select Convert( varchar(max), CampoTipoXML.Query('Data(//prev//part[p="d"]//t)') ) from TabelaComDadosXML Página 25 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 15.TRATAMENTO DE ERROS O SQL Server oferece duas formas de realizar tratamento de erros em código Transact-SQL: Através da variável de ambiente @@ERROR Através da utilização de blocos TRY...CATCH. 15.1. Tratamento com @@ERROR A utilização da variável @@ERROR era a única opção válida até a versão 2000 do SQL Server. Ela oferece um mecanismo simples de validação que permite verificar se um comando foi executado com sucesso ou não. Para trabalhar com a @@ERROR, deve-se verificar o valor da variável após a execução de cada comando: Exemplo: INSERT INTO funcionario ( cod_funcionario, nom_funcionario, dtc_atualizacao VALUES (1, 'Joao da Silva', getdate()); ) IF @@ERROR <> 0 BEGIN RAISERROR 50001 'Erro ao cadastrar funcionario' RETURN END; Esse mecanismo está sujeito às seguintes limitações: Necessidade de testar a variável @@ERROR a cada comando executado; Necessidade de interrupção explícita do fluxo de execução a cada teste realizado; Informação limitada ao código do erro. Detalhes como a linha em que o erro ocorreu, a severidade, o estado e o nome objeto (procedure, trigger, função) que executou o comando com erro não podem ser capturados e tratados. 15.2. Tratamento com TRY...CATCH Recomendação: Realizar o tratamento de erros através de blocos TRY...CATCH. A utilização de blocos TRY...CATCH permite um tratamento de erros mais eficiente e flexível. O bloco TRY define um conjunto de comandos a serem executados e transfere a execução para o bloco CATCH, em caso de erro. No bloco CATCH é possível capturar detalhes sobre o erro ocorrido através de funções do próprio SQL Server. Exemplo: A procedure "up_teste_try_catch" insere um registro na tabela de teste "funcionario". A primeira execução dessa procedure será bem sucedida e somente o bloco TRY será executado. A partir da segunda execução, ocorrerá uma violação de chave primária e o controle será transferido para o bloco CATCH. O bloco efetua o tratamento de erros que inclui: Captura dos detalhes do erro; Tratamento de transação (rollback quando necessário); Lançamento do erro para a aplicação (RAISERROR); Página 26 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 Interrupção do fluxo de execução. -- Criacao da tabela de teste CREATE TABLE funcionario ( cod_funcionario SMALLINT NOT NULL, nom_funcionario VARCHAR(50) NOT NULL ); ALTER TABLE funcionario ADD CONSTRAINT ix_funcionario01 PRIMARY KEY (cod_funcionario); GO -- Criacao da procedure de teste -- Exemplo de uso do TRY...CATCH CREATE PROCEDURE up_teste_try_catch AS BEGIN SET NOCOUNT ON; BEGIN TRY -- Inicio do bloco TRY BEGIN TRANSACTION; INSERT INTO funcionario VALUES (1, 'Jose da Silva'); COMMIT TRAN; PRINT 'Operação realizada com sucesso!'; END TRY -- Fim do bloco TRY -- Se nao ocorrer erro, somente o bloco TRY sera executado -- Caso ocorra algum erro, o bloco CATCH sera acionado BEGIN CATCH -- Inicio do bloco CATCH DECLARE @num_erro int, -- Codigo do erro @num_severidade tinyint, -- Severidade do erro @sts_erro tinyint, -- Estado do erro @nom_proc_erro varchar(128), -- Nome da procedure @num_linha_erro smallint, -- Posicao no codigo @des_mensagem_erro varchar(4000), -- Descricao @sts_transacao tinyint; -- Estado da transacao -- Armazenando os detalhes sobre o erro -- ocorrido e o estado da transacao SELECT @num_erro = ERROR_NUMBER(), @num_severidade = ERROR_SEVERITY(), @sts_erro = ERROR_STATE(), @nom_proc_erro = ERROR_PROCEDURE(), @num_linha_erro = ERROR_LINE(), @des_mensagem_erro = ERROR_MESSAGE(), @sts_transacao = XACT_STATE(); -- Verifica o estado da transacao IF @sts_transacao <> 0 -- Em estado de transacao BEGIN Página 27 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 ROLLBACK TRANSACTION; END; RAISERROR ( 'Erro %d na linha %d, ao executar %s: %s', @num_severidade, @sts_erro, @num_erro, @num_linha_erro, @nom_proc_erro, @des_mensagem_erro); RETURN; END CATCH END O tratamento através de blocos TRY...CATCH permite um controle maior do fluxo de execução, simplificando a codificação, facilitando a manutenção e permitindo um detalhamento maior do erro ocorrido. Observação: O uso do TRY...CATCH não dispensa a utilização do RAISERROR, conforme exemplo acima, a menos que o tratamento direcione os detalhes do erro para uma tabela; O tratamento de transações é fundamental para evitar que o bloco CATCH seja concluído com a transação em aberto. Nesse contexto o uso da função XACT_STATE() é mais indicado do que a variável @@TRANCOUNT porque permite verificar se a transação entrou em um estado uncommittable; Maiores informações sobre o TRY...CATCH, seus tratamentos e variações podem ser encontradas no SQL Server 2005 Books Online. Página 28 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 16.OBSERVAÇÕES Seguem algumas observações importantes sobre Transact-SQL. HINTS O SQL disponibiliza cláusulas adicionais que podem ser utilizadas para mudar condições do contexto em que uma consulta é executada. Estas cláusulas, chamadas HINTS, são muito poderosas, mas devem ser utilizadas com muito cuidado. Seguem 4 dos hints mais conhecidos: tablockx Atribui um lock exclusivo em toda a tabela que está sendo consultada. Ninguém poderá ler ou atualizar absolutamente nada nesta tabela até o final da transação que gerou o tablockx. holdlock Mantém os locks nas tabelas envolvidas numa operação até o final da transação. O contexto da transação será definido pelo BEGIN TRAN mais externo. nolock Nenhum tipo de lock será respeitado na consulta. Os resultados retornados poderão estar inconsistentes. index Força a utilização de um determinado índice. Esta opção foi muito utilizada na versão 6.5, mas tornou-se quase desnecessária a partir da versão 7.0. Importante: Hints podem influenciar muito a performance de uma aplicação e até mesmo do servidor como um todo, por isso, o uso de hints deverá sempre ser negociado com a GETEC. Cursores Cursores, assim como tabelas temporárias, têm um custo muito grande para o banco. É aconselhável evitar o uso de cursores, procurando substituí-los por joins e/ou tabelas derivadas. Quando for utilizar um cursor, procure selecionar os mais simples como FAST FOWARD (este tipo mescla as opções FOWARD ONLY e READ ONLY) Só use cursores SCROLL se for realmente necessário Feche sempre os cursores depois da utilização com CLOSE e DEALLOCATE Lembre sempre de executar o FETCH dentro do WHILE que varre o cursor evitando LOOPs infinitos. ORDER BY A cláusula ORDER BY é bastante conhecida e utilizada, mas compromete a performance nas consultas. Esta cláusula só deve ser utilizada quando a ordenação não puder ser realizada no cliente. CONTROLE DE EXECUÇÃO DO SCRIPT Página 29 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 Ao elaborar um script com blocos de instruções dependentes, é recomendável utilizar o RETURN ou declarar numa transação. Desta forma, é possível controlar as instruções que serão executadas. Seguem abaixo, 2 exemplos: Exemplo 1: IF @@SERVERNAME <> 'PCORPORATIVO\BDP_CORPORATIVO' BEGIN PRINT 'A instância não é de produção' RETURN END ELSE BEGIN SELECT loginname as 'Logins de Produção:' FROM master..syslogins END; Exemplo 2: BEGIN TRANSACTION UPDATE usuario_dba SET usuario = 'ab' WHERE usuario = 'ad' IF @@ERROR <> 0 ROLLBACK ELSE COMMIT SET NOCOUNT ON O SET NOCOUNT ON pode proporcionar um aumento significativo de desempenho, pois o tráfego de rede é bastante reduzido. Vale lembrar que SET NOCOUNT ON somente deve ser utilizado quando o retorno da quantidade de registros afetados não for necessário. A função @@ROWCOUNT é atualizada mesmo quando SET NOCOUNT é ON. CLÁUSULA CATCH Não existem restrições para o uso do comando SELECT dentro da cláusula CATCH. A instrução TRY...CATCH captura erros de execução com severidade maior que 10. Os erros com severidade menor não serão tratados. Se houver um TRY...CATCH dentro de um cursor,deve-se obrigatoriamente desalocar e fechar o cursor antes de finalizar o CATCH. Página 30 841067067 Secretaria da Fazenda do Estado da Bahia DTI - Diretoria de Tecnologia da Informação 29/05/2017 17.CONCLUSÃO Alguns dos tópicos acima foram indicados como recomendação ou simplesmente difusão de conhecimento, outros devem ser seguidos à risca por terem conseqüências sérias para o banco de dados e para as aplicações. A programação em Transact-SQL, por envolver muitos fatores, deve ser realizada sempre de maneira criteriosa, através da discussão de soluções, análise de planos de execução e principalmente atualização de conhecimentos. Este documento estará sendo atualizado sempre que algum fator importante for identificado, portanto servirá como referência técnica e deverá ser consultado com freqüência. 18.REFERÊNCIAS SQL Server Performance.com http://www.sql-server-performance.com Microsoft SQL Server 2005 Books Online http://msdn.microsoft.com/en-us/library/ms130214(v=sql.90).aspx SQL Server Customer Advisory Team - SQL Server Best Practices http://sqlcat.com/ Microsoft Sql Server 2000 Performance Tuning Edward Whalen, Marcilina Garcia, Steve Adrien Deluca, Edward Whalen Reding, Jamie Marcilina Garcia – Microsoft Press ISBN: 0735612706 Página 31 841067067