Inquiridor
Seleção dinâmica de TABELA destino, conforme a linha de arquivo TXT!

Discussão Geral
-
Em primeiro lugar vou agradecer as diversas ajudas recebidas em alguns Tópicos que criei. Na verdade, todos estão relacionados à um código maior que estou elaborando graças a vocês, valeu!
Relembrando, Eí-los:
https://social.msdn.microsoft.com/Forums/office/pt-BR/5a90cf8c-db90-441d-aeab-3a130e4de494/converso-de-cdigo-vba-para-sql
https://social.msdn.microsoft.com/Forums/office/pt-BR/e8d9caf8-93c6-4a23-b3d8-659eca595a6d/problemas-com-insert-into
https://social.msdn.microsoft.com/Forums/office/pt-BR/c41fd1cd-635b-413e-af65-03f92908c104/bulk-insert-de-arquivo-txt-para-tabela-com-uso-de-identity
Agora segue o código depois de esclarecer as dúvidas 'iniciais', pois chegou a grande dúvida.
Tentei inserir diversos comentários para deixar o mais claro possível quanto a execução do código.
O objetivo é: primeiro, ler um arquivo EFD contribuição para a tabela TEMPOR (o arquivo TXT em questão) e posteriormente ler linha-a-linha a tabela TEMPOR para inserí-lo na tabela correspondente ao TIPO de registro, ou seja 0150 para table de Participantes, 0200 para tabela de Produtos e assim por diante!
-- Seleção do BD
USE TESTE
-- se existe apaga/recria tabela TEMPOR e faz a leitura do arquivo
IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'TEMPOR')
DROP TABLE TEMPOR
CREATE TABLE TEMPOR(
ID INTEGER IDENTITY (1,1),
LINHA VARCHAR(999))
-- Leitura do arquivo para tabela TEMPOR
INSERT INTO TEMPOR EXEC xp_cmdshell 'findstr /b "|" C:\@IMPORTA\TESTE\teste.txt'
-- Declaração de variáveis
DECLARE @MINID INTEGER
DECLARE @MAXID INTEGER
DECLARE @LINHA VARCHAR(999)
-- Primeiro (@MINID) e último (@MAXID) registro de TEMPOR
SELECT @MINID = MIN(ID), @MAXID = MAX(ID) from TEMPOR
-- Leitura de TEMPOR linha-a-linha
WHILE @MINID <= @MAXID
BEGIN
SELECT @LINHA = LINHA FROM TEMPOR WHERE ID = @MINID
IF SUBSTRING(@LINHA, 2,4) = '0150' BEGIN
-- comandos
????? SELECT INTO TAB_0150 FROM dbo.SplitString(@LINHA, '|')
END
ELSE IF SUBSTRING(@LINHA, 2,4) = '0200' BEGIN
-- comandos
????? SELECT INTO TAB_0200 FROM dbo.SplitString(@LINHA, '|')
END
SET @MINID = @MINID + 1
END
Pois bem, a grande dúvida é a seguinte: cada registro lido (@LINHA) possui como delimitador o pipe (|) e supondo que já tenho cada uma das tabelas com TODOS os campos necessários (0150, 0200) já preparados quero carregá-lo na tabela correspondente!
Como? Li sobre as funções SPLIT que "quebram a frase" conforme necessidade... mas não encontrei um exemplo SIMPLES! Estilo SELECT INTO TABLE FROM dbo.SplitString(@LINHA, '|')
Desde já agradeço
Todas as Respostas
-
MLRamos,
Sugiro avaliar a utilização do comando BULK INSERT que já possui parâmetros para delimitadores de campo e linha, inserir o conteúdo do arquivo em uma tabela temporária e depois realizar o tratamento necessário para inserir nas suas tabelas de destino.
Segue abaixo link sobre o comando BULK INSERT para leitura.
https://msdn.microsoft.com/pt-br/library/ms188365%28v=sql.120%29.aspx?f=255&MSPPError=-2147217396
Felipe Lauffer MCSA: SQL Server | MCP
- Editado FLauffer sexta-feira, 22 de janeiro de 2016 15:05
-
-
Estou quase... vejam esses dois exemplos de função SPLIT:
-- http://zavaschi.com/index.php/2009/06/repostagem-funo-split-no-sql-server/
USE TESTE
GO
drop function dbo.FnSplit
GO
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=============================================
-- https://ole.michelsen.dk/blog/split-string-to-table-using-transact-sql.html
USE TESTE
GO
drop function dbo.FnSplit
GO
create FUNCTION dbo.FnSplit(
@String NVARCHAR(4000),
@Delimiter NCHAR(1)
)
RETURNS TABLE
AS
RETURN
(
WITH Split(stpos,endpos)
AS(
SELECT 0 AS stpos, CHARINDEX(@Delimiter,@String) AS endpos
UNION ALL
SELECT endpos+1, CHARINDEX(@Delimiter,@String,endpos+1)
FROM Split
WHERE endpos > 0
)
SELECT -- 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)),
'Data' = SUBSTRING(@String,stpos,COALESCE(NULLIF(endpos,0),LEN(@String)+1)-stpos)
FROM Split
)
GO
A grande diferença entre elas é que no primeiro exemplo quando houverem dois delimitadores consecutivos (coluna em branco) a função concatena tudo, eliminando aquela coluna e isso não interessa...
No segundo caso fica OK, ou seja, ele traz a coluna EM BRANCO. Entretanto preciso levar em consideração que estou passando valor para uma tabela ... deveria estar NULL ? Não sei...
=(
- Editado MLRamos sexta-feira, 22 de janeiro de 2016 18:41 identação do codigo
-
Ramos,
usa o MERGE, já que você já colocou o TXT em uma tabela, abaixo um exemplo básico e você pode enquadrar na sua realidade
--USANDO O MERGE T-SQL -- Cara se voce quiser sincronizar uma tabela com outra com base nas diferenças em relação aos dados -- de uma tabela, voce deve usar o Merge. CREATE TABLE #TAB1 ( ID INT, NOME VARCHAR(10)); CREATE TABLE #TAB2 ( ID INT, NOME VARCHAR(10)); INSERT INTO #TAB1 VALUES(100, 'MONSTRAO'); INSERT INTO #TAB1 VALUES(101, 'MLK DOIDO') INSERT INTO #TAB1 VALUES(102, 'SHOUGLAS') INSERT INTO #TAB2 VALUES(103, 'DOUGLAS') INSERT INTO #TAB2 VALUES(104, 'MOUGLAS') SELECT * FROM #TAB1 SELECT * FROM #TAB2 MERGE #TAB1 AS TAB1 -- DESTINO DAS OPERAÇÕES USING #TAB2 AS TAB2 -- FONTE DE DADOS DO DESTINO. ON(TAB1.ID = TAB2.ID) -- CLAUSULA PARA JUNTAR #TAB1 E #TAB2 WHEN NOT MATCHED BY TARGET AND TAB2.NOME LIKE '%S' THEN INSERT(ID,NOME) VALUES(TAB2.ID, TAB2.NOME) WHEN MATCHED THEN UPDATE SET TAB1.NOME = TAB2.NOME WHEN NOT MATCHED BY SOURCE AND TAB1.NOME LIKE '%S' THEN DELETE OUTPUT $ACTION, INSERTED.*, DELETED.*; -- RETORNA UMA LINHA PARA CADA LINHA DO DESTINO QUE É INSERIDA, ATUALIZADA OU EXCLUIDA.
-
Ok, vamos a um exemplo pratico. Vejam as estrutura das duas tabelas citadas (0150-Participante e 0200-Produtos).
Como podem observar as tabelas são muito diferentes entre si: quantidade de colunas (campos), etc, etc. Igualmente, há outras MUITAS tabelas e, conforme dito anteriormente, o TXT lido na primeira parte do código menciona tal código (DA TABELA DESTINO), exemplos:
...
|0150|0000000001|NOME DA EMPRESA LTDA|1058|12345678000134|||0000001||RUA DA EMPRESA|99||BAIRRO|
|0150|0000000002|FULANO DA COSTA|1058||12345678901||0000001||RUA DO FULANO DA COSTA|999||BAIRRO|
...
|0200|00000001|NOME DO PRODUTO 1|||UN|00|12345678||||18|
|0200|00000002|NOME DO PRODUTO 2|||UN|00|12345678||||18|
....
Agora, preciso pegar a @LINHA (da tabela TEMPOR, já lida) e jogar para a tabela correspondente, ficou claro !?
=)
- Editado MLRamos terça-feira, 26 de janeiro de 2016 12:15 Correção de tabelas
-
-
-
MLRamos,
Posso te dar uma sugestão?
Ao invés de tentar fazer o processo usando loops e "N" funções para quebrar a string, porque você não faz uma importação do arquivo usando o OPENROWSET? Assim, você consegue fazer a filtragem das linhas da forma como precisa.
Outra opção:
- Importar o arquivo todo (Como já faz)
- Exportar os registros em arquivos separados (0150 em um arquivo, 0200 em outro)
- Importar diretamente na tabela de destino, usando o BULK INSERT.
Apesar de obrigar a exportação em novo arquivos, todo o processo fica mais fácil e direto.
[]'s!
/* Logan Destefani Merazzi - DBA | @LoganMerazzi | http://www.merazzi.eti.br
Se a resposta for útil, vote nela. Se resolveu, marque-a como resposta. */ -
Logan, desconheço tal função. Vou estudá-la , mas se puderes exemplificar agradeço.
Noutro Tópico que criei melhorei o código utilizando CURSOR - em desempenho pelo menos melhorou muito - e ficou conforme segue:
USE TESTE
SET NOCOUNT ON
IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'TEMPOR')
DROP TABLE TEMPOR
CREATE TABLE TEMPOR(
ID INTEGER IDENTITY (1,1) PRIMARY KEY,
LINHA VARCHAR(999))
INSERT INTO TEMPOR EXEC xp_cmdshell 'findstr /b "|" C:\@IMPORTA\TESTE\teste.txt'
DECLARE @LINHA VARCHAR(999)
DECLARE TAB_CURSOR CURSOR LOCAL FAST_FORWARD FOR SELECT LINHA FROM TEMPOR ORDER BY ID
OPEN TAB_CURSOR
FETCH NEXT FROM TAB_CURSOR INTO @LINHA -- primeiro registro da tabela
WHILE(@@FETCH_STATUS = 0)
BEGIN
IF SUBSTRING(@LINHA, 2, 4) = '0150' BEGIN
-- comandos
????? SELECT INTO TAB_0150 FROM dbo.SplitString(@LINHA, '|')
END
IF SUBSTRING(@LINHA, 2, 4) = '0200' BEGIN
-- comandos
????? SELECT INTO TAB_0200 FROM dbo.SplitString(@LINHA, '|')
END
FETCH NEXT FROM TAB_CURSOR INTO @LINHA
END
CLOSE TAB_CURSOR
DEALLOCATE TAB_CURSOR
SET NOCOUNT OFF -
MLRamos,
Posso te dar uma sugestão?
Ao invés de tentar fazer o processo usando loops e "N" funções para quebrar a string, porque você não faz uma importação do arquivo usando o OPENROWSET? Assim, você consegue fazer a filtragem das linhas da forma como precisa.
Outra opção:
- Importar o arquivo todo (Como já faz)
- Exportar os registros em arquivos separados (0150 em um arquivo, 0200 em outro)
- Importar diretamente na tabela de destino, usando o BULK INSERT.
Apesar de obrigar a exportação em novo arquivos, todo o processo fica mais fácil e direto.
[]'s!
/* Logan Destefani Merazzi - DBA | @LoganMerazzi | http://www.merazzi.eti.br
Se a resposta for útil, vote nela. Se resolveu, marque-a como resposta. */Logan,
Pelo que entendi OPENROWSET também serve para carga em massa e filtro...
No entanto eu teria que executá-lo cerca de 100 vezes! Uma para cada filtro.
Nesse formato eu fiz apenas 1 leitura de carga (para tabela TEMPOR) e trabalho com ela no loop para selecionar a tabela destino correta, entendeu ?
Ou seja, meu problema está no segundo ponto descrito por você...