sexta-feira, 26 de outubro de 2012

TOP dinâmico, usando SET ROWCOUNT

veremos como utilizar a função ROWCOUNT para realizar uma consulta com a quantidade de retorno de registros de forma dinâmica, definindo o quantidade de retorno de linhas por uma varável

definindo banco de dados para teste
--definindo banco de dados para teste
                use pubs;


select comum com o uso do TOP fixo
--select comum com o uso do TOP fixo
                select top 10 * from authors


caso tentarmos passar a quantidade a ser usada pelo TOP por uma variável, o sql server não aceita
declare @top int
                set @top = 3
                select top @top * from authors


ao executar a linha acima é retornado o seguinte erro:
Quote
Server: Msg 170, Level 15, State 1, Line 9
Line 9: Incorrect syntax near '@top'.


para contornar o erro acima poderiamos usar o EXEC e deixar a query em string, mas desta forma a execução perde performance (nada de anormal, mas perde)
declare @top int
                set @top = 3
                exec ('select top ' + @top + ' * from authors')



Uma forma de conseguir o mesmo resultado sem usar o EXEC seria com o comando SET ROWCOUNT, ao setar o ROWCOUNT, são afetados somente as quantidades de registros definida para os comandos SELECT, DELETE, INSERT e UPDATE 
E com o ROWCOUNT tem-se a opção de ser passado a quantidade por variável

exemplo:
declare @top int
           set @top = 3
           
           set rowcount @top
           
           --somente serão retornados 3 registros
           select * from authors



testando no update:
--adicionando uma coluna de testes na tabela authors, seu valor padrão ficará como NULL
  alter table authors add ColunaTeste char(1)
  
  --definindo quantidade de registros a serem processados
  declare @top int
  set @top = 3
           
  set rowcount @top
           
  --fazendo update, repare que esta sem WHERE, mas serão executados somente 3 registros
  update authors set ColunaTeste = 'x'



para voltar a quantidade de registros a serem processados para o normal, ou seja, todos, basta setar o ROWCOUNT para ZERO (0)
set rowcount 0
  select * from authors

verifique que na nossa coluna de testes, somente foram processados no update 3 registros

para remover a coluna de testes
alter table authors drop column ColunaTeste



AUTOR: "eriva_br"

Dúvidas, criticas, contribuições, correções e adições seram bem vindas.

Concatenando registros de várias linhas em uma só linha

estava vendo esse artigo http://classicasp.as...sql-server.html sobre envio de emails quando vi essa dica da concatenação com o comando COALESCE

segue um simples exemplo de uso:
--definindo banco de dadosuse pubs;
--variavel para receber os registros da tabela separados por;
DECLARE @Lista VARCHAR(80)
--zerando a variavel
SET @Lista = ''
--buscando dados
SELECT top 5 @Lista = @Lista + ';' + COALESCE(au_id, '') FROM authors--retirando 1º; da lista
SET @Lista = SUBSTRING(@Lista, 2, LEN(@Lista))
--verificando os dadosselect @Lista


irá retornar:
Quote
409-56-7008;648-92-1872;238-95-7766;722-51-5454;712-45-1867

(1 row(s) affected)

um bom exemplo de uso é o que mostra no artigo, o envio para diversos e-mails separando pelo ";"


t+ 

Verificando e excluindo duplicidades com e sem chave

Verificando e excluindo duplicidades com e sem chave


/*-Verificar e apagar linhas duplicadas, repetidas

OBS.: nestes casos em que é necessário eliminar uma quantidade considerável de registros de uma tabela, é muito importante a realização de um backup antes da operação.

1° caso: se a tabela tiver uma chave única e quisermos apagar uma outra coluna que está se repetindo, uma possível solução seria deletar todos os registros filtrando menos (NOT IN) o de código mínimo agrupado GROUP BY pela coluna nome (se tiver mais colunas na tabela, sem grilo, é só adicionar ae), usando a função MIN
*/
set nocount on
declare @teste table (cod int, nome varchar(30))
insert into @teste (cod, nome) values (1, 'Brasil')
insert into @teste (cod, nome) values (2, 'Romenia')
insert into @teste (cod, nome) values (3, 'Brasil')
insert into @teste (cod, nome) values (4, 'Brasil')
insert into @teste (cod, nome) values (5, 'USA')
insert into @teste (cod, nome) values (6, 'Italia')
insert into @teste (cod, nome) values (7, 'USA')
insert into @teste (cod, nome) values (8, 'Brasil')
insert into @teste (cod, nome) values (9, 'USA')
insert into @teste (cod, nome) values (10, 'Argentina')
insert into @teste (cod, nome) values (11, 'Romenia')

