13/05/2015

Concatenar conteúdo de um registro SQL Server 2005

Boa tarde,

Estou com uma necessidade de retornar o conteúdo de um registro de uma determinada tabela. Exemplo:

Tenho uma tabela de PRODUTOS com os seguintes campos:
CODIGO NOME MARCA VALOR ------------ -------------------------------- -------------- --------- 2514 MOUSE 3 BOTOES USB LOGITECH 45,00


Gostaria de saber se existe algum comando SQL ou Função ou StoreProcedure para que ao passar o nome da tabela como parâmetro a mesma me retorne o conteúdo do registro devidamente concatenado. Seguindo o exemplo do registro acima ficaria assim:
2514MOUSE 3 BOTÕES USBLOGITECH45,00

Atualmente estou utilizando uma função em Delphi7 para essa finalidade mas devido a alguns clientes terem muitos registros esta um pouco lento.

if not QAux.FieldByName(CampoChave).IsNull then begin //Loop nos Fields for i := 0 to QAux.FieldCount -1 do begin if QAux.FieldDefs[i].Name <> NomeCampo then //elimina o campo _HASH para que o mesmo não faça parte do calculo MD5 StringAux := StringAux + QAux.Fields.Fields[i].AsString; end; end;


Minha necessidade desta rotina é para atender um requisito do PAF-ECF para detectar se houve algum tipo de manipulação no banco de dados que não passou pelo meu sistema.

Carlos Eduardo

Melhor resposta

13/05/2015

Um detalhe, pode usar o convert em campos que já sao do tipo varchar que não da erro.


Vou testar a consulta sql e assim que possível postarei o resultado.

Grato pela contribuição.

Carlos Eduardo

Responder Citar

Outras Respostas

13/05/2015

Marcos P

Se todos os campos forem char, você pode concatenar direto :

select CODIGO+NOME+MARCA+VALOR 


Se existirem campos numéricos, converta-os para char ( via convert, rtrim, str,... como no exemplo abaixo ) e depois execute a concatenação :

declare @Numerico1 Int
declare @Numerico2 Float

set @Numerico1 = 83
set @Numerico2 = 15.76

select @Numerico1, @Numerico2, rtrim(convert(varchar(20),@Numerico1)), rtrim(str(@Numerico2,10,2))
Responder Citar

13/05/2015

Carlos Eduardo

Se todos os campos forem char, você pode concatenar direto :
select CODIGO+NOME+MARCA+VALOR 
Se existirem campos numéricos, converta-os para char ( via convert, rtrim, str,... como no exemplo abaixo ) e depois execute a concatenação :
declare @Numerico1 Int
declare @Numerico2 Float

set @Numerico1 = 83
set @Numerico2 = 15.76

select @Numerico1, @Numerico2, rtrim(convert(varchar(20),@Numerico1)), rtrim(str(@Numerico2,10,2))


Marcos,

Como citado no post preciso de uma função ou algo parecido onde passo o nome da tabela como parâmetro sem a necessidade de informar os campos pois são varias tabelas.

Já consegui pegar o nome dos campos. rs

 SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'PRODUTOS' 


Grato pela atenção!
Responder Citar

13/05/2015

Ceilton M

Carlos, defina melhor sua necessidade:
"detectar se houve algum tipo de manipulação no banco de dados que não passou pelo meu sistema. "

Você quer saber se a estrutura da tabela foi alterada?

Se for isso você tem que estudar triggers DDL....
Responder Citar

13/05/2015

Ceilton M

Exemplo:
Esse trigger impede que tabelas sejam alteradas ou apagadas de um banco.

CREATE TRIGGER safety
ON DATABASE
FOR DROP_TABLE, ALTER_TABLE
AS
PRINT 'You must disable Trigger "safety" to drop or alter tables!'
ROLLBACK ;
Responder Citar

13/05/2015

Marcos P

Carlos,

Se sua ideia é fazer isso dinamicamente, crie uma stored procedure no banco que, passando o nome da tabela, leia sua estrutura e gere a concatenação conforme os campos disponíveis.

Você fará isso através de uma query dinâmica, tratando das devidas conversões para char / varchar.

Vai dar um "trabalhinho", mas eu testaria isso antes de maneira fixa, para verificar se a performance do lado da aplicação melhora...
Responder Citar

13/05/2015

Carlos Eduardo

Carlos, defina melhor sua necessidade: "detectar se houve algum tipo de manipulação no banco de dados que não passou pelo meu sistema. " Você quer saber se a estrutura da tabela foi alterada? Se for isso você tem que estudar triggers DDL....


Minha necessidade não consiste em saber se a estrutura do banco foi alterada e sim um registro. Exemplo:

