Thiago Zavaschi R2 www.zavaschi.com

2Jun/090

Repostagem – Utilizando Campos Identity no SQL Server

Olá a todos!
Primeiramente gostaria de dizer que este é um post para o nível iniciante, e serve como base para aquelas perguntas “como é que eu fazia aquilo mesmo?”. É baseado em um mini artigo que escrevi, e este post também já publiquei no DevBR.

Em muitas ocasiões nos deparamos com situações em que precisamos trabalhar em alguma coluna que seja identity. Este mini-artigo descreve alguns comandos úteis na manipulação destas colunas.

Criando uma tabela com campo identity: Devemos criar com o parâmetro identity, e configurar o primeiro valor, e o valor de incremento, respectivamente (IDENTITY (<inicio>,<incremento>)). Conforme no exemplo a seguir:

CREATE TABLE Tabela (
      Id INT IDENTITY (1,1) PRIMARY KEY,
      Valor VARCHAR(50)
)

Reiniciando o valor do campo identity: Algumas vezes precisamos alterar o valor do identity (para reiniciar a contagem, por exemplo). Há o comando truncate que além de limpar a tabela (entre outras coisas), também zera o valor do identity. No entanto, se o nosso objetivo é apenas trocar o valor do identity, o truncate não é uma saída interessante. Para manipular o identity podemos fazer como descrito a seguir:

Vamos imaginar que queremos que o próximo registro a ser inserido na tabela Tabela queremos que sua chave primária Id, se inicie em 1.

Para resolver esse problema, podemos usar a seguinte instrução:

DBCC CHECKIDENT (Tabela, reseed, 0)

O último parâmetro se torna o valor atual do identity. Ao inserir o próximo registro, o mesmo virá com o seu Id com o valor identity somado de 1 (caso seja um identity de incremento 1), o valor do identity da tabela vai para 1, e assim por diante.

Recuperar o último valor inserido de um campo identity: Para recuperar o último valor de identity inserido (ex: inserir algo em uma tabela, e cadastrar um novo registro com o id do registro recém inserido), podemos utilizar:

@@IDENTITY, que retorna o valor do identity do último registro inserido na tabela, ou;

SCOPE_IDENTITY(), que retorna o último valor mas dentro do escopo em que a query foi executada.

Não recomendo a utilização de @@IDENTITY, pois isso pode acarretar problemas de consistência em cenários onde existe um alto grau de inserção/manipulação dos dados no banco (ou mais especificamente, na tabela em questão).

Um exemplo simples de uso pode ser:

DECLARE @techId INT
INSERT INTO Tabela (Valor) VALUES (‘Testando Identity’)
SELECT @techId = SCOPE_IDENTITY()

---

Um complemento em relação ao post original:

”Zavaschi, e se eu quiser inserir um registro em uma tabela, mas somente nessa inserção quero especificar o valor do campo identity. É possível?”

É sim, basta fazer da seguinte forma:

SET IDENTITY_INSERT Tabela ON
INSERT INTO Tabela (Id, Valor) VALUES (123, ‘Informação’)
SET IDENTITY_INSERT Tabela OFF

 

Simples, mas temos que lembrar que precisamos explicitar os campos, ainda que estejamos fazendo uma inserção em todas as colunas.

É isso, espero que tenham gostado!

Abraços!

Thiago Zavaschi

1Jun/090

Repostagem – Database Mail – Configuração Através de Scripts

DatabaseMail – Configuração através de scripts

Postei um tempo atrás um artigo sobre o DatabaseMail (aqui), mas era através dos menus de configuração. Hoje trago para vocês a realização da mesma tarefa, só que através de scripts.

Configurando o DatabaseMail

-- Cria uma account do Database Mail
EXECUTE msdb.dbo.sysmail_add_account_sp
    @account_name = 'SQL',
    @description = 'Conta de email para o envio do
    email na rotina de log de erro.', -- Uma descrição para a sua account
    @email_address = 'sql@zavaschi.com', -- Email que será 
    -- mostrado como o emissor
    @replyto_address = 'sql@zavaschi.com', -- Email de reply
    @display_name = 'SQL Server', -- Nome mostrado no email enviado
    @mailserver_name = 'smtp.zavaschi.com', -- Servidor smtp
    @port = 25, -- Porta
    @username = 'sql', -- Login do servidor de smtp
    @password = 'sql'; -- senha do servidor de smtp 

-- Cria um profile do Database Mail
EXECUTE msdb.dbo.sysmail_add_profile_sp
    @profile_name = 'SQL Profile',
    @description = 'Profile usado para o
    envio do email na rotina de log de erro.' ;
    -- Cria o profile 

-- Adiciona a account ao profile
EXECUTE msdb.dbo.sysmail_add_profileaccount_sp
    @profile_name = 'SQL Profile',
    @account_name = 'SQL',
    @sequence_number =1 ; -- Associa a account ao profile, recém criados. 

-- Garante acesso ao profile a todos os usuarios da base msdb
EXECUTE msdb.dbo.sysmail_add_principalprofile_sp
    @profile_name = 'SQL Profile',
    @principal_name = 'public',
    @is_default = 1; -- Configura o profile criado como público e padrão. 

 

Pronto, só isso. E agora para enviar email é da mesma maneira:

EXEC msdb.dbo.sp_send_dbmail @profile_name='SQL Profile',
@recipients='sql@zavaschi.com',
@subject='Titulo',
@body='Corpo da mensagem.'

 

Tranquilo né? Mas se ao executar a procedure de envio de email você obter o seguinte erro:

Msg 15281, Level 16, State 1, Procedure sp_send_dbmail, Line 0

SQL Server blocked access to procedure 'dbo.sp_send_dbmail' of component 'Database Mail XPs' because this component is turned off as part of the security configuration for this server. A system administrator can enable the use of 'Database Mail XPs' by using sp_configure. For more information about enabling 'Database Mail XPs', see "Surface Area Configuration" in SQL Server Books Online.

É pelo fato do DatabaseMail estar desabilitado no seu SQL Server, então rode o seguinte script:

sp_configure 'Database Mail XPs', 1
GO
RECONFIGURE 

Há alguns casos que não é possível executar o RECONFIGURE diretamente (sem entrar nos méritos)

Executar então: RECONFIGURE WITH OVERRIDE

Espero que seja útil para você, acredito que sim, pois muitos me pedem!

Grande abraço!

Thiago Zavaschi

1Jun/092

Repostagem – Como Proteger as Minhas Stored Procedures ?

Como proteger as minhas stored procedures?

Bom pessoal, o assunto do post de hoje não é uma novidade, mas hoje a tarde estava conversando com um amigo (Eduardo Ordine) e chegamos ao assunto de proteção às stored procedures no banco de dados SQL Server, vamos lá!

“Thiago, estou colocando minhas stored procedures em um banco de dados compartilhado, ou que a administração não cabe a mim, e nas mesmas eu tenho regra de negócio importante e confidencial da minha empresa, tem como protegê-las?”

A resposta é: Sim!

As aplicações costumam ser divididas em camadas para agilizar seu entendimento e, principalmente, facilitar as manutenções. Acontece que eu, como consultor/estudante/desenvolvedor, já MUITAS empresas que colocam a regra de negócio em stored procedures do banco de dados, utilizando as linguagens de programação (.NET (C# e VB.NET), Delphi, VB, etc.) apenas como casca gráfica. Méritos e desvantagens à parte, temos o que: a necessidade de proteger estas procedures no banco de dados. Entendemos que “proteger” é igual a evitar que descubram o conteúdo das procedures.

Proteger 100% e garantir que NUNCA NINGUÉM conseguirá ver o conteúdo da procedure é utopia, mas podemos dificultar BASTANTE o acesso à mesma através da encriptação da procedure.

A stored procedure continua podendo ser executada normalmente (segundo as permições dos usuários da base de dados), mas ninguém ve o seu conteudo.

Como fazer?

Se a sua procedure ia ser criada com um comando similar a este:

CREATE PROCEDURE dbo.spImportante
BEGIN
    SELECT 'Thiago Zavaschi'
END

Então utilize da seguinte forma:

CREATE PROCEDURE dbo.spImportante
WITH ENCRYPTION
AS
BEGIN
    SELECT 'Thiago Zavaschi'
END

“Pronto Thiago, encriptei a minha procedure, estou seguro e não preciso me preocupar com mais nada, certo?”

Errado! E vejamos o porquê.

Vou criar aqui a procedure do exemplo acima e tentarei fazer alguns testes.

Primeiro farei um exec.

---------------

Thiago Zavaschi

(1 row(s) affected)

Beleza, executado com sucesso! Agora vou executar a sp_helptext:

The text for object 'spImportante' is encrypted.

Não consigo ver, até mesmo o “Modify” do Management Studio fica desabilitado, impedindo a geração do script (e o mesmo vale para todas as formas de geração de scripts do SSMS).

“Bom Thiago, para mim parece bem seguro.”

Será mesmo?

Vamos fazer mais um teste: Vou ligar o SQL Server Profiler monitorando o banco e vou executar novamente a procedure (vimos que a execução é permitida sem problemas), e temos:

Ok, texto encriptado, mas e se rodarmos o profiler DURANTE a criação da procedure:

Opa, opa, opa, temos a visualização da procedure. Tem como proteger-se disso? Tem sim, basta adicionar um comentário (-- comment: sp_password) ao seu script.

Infelizmente há como burlar essa segurança (não vou mostrar como se faz, pois não acho ético), eu fiz aqui alguns métodos mas não consegui para algumas procedures, pois eram SPs CLR.

Consegui abrir essas stored procedures por outros métodos (que também não vou comentar quais), mas fiz isso só para mostrar que com segurança não devemos brincar!

Então fica aí a dica!

E não se esqueçam de manter o código original da stored prcedure em um lugar a salvo (de preferência sob o controle de um controlador de código como o TFS, etc.).

Grande abraço e até a próxima!

Thiago Zavaschi

1Jun/091

Repostagem – Função Split no SQL Server

 

Então pessoal, atenção que é uma repostagem! O post sobre Common Table Expressions (CTE) e recursividade já está postado! O de CLR ainda não hehe, mas um dia cumpro todas as promessas!

Função Split no SQL Server

Uma função que sempre senti falta no SQL Server é uma função para split, aonde passaríamos dois parâmetros:

    • Frase a ser “splitada”.
    • Delimitador.

E teríamos uma lista de palavras originadas da frase passada, separada em cada delimitador.

Já desenvolvi várias versões para fazer split, mas a que considero a melhor (e também bastante popular) é a seguinte:

CREATE FUNCTION dbo.fnSplit(
    @frase VARCHAR(max)
  , @delimitador VARCHAR(max) = ','
) RETURNS @result TABLE (item VARCHAR(8000)) 

BEGIN
DECLARE @parte VARCHAR(8000)
WHILE CHARINDEX(@delimitador,@frase,0) <> 0
BEGIN
SELECT
  @parte=RTRIM(LTRIM(
          SUBSTRING(@frase,1,
        CHARINDEX(@delimitador,@frase,0)-1))),
  @frase=RTRIM(LTRIM(SUBSTRING(@frase,
          CHARINDEX(@delimitador,@frase,0)
        + LEN(@delimitador), LEN(@frase))))
IF LEN(@parte) > 0
  INSERT INTO @result SELECT @parte
END 

IF LEN(@frase) > 0
INSERT INTO @result SELECT @frase
RETURN
END
GO

Para testar é simples:

SELECT * FROM dbo.fnSplit('separar por espaço em branco', ' ')

----

Se desejar, há outra forma, utilizando tabela temporária, sem função:

SET NOCOUNT ON
DECLARE @ARRAY VARCHAR(8000), @DELIMITADOR VARCHAR(100), @S VARCHAR(8000)  

SELECT @ARRAY = 'separar por espaço em branco'
SELECT @DELIMITADOR = ' '  

IF LEN(@ARRAY) > 0 SET @ARRAY = @ARRAY + @DELIMITADOR
CREATE TABLE #ARRAY(ITEM_ARRAY VARCHAR(8000))  

WHILE LEN(@ARRAY) > 0
BEGIN
    SELECT @S = LTRIM(SUBSTRING(@ARRAY, 1,
    CHARINDEX(@DELIMITADOR, @ARRAY) - 1))
    INSERT INTO #ARRAY (ITEM_ARRAY) VALUES (@S)
    SELECT @ARRAY = SUBSTRING(@ARRAY,
    CHARINDEX(@DELIMITADOR, @ARRAY) + 1, LEN(@ARRAY))
END  

-- MOSTRANDO O RESULTADO JÁ POPULADO NA TABELA TEMPORÁRIA  
SELECT * FROM #ARRAY
DROP TABLE #ARRAY
SET NOCOUNT OFF

 

Simples não?

Estas maneiras mostradas resolvem a necessidade da falta do split? Sim!

Porém, desde o SQL Server 2005 temos o recurso das CTEs (Common Table Expression), que também podem ser utilizadas para split. A seguir mostro o exemplo que considero mais elegante para tal tarefa, utiliza recursividade ao invés do loop:

DECLARE @s VARCHAR(8000), @d VARCHAR(10)
SET @s = 'separar por espaço em branco'
SET @d = ' ' 

;WITH split(i,j) AS
(
SELECT i = 1, j = CHARINDEX(@d, @s + @d)
UNION ALL
SELECT i = j + 1, j = CHARINDEX(@d, @s + @d, j + 1) FROM split
   WHERE CHARINDEX(@d, @s + @d, j + 1) <> 0
)
SELECT SUBSTRING(@s,i,j-i)
FROM split

Observação importante sobre CTEs:

Ao utilizar CTEs recursivas devemos tomar cuidado com o número máximo de recursões permitidas.

Por padrão esse número é igual a 100. Isso na prática significa que eu só poderia ter 100 delimitadores dentro da minha variável @s.

Caso a recursão máxima seja alcançada, termos uma mensagem igual a essa:

“The statement terminated. The maximum recursion 100 has been exhausted before statement completion.”

Para aumentar este valor, podemos acionar um parâmetro extra para a CTE: maxrecursion.

Na prática, para trocar a recursão máxima para 1000, devemos fazer:

DECLARE @s VARCHAR(8000), @d VARCHAR(10)
SET @s = 'separar por espaço em branco'
SET @d = ' ' 

;WITH split(i,j) AS
(
SELECT i = 1, j = CHARINDEX(@d, @s + @d)
UNION ALL
SELECT i = j + 1, j = CHARINDEX(@d, @s + @d, j + 1) FROM split
   WHERE CHARINDEX(@d, @s + @d, j + 1) <> 0
)
SELECT SUBSTRING(@s,i,j-i)
FROM split
OPTION (maxrecursion 1000)

 

O valor máximo para o parâmetro MAXRECURSION é de 32767, então fiquem atentos. Caso seja necessário uma CTE recursiva com mais de 32767 iterações, então devemos pensar numa estratégia para executar mais de um split (ou mais de uma vez a operação desejada) por iteração. Em casos muito específicos, podemos deixar o máximo de recursão infinito (MAXRECURSION = 0).

Não entendeu muito bem o que é uma CTE? Pode recorrer ao BOL, ou esperar o meu próximo post (há este e o de cursores para postar) que abordarei BEM detalhadamente o que é uma CTE, sintaxe, como funciona, seus usos comuns, e a questão de como funciona a recursividade. Então fiquem atentos! 

---

Depois destas três maneiras mostradas, ainda há uma quarta possibilidade (a partir do SQL Server 2005 também) que é utilizar as chamadas Functions CLR (programadas em C# por exemplo). O por quê? Porque operações em strings são custosas para o SQL Server, então uma função CLR poderia se comportar de maneira muito interessante (performática) neste caso.

Também não sabe o que é CLR ou como funciona? Então aguarde os próximos posts também!

Mas eu ainda continuo na esperança de algum dia termos isso diretamente no SQL Server, de uma maneira mais otimizada. :)

Abraços,

Thiago Zavaschi

Tagged as: , , 1 Comment
1Jun/091

Repostagem – Surface Area Configuration (SAC) no SQL Server 2008

Segue mais uma repostagem do blog antigo!

Surface Area Configuration no SQL Server 2008 ?

O SQL Server Surface Area Configuration (SAC), presente no SQL server 2005, responsável por gerenciar serviços (conexões remotas, protocolos, etc.) e features não está mais presente no SQL Server 2008.

Aí cabe a pergunta:

“Ok Thiago, eu sei que muitas coisas que eu fazia com o SAC eu poderia fazer no Configuration Manager, mas e se eu quiser habilitar as features, tarefa que eu fazia no SAC?”

No SQL Server 2008 é bastante simples, para isso usamos as Facetas (Facets).
A maneira mais de alterá-las é através do SQL Server Management Studio.

Segue o passo a passo:

1. Clique com o botão direito no nome da instância (na janela do object Explorer) e selecione “Facets”.

2. Na janela “View Facets”, em Facet, selecione “Surface Area Configuration”.

3. Pronto, agora basta habilitar as features desejadas, como por exemplo a integração com o CLR.

Lembrando que também é possível alterar essas configurações através das system stored procedures, como a sp_configure!

E só para complementar o post, segue as principais diferenças entre o SQL Server Configuration Manager e o Surface Area Configuration para o SQL Server 2005:

Recurso

Surface Area Configuration

Configuration Manager

Iniciar e parar serviços SIM SIM
Ativar e desativar recursos SIM NÃO
Configura clientes da rede NÃO SIM
Configurar clientes nativos NÃO SIM
Gerenciar conexões remotas SIM SIM
Cria apelidos na rede (os famosos Alias) NÃO SIM
Configurar apelidos locais NÃO SIM
Mudar endereços TCP/IP NÃO SIM

 

É isso pessoal, abraços!
Thiago Zavaschi

1Jun/091

Repostagem – Tutorial: Configurando o Database Mail

Então pessoal, sei que prometi o artigo de PowerPivot, mas estou finalizando-o ainda. Enquanto isso farei a repostagem de alguns posts do antigo blog que não vieram no processo de importação (não serão muitos).

Tutorial: Configurando o DatabaseMail

O Database Mail é a evolução do SQL Mail (método marcado como obsoleto e fadado a ser removido do SQL Server em uma próxima versão) com melhorias. Se você por alguma razão utiliza o SQLMail com o SQL Server 2005 ou 2008, considere a mudança imediata.

O objetivo deste artigo é mostrar o wizard de configuração do DatabaseMail e demonstrar como enviar emails através do T-SQL, além de como verificar o status dos emails enviados.

A base deste tutorial é o novo SQL Server 2008, porém os passos são muito similares no SQL Server 2005.

Configurando o Database Mail

Abra a instância desejada, abra a janela Object Explorer, abra a pasta Management e clique duas vezes no “Database Mail” para abrir a seguinte janela:

Iniciaomente somos deparados com a tela de boas vindas do wizard de configuração. Não há muito oq ue fazer aqui, podemos marcar para não ser exibida novamente e clicar em Next para prosseguir.

Marque a opção “Set up Database Mail by performing the following tasks:” e clique em next.

Caso o Database Mail não esteja habilitado você será indagado a habilitá-lo. Clique em Yes para ativar.

Dê um nome e uma descrição a conta. Após isto clique em “Add”.

Configure os dados pedidos: Nome, descrição, endereço de email, servidor smtp, e assim por diante. Clique em Ok quando terminar.

Clique em next para prosseguir.

Aqui estou tornando essa configuração pública (marcando a checkbox “public”) e tornando o perfil padrão.

Agora é a hora de configurar alguns parâmetros de sistema, como por exemplo, a extensão de arquivos que não podem ser enviados ao utilizar o database mail.
Após configurar como desejar, clique em next.

Clique em finish para encerrar o wizard e terminar de configurar a sua conta.

Esta tela mostra o progresso da configuração. Repare na coluna status, pois ela fornece a informação de se a tarefa foi bem sucedida ou não. Clique em close para encerrar.

Enviando emails

Para enviar emails através do DatabaseMail é bastante simples.

É necessário primeiramente executar a seguinte configuração para garantir que não haverá problemas:

sp_configure 'show advanced', 1
GO
RECONFIGURE
GO
sp_configure 'Database Mail XPs', 1
GO
RECONFIGURE
GO

Na sequência podemos utilizar a stored procedure de sistema denominada: sp_send_dbmail, cuja sintaxe é mostrada a seguir (para mais detalhes consulte o books online):

sp_send_dbmail [ [ @profile_name = ] 'profile_name' ]
    [ , [ @recipients = ] 'recipients [ ; ...n ]' ]
    [ , [ @copy_recipients = ] 'copy_recipient [ ; ...n ]' ]
    [ , [ @blind_copy_recipients = ] 'blind_copy_recipient [ ; ...n ]' ]
    [ , [ @subject = ] 'subject' ]
    [ , [ @body = ] 'body' ]
    [ , [ @body_format = ] 'body_format' ]
    [ , [ @importance = ] 'importance' ]
    [ , [ @sensitivity = ] 'sensitivity' ]
    [ , [ @file_attachments = ] 'attachment [ ; ...n ]' ]
    [ , [ @query = ] 'query' ]
    [ , [ @execute_query_database = ] 'execute_query_database' ]
    [ , [ @attach_query_result_as_file = ] attach_query_result_as_file ]
    [ , [ @query_attachment_filename = ] query_attachment_filename ]
    [ , [ @query_result_header = ] query_result_header ]
    [ , [ @query_result_width = ] query_result_width ]
    [ , [ @query_result_separator = ] 'query_result_separator' ]
    [ , [ @exclude_query_output = ] exclude_query_output ]
    [ , [ @append_query_error = ] append_query_error ]
    [ , [ @query_no_truncate = ] query_no_truncate ]
    [ , [ @mailitem_id = ] mailitem_id ] [ OUTPUT ]
Segue um exemplo prático de envio de email:

USE msdb

GO

EXEC sp_send_dbmail @profile_name='Zavaschi', -– Coloque o profile desejado.

@recipients='thiagoh@techresult.com.br', – Coloque os receptores da mensagem.

@subject='Título da Mensagem',

@body='Corpo da mensagem.

E-mail recebido através do database mail do SQL Server!!!!'

Note que o envio não é disparado necessariamente na hora, ele é posto numa fila e o envio dependerá das condições da rede e de conectividade.

Podemos analisar as mensagens e o status do envio das mesmas, através dos seguintes comandos de seleção:

SELECT * FROM sysmail_mailitems

GO

SELECT * FROM sysmail_log

GO

É isso, abraços pessoal!

Thiago Zavaschi