--esse select aqui é do que vai ser mantido, logo é só fazer um NOT IN nestes códigos na instrução DELETE.
(select min(cod) from @teste group by nome)

--deletando
delete from @teste
 where cod not in (select min(cod) from @teste group by nome)

select * from @teste


/*
2° caso: se a tabela tiver a linha inteira idêntica, neste caso usaremos uma tabela virtual para armazenar apenas uma ocorrência das linhas repetidas usando o comando distinct, apagaremos todos os repetidos na tabela e vamos reinserir somente uma ocorrência destes
*/
set nocount on
declare @teste table (cod int, nome varchar(30))
insert into @teste (cod, nome) values (1, 'Brasil')
insert into @teste (cod, nome) values (2, 'Romenia')
insert into @teste (cod, nome) values (1, 'Brasil')
insert into @teste (cod, nome) values (1, 'Brasil')
insert into @teste (cod, nome) values (3, 'USA')
insert into @teste (cod, nome) values (4, 'Italia')
insert into @teste (cod, nome) values (3, 'USA')
insert into @teste (cod, nome) values (1, 'Brasil')
insert into @teste (cod, nome) values (3, 'USA')
insert into @teste (cod, nome) values (5, 'Argentina')
insert into @teste (cod, nome) values (2, 'Romenia')

--verificando o numero de repetições
select cod, nome, count(*) as nro_repeticoes
from @teste
group by cod, nome
having count(*)>1

--armazenando somente uma ocorrência dos repetidos (distinct) na virtual #tempx
select distinct t.cod, t.nome into #tempx from @teste t
inner join (select cod, count(*) as nro_repeticoes
  from @teste
  group by cod
  having count(*)>1)
taux on t.cod = taux.cod

--apagando todos os repetidos
delete from @teste
where cod in (select distinct t.cod from @teste t
  inner join (select cod, count(*) as nro_repeticoes
    from @teste
    group by cod
    having count(*)>1) taux
  on t.cod = taux.cod
)
--reinserindo somente uma ocorrência dos repetidos na tabela principal a partir da tabela temporária #tempx
insert into @teste (cod, nome)
select cod, nome from #tempx 
--destruindo a tabela virtual
drop table #tempx 

--consultado a tabela depois das modificações
select * from @teste


Fonte/Autor/link
AUTOR: "Eriva_br"

domingo, 26 de agosto de 2012

Operador UNION e UNION ALL - união de queries

Operador UNION
união de query's, juntar, unir, atrelar, conectar, ligar, acoplar, vincular... várias queries em um único resultado, juntando o select num moido só...


Descrição:
Este operador possibilita a combinação de uma ou mais queries em um único resultado consistindo todas as linhas pertencentes a todas as querys neste união. Veja que isto é diferente de usar JOIN'sque combinam colunas de diferentes tabelas

Regras:
Duas regras básicas para o uso do UNION
- O número e a ordem das colunas deve ser identico em todas as queries
- O tipo de dados deve ser compativel, caso náo for, uma dica seria converter tudo pra varchar usando a função convert

Opções:
Temos a opção de definir a união total com UNION ALL ou somente a união simples com UNION, vejamos abaixo as diferenças:
- UNION: faz a união e já elimina linhas idênticas
- UNION ALL: faz a união e mantém as linhas idênticas
No exemplo abaixo veremos melhor esta a diferença.

Exemplo:
Vamos fazer a união das tabelas clientes e fornecedores, somente com o campo 'Nome', note que o registro "Vito Corleone" (ele mesmo Don Vito, o poderoso Chefão), aparece na tabela de clientes e fornecedores, logo em UNION ele vai aparecer uma única vez e em UNION ALL ele vai aparecer duas vezes.

tabelas para testes
--configurando para não aparecer o contador de registros executados 
set nocount on
--declarando tabelas temporarias para teste
declare @tbClientes table (cod int identity, nome varchar(30))
declare @tbFornecedores table (cod int identity, nome varchar(30))
                  
--inserindo dados na tabela clientes
insert into @tbClientes (nome) values ('Vito Corleone')
insert into @tbClientes (nome) values ('Bonasera')
insert into @tbClientes (nome) values ('Luca Brasi')
                  
--inserindo dados na tabela fornecedores
insert into @tbFornecedores (nome) values ('Paul Vitti')
insert into @tbFornecedores (nome) values ('Tonny Gordo')
insert into @tbFornecedores (nome) values ('Vito Corleone')