Tenho um sistema de Frente de Loja onde é feito uma emissão de cupom fiscal. Ao gravar o registro pertinente a venda eu também calculo o HASH do registro e armazeno num campo da tabela para que depois eu possa verificar se o conteúdo do registro esta igual ao que foi gravado.

Desta forma se alguém acessar uma ferramente de manutenção e modificar os dados deste registro eu tenho como saber se o mesmo foi manipulado.

Esta é uma exigência para quem vai homologar o sistema na legislação PAF-ECF.

Existe um arquivo eletrônico que é gerado todos os dias após a Redução Z e caso algum registro de produto, cliente, venda, estoque, etc... for manipulado eu tenho que gerar interrogações no lugar do campo. "????????"
Responder Citar

13/05/2015

Carlos Eduardo

Exemplo: Esse trigger impede que tabelas sejam alteradas ou apagadas de um banco. CREATE TRIGGER safety ON DATABASE FOR DROP_TABLE, ALTER_TABLE AS PRINT 'You must disable Trigger "safety" to drop or alter tables!' ROLLBACK ;


Vou guardar essa trigger achei bem interessante.

Obrigado!
Responder Citar

13/05/2015

Carlos Eduardo

Carlos, Se sua ideia é fazer isso dinamicamente, crie uma stored procedure no banco que, passando o nome da tabela, leia sua estrutura e gere a concatenação conforme os campos disponíveis. Você fará isso através de uma query dinâmica, tratando das devidas conversões para char / varchar. Vai dar um "trabalhinho", mas eu testaria isso antes de maneira fixa, para verificar se a performance do lado da aplicação melhora...


Marcos, vou tentar criar essa StoreProcedure e caso eu consiga postarei aqui o resultado e principalmente a performance. Pois hoje já faço isso via Delphi a qual está muito lento pois um de meus clientes tem mais de 50 mil produtos. Atualmente minha estrutura de Produtos tem 60 campos. Fazendo uma conta rápida temos:

60 X 50.000 = 3.000.000 (essa é a quantidade de vezes que meu programa executa).

O cliente me disse que todo final de dia após a Redução Z todo o processo de geração do arquivo eletrônico demora aproximadamente 15 minutos.
Responder Citar

13/05/2015

Marcos P

Qual a versão do Sql Server ?

Tente desenvolver a procedure, caso você tenha dificuldades... retorne a esse post que posso lhe ajudar via Skype.
Responder Citar

13/05/2015

Carlos Eduardo

Qual a versão do Sql Server ? Tente desenvolver a procedure, caso você tenha dificuldades... retorne a esse post que posso lhe ajudar via Skype.


SQL Server 2005 Express

Beleza Marcos assim que eu conseguir fazer algo lhe aviso sim.

Grato pela atenção!
Responder Citar

13/05/2015

Ceilton M

Segue um exemplo
Só deu erro em colunas do tipo timestamp.


declare @nometabela varchar(200) = 'TABELA';
declare @objectid int --= 124956663;
declare @name varchar(200), @system_type_id int;
declare @SQL varchar(2000) = ' select '
DECLARE @UID INT, @schemaname varchar(20)

select @objectid = id from sysobjects where type = 'U' and name = @nometabela

declare cursorcolumns cursor for SELECT name,system_type_id FROM sys.columns where object_id = @objectid
open cursorcolumns;

fetch cursorcolumns into @name, @system_type_id;

while @@FETCH_STATUS = 0
begin
if len (@SQL) > 8 set @SQL = @SQL + ' + ';
set @SQL = @SQL + ' convert(varchar(200), isnull(' + @name + ','''') ) ';
/*
if @system_type_id not in (35, 167, 175,231,239) -- Se nao for do tipo character
begin

set @SQL = @SQL + ' convert(varchar(50), ' + @name + ' ) '

end;
*/
fetch cursorcolumns into @name, @system_type_id
end;
close cursorcolumns;
deallocate cursorcolumns;

select @UID = UID from sysobjects where name IN ( 'GN_USUARIO' )
SELECT @schemaname = name from sys.SCHEMAS WHERE SCHEMA_ID = @UID

set @SQL = @SQL + ' from ' + @schemaname + '.' + @nometabela

select @SQL;

exec @SQL;
Responder Citar

13/05/2015

Ceilton M

Um detalhe, pode usar o convert em campos que já sao do tipo varchar que não da erro.
Responder Citar

14/05/2015

Carlos Eduardo

Boa tarde!

Caro Ceilton,

Fiz testes no exemplo que você disponibilizou mas não obtive sucesso pois ao executar a consulta informando minha tabela (@nometabela = 'PRODUTOS') a consulta esta retornando vazia.

Hoje consegui fazer uma store procedure e estou quase lá, só está faltando o procedimento me retornar o conteúdo do select que estou compondo no loop. Não consegui fazer com que este procedimento me retornasse o resultado do select mas sim a instrução sql. Exemplo: "select campo1, campo2 from PRODUTOS"

Estou postando meu procedimento para que vocês possam me ajudar:

CREATE PROCEDURE [dbo].[sp_Retorna_Conteudo_Registro]( @nome_tabela varchar(50), @campo_chave varchar(50), @codigo varchar(10), @resultado varchar(8000) OUTPUT) AS set nocount on declare cursorcolumns cursor for select column_name, data_type from information_schema.columns where table_name = @nome_tabela and (column_name not like '%HASH') declare @column_name varchar(100), @data_type varchar(100), @SQL varchar(5000) set @SQL = 'select ' open cursorcolumns fetch next from cursorcolumns into @column_name, @data_type while (@@FETCH_STATUS = 0) begin if @data_type in ('char', 'nchar', 'nvarchar', 'varchar') set @SQL = @SQL + 'coalesce(' + @column_name + ','''') + ' else set @SQL = @SQL + 'coalesce(convert(varchar(8000), ' + @column_name + '),'''') + ' fetch next from cursorcolumns into @column_name, @data_type end close cursorcolumns deallocate cursorcolumns if right(@SQL,2) = '+ ' --remove o caracter "+" resultado da concatenação no loop dos campos set @SQL = left(@SQL,(len(@SQL)-1)) ----------------- É neste ponto que não consigo retornar o resultado deste select ----------------- select @Resultado = (@SQL + ' from ' + @nome_tabela + ' where ' + @campo_chave + ' = ' + @codigo) set nocount off


Estou realizando os testes no Delphi:
procedure TForm1.btnExecutarClick(Sender: TObject); Var sp_Retorna_Conteudo_Registro: TSQLStoredProc; begin //Store Procedure try try sp_Retorna_Conteudo_Registro := TSQLStoredProc.Create(nil); sp_Retorna_Conteudo_Registro.SQLConnection := AutoConnection; sp_Retorna_Conteudo_Registro.SchemaName := 'dbo'; sp_Retorna_Conteudo_Registro.StoredProcName := 'sp_Retorna_Conteudo_Registro'; sp_Retorna_Conteudo_Registro.Params.FindParam('@nome_tabela').AsString := lbledtTabela.Text; sp_Retorna_Conteudo_Registro.Params.FindParam('@campo_chave').AsString := lbledtChave.Text; sp_Retorna_Conteudo_Registro.Params.FindParam('@codigo').AsString := lbledtCodigo.Text; sp_Retorna_Conteudo_Registro.ExecProc; lbledtResultSP.Text := sp_Retorna_Conteudo_Registro.Params.FindParam('@resultado').AsString; finally FreeAndNil(sp_Retorna_Conteudo_Registro); end; except on E: Exception do Application.MessageBox(PChar(E.Message), 'Erro', MB_ICONERROR); end end;
Responder Citar

14/05/2015

Marcos P

select @Resultado = (@SQL + ' from ' + @nome_tabela + ' where ' + @campo_chave + ' = ' + @codigo)

Não rola !

Faça...

set @SQL = @SQL + ' from ' + @nome_tabela + ' where ' + @campo_chave + ' = ' + @codigo
exec(@SQL)

Se quiser ver a query antes de rodar, troque o "exec" por "print".

Uma pergunta... um item cuja descrição é "SUPORTE PARA MONITOR", deve retornar "SUPORTE PARA MONITOR" ou "SUPORTEPARAMONITOR" ?
Responder Citar

14/05/2015

Carlos Eduardo

Marcos, agora a situação é a seguinte.

O procedimento que você pediu para alterar funcionou somente quando executo o procedimento no "SQL Management Studio":

exec sp_Retorna_Conteudo_Registro 'PRODUTOS', 'PRD_CODIGO', 1, ''


Resultado:
1CONJUNTO DIVS.Sep 18 2005 12:00AM CJ0.00000.00000.00001020.000017.000000.00009949.000049.00000.000025.00000.00000.00000.000067.379921100.0000.00000.0000R$Sep 29 2014 12:00AMTT0


Mas no Delphi executa sem da erro mas nao traz nada no retorno. Alguma sugestão? No post anterior eu inseri como estou utilizando a Store Procedure no Delphi.

Respondendo sua pergunta a descrição deve retornar com os devidos espaços: "SUPORTE PARA MONITOR"

Esse ta dando trabalho rs.

Grato pela atenção!
Responder Citar