.
.
.
.

UNION
--executando o UNION 
select nome from @tbClientes
union
select nome from @tbFornecedores
order by nome
Resultado, observe que Vito Corleone aparece apenas uma única vez:
nome
------------------------------
Bonasera
Luca Brasi
Paul Vitti
Tonny Gordo
Vito Corleone

.
.
.
.
UNION ALL

--executando o UNION ALL
select nome from @tbClientes
union all
select nome from @tbFornecedores
order by nome

Resultado, observe que Vito Corleone aparece duas vezes:
nome
------------------------------
Bonasera
Luca Brasi
Paul Vitti
Tonny Gordo
Vito Corleone
Vito Corleone


OBS:
- Para a ordenação o comando ORDER BY tem que ficar após o último SELECT
- No exemplo citado para as construções UNION foram usadas duas query's, mas poderiamos ter usado N query's


AUTOR: "eriva_br"
Dúvidas, criticas, contribuições, correções e adições seram bem vindas. 

Código para encontrar / descobrir último dia do mês

Código para achar / localizar o último dia do mês em SQL Server

DECLARE @mes CHAR(2), @ano CHAR(4)
        SET @mes = '02'
        SET @ano = '2008'
        SELECT DAY(DATEADD(d,-1,DATEADD(M,1,CONVERT(DATETIME,@ano + @mes + '01'))))
        
        
        SET @mes = '02'
        SET @ano = '2007'
        SELECT DAY(DATEADD(d,-1,DATEADD(M,1,CONVERT(DATETIME,@ano + @mes + '01'))))


funções utilizadas:
DAY
DATEADD
CONVERT


AUTOR: "eriva_br"
Dúvidas, criticas, contribuições, correções e adições seram bem vindas. 

xp_cmdshell - comandos no SQL Server via shell

Caracteristicas:
Nome: XP_CMDSHELL
Localização: Banco de dados Master
Tipo: Extended Procedure
Descrição: Esta procedure executa uma sequencia de comandos em string do tipo shell no computador onde o sql server está instalado.
Utilidades: listar diretórios; criar, copiar, apagar arquivos; enviar mensagens, etc

Parâmetros:
'command_string', obrigatório, é o comando a ser executado
no_output, opcional, se colocado indica que não será retornado mensagem de saída na execução

Retornos:
retona 0 ou 1: 0-sucesso ou 1-Falha

Observações:
Obs1.: esta proc trabalha de forma sincrona, os comandos colocados abaixo dela só serão executados após o término de sua execução, ao contrário do comando shell do vb6 que trabalha de forma assincrona, ao ser executado, mesmo sem ter finalizado o comando já é liberado para a linha subsequente.
Obs2.: verificar segurança, tomar cuidados com SQL Injection, dica do nosso amigo Micox
http://www.macoratti.net/sql_inj.htm
http://www.google.com.br/search?hl=pt-BR&a...ta=lr%3Dlang_pt

Exemplos de uso:
exemplo 1: copiando um arquivo
declare @retorno INT
  EXEC @retorno = MASTER..xp_cmdshell 'copy C:\Lixo\teste.txt C:\Lixo\testeBKP.txt'
  select CASE WHEN @retorno = 0 THEN 'COPIADO COM SUCESSO' ELSE 'FALHA NO COMANDO' END as Retorno


exemplo 2: listando os arquivos de uma pasta
declare @retorno int
  EXEC @retorno = master..xp_cmdshell 'dir C:\lixo\'
  if @retorno = 0
          print 'comando executado com EXITO'
  else
          print 'FALHA no comando'

para testar esse exemplo verificando uma execução com sucesso e uma com falha, teste passando o caminho correto do arquivo e depois teste definindo o caminho incorretamente


exemplo 3: deletando um arquivo
EXEC MASTER..XP_CMDSHELL 'del C:\Lixo\teste.txt'


exemplo 4: enviando mensagens
EXEC master..xp_cmdshell 'net send /domain:SQL_USERS ''SQL Server será desligado em em 1 minuto.'


exemplo 5: escrevendo em um arquivo texto
DECLARE @arqTexto VARCHAR(8000),
          @caminho VARCHAR(255),
                  @retEcho INT --variavel para verificar se o comando foi executado com exito ou ocorreu alguma falha
  
          SET @caminho = 'C:\Lixo\teste.txt'
  
          SET @arqTexto = 'ECHO Registro;CAMPO1;CAMPO2;CAMPO3 ' + CONVERT(VARCHAR(30),GETDATE()) +  ' >> ' + @caminho
  
          --remove caracteres não aceitos pelo comando ECHO, veja no final do post comentário
          select @arqTexto = dbo.FU_CONVERTE_TXT(@arqTexto)
  
          --escreve no arquivo txt, se ele naum existir será criado, se existir será adicionado (incrementado) a partir da última linha
          EXEC @retEcho = MASTER..XP_CMDSHELL @arqTexto, NO_OUTPUT --NO_OUTPUT indica que o comando não irá retornar alguma possível mensagem na sua execução
          IF (@retEcho <> 0)
          BEGIN
                  --caso ocorrer algum erro podemos retornar uma mensagem
                  SELECT 'ocorreu erro, verifique se o diretório existe' as Falha, @retEcho as retEcho
          END



exemplo 6: escrevendo arquivo texto, vamos escrever um arquivo texto com alguns campos da tabela authors do banco de dados pubs, através de um loop
--definindo o banco pubs
  use pubs;
  --definindo variaveis
  declare @arqTexto varchar(8000),
          @au_fname varchar(50),
          @au_lname varchar(50),
          @caminho varchar(255),
          @au_id varchar(20),
          @retEcho INT --variavel para verificar se o comando foi executado com exito ou ocorreu alguma falha
  
  --definindo o caminho e arquivo que será escrito, OBS: este caminho deve ser no servidor onde estiver instalado o SQL Server
  set @caminho = 'C:\Lixo\teste.txt'
  --setando o menor id para verificação do loop
  select @au_id = min(au_id) from authors
  --while da tabela authors
  while @au_id is not null
  begin
          --buscando os valores dos campos e adicionando nas variaveis
          select @au_id = au_id, @au_lname = au_lname, @au_fname = au_fname from authors
          where au_id = @au_id
          --setando o menor id para verificação do loop
          select @au_id = min(au_id) from authors
          where au_id > @au_id
          --definindo a linha que será escrita no arquivo txt
          SET @arqTexto = 'ECHO Registro;' + @au_id + ';' + @au_lname + ';' + @au_fname +  ' >> ' + @caminho
          --escreve no arquivo txt, se ele naum existir será criado, se existir será adicionado (incrementado) a partir da última linha
          exec @retEcho = MASTER..XP_CMDSHELL @arqTexto
          IF (@retEcho <> 0)
          BEGIN
                  --caso ocorrer algum erro podemos retornar uma mensagem
                  select 'ocorreu erro, verifique se o diretório existe' as Falha, @retEcho as retEcho
                  --parar o loop em caso de erro
                  break
          END
  end





Atenção:
Para escrever arquivo texto, cuidado com os caracteres especiais, pois se for um destes caracteres no comando a linha inteira não é escrita, dai imagina um arquivo texto de exportação faltando uma linha lá no meio do arquivo... como diria o Silvio Luiz, "o que que eu vou dizer lá no 'escritório'!"
Os caracteres que verifiquei até agora que não são aceitos pelo comando ECHO estão na função abaixo que fiz para trocar tais caracteres, se ver mais algum por favor nos avise:
CREATE FUNCTION FU_TRANSFORMA_CARACTERES (@Texto VARCHAR(8000))
  RETURNS VARCHAR(8000)
  AS
  BEGIN
  SET @Texto = UPPER(REPLACE(@Texto,'Á','A'))
  SET @Texto = UPPER(REPLACE(@Texto,'Á','A'))
  SET @Texto = UPPER(REPLACE(@Texto,'Ã','A'))
  SET @Texto = UPPER(REPLACE(@Texto,'Â','A'))
  SET @Texto = UPPER(REPLACE(@Texto,'Ó','O'))
  SET @Texto = UPPER(REPLACE(@Texto,'Ô','O'))
  SET @Texto = UPPER(REPLACE(@Texto,'Õ','O'))
  SET @Texto = UPPER(REPLACE(@Texto,'Ú','U'))
  SET @Texto = UPPER(REPLACE(@Texto,'Ü','U'))
  SET @Texto = UPPER(REPLACE(@Texto,'Í','I'))
  SET @Texto = UPPER(REPLACE(@Texto,'É','E'))
  SET @Texto = UPPER(REPLACE(@Texto,'Ê','E'))
  SET @Texto = UPPER(REPLACE(@Texto,'Ç','C'))
  SET @Texto = REPLACE(@Texto,'&','e')
  SET @Texto = REPLACE(@Texto,'"',' ')
  
  RETURN @Texto
  END



AUTOR: "eriva_br"

Dúvidas, criticas, contribuições, correções e adições seram bem vindas.