web-dev-qa-db-fra.com

Comment insérer en vrac un fichier dans une table * temporaire * où le nom de fichier est une variable?

J'ai du code comme celui-ci que j'utilise pour faire un INSERT EN VRAC d'un fichier de données dans une table, où le fichier de données et le nom de la table sont des variables:

DECLARE @sql AS NVARCHAR(1000)
SET @sql = 'BULK INSERT ' + @tableName + ' FROM ''' + @filename + ''' WITH (CODEPAGE=''ACP'', FIELDTERMINATOR=''|'')'

EXEC (@sql)

Cela fonctionne bien pour les tables standard, mais maintenant je dois faire la même chose pour charger les données dans une table temporaire (par exemple, #MyTable). Mais quand j'essaye ceci, j'obtiens l'erreur:

Invalid Object Name: #MyTable

Je pense que le problème est dû au fait que l'instruction BULK INSERT Est construite à la volée puis exécutée à l'aide de EXEC, et que #MyTable N'est pas accessible dans le contexte de la EXEC appel.

La raison pour laquelle j'ai besoin de construire l'instruction BULK INSERT Comme ceci est que j'ai besoin d'insérer le nom de fichier dans l'instruction, et cela semble être le seul moyen de le faire. Donc, il semble que je puisse soit avoir un nom de fichier variable, ou utilisez une table temporaire, mais pas les deux.

Existe-t-il un autre moyen d'y parvenir - peut-être en utilisant OPENROWSET(BULK...)?


MISE À JOUR: OK, donc ce que j'entends, c'est que BULK INSERT et les tables temporaires ne fonctionneront pas pour moi. Merci pour les suggestions, mais déplacer plus de mon code dans la partie SQL dynamique n'est pas pratique dans mon cas.

Après avoir essayé OPENROWSET(BULK...), il semble que cela souffre du même problème, c'est-à-dire qu'il ne peut pas traiter un nom de fichier variable, et j'aurais besoin de construire l'instruction SQL dynamiquement comme avant (et donc de ne pas pouvoir accéder la table temporaire).

Donc, cela ne me laisse qu'une seule option qui consiste à utiliser une table non temporaire et à réaliser l'isolation des processus d'une manière différente (en veillant à ce qu'un seul processus puisse utiliser les tables à la fois - je peux penser à plusieurs façons de faire cela).

C'est ennuyant. Il aurait été beaucoup plus pratique de le faire comme je l'avais prévu à l'origine. Juste une de ces choses qui devrait être banale, mais qui finit par manger toute une journée de votre temps ...

30
Gary McGill

Il est possible de faire tout ce que vous voulez. La réponse d'Aaron n'était pas tout à fait complète.

Son approche est correcte, jusqu'à la création de la table temporaire dans la requête interne. Ensuite, vous devez insérer les résultats dans un tableau dans la requête externe.

L'extrait de code suivant capture la première ligne d'un fichier et l'insère dans la table @Lines:

declare @fieldsep char(1) = ',';
declare @recordsep char(1) = char(10);

declare @Lines table (
    line varchar(8000)
);

declare @sql varchar(8000) = ' 
    create table #tmp (
        line varchar(8000)
    );

    bulk insert #tmp
        from '''+@filename+'''
        with (FirstRow = 1, FieldTerminator = '''+@fieldsep+''', RowTerminator = '''+@recordsep+''');

    select * from #tmp';

insert into @Lines
    exec(@sql);

select * from @lines
12
Gordon Linoff

Vous pouvez toujours construire la table #temp en SQL dynamique. Par exemple, en ce moment, je suppose que vous essayez:

CREATE TABLE #tmp(a INT, b INT, c INT);

DECLARE @sql NVARCHAR(1000);

SET @sql = N'BULK INSERT #tmp ...' + @variables;

EXEC master.sys.sp_executesql @sql;

SELECT * FROM #tmp;

Cela le rend plus difficile à maintenir (lisibilité) mais résout le problème de portée:

DECLARE @sql NVARCHAR(MAX);

SET @sql = N'CREATE TABLE #tmp(a INT, b INT, c INT);

BULK INSERT #tmp ...' + @variables + ';

SELECT * FROM #tmp;';

EXEC master.sys.sp_executesql @sql;

EDIT 2011-01-12

À la lumière de la façon dont ma réponse vieille de presque 2 ans a été soudainement jugée incomplète et inacceptable, par quelqu'un dont la réponse était également incomplète, que diriez-vous:

CREATE TABLE #outer(a INT, b INT, c INT);

DECLARE @sql NVARCHAR(MAX);

SET @sql = N'SET NOCOUNT ON; 

CREATE TABLE #inner(a INT, b INT, c INT);

BULK INSERT #inner ...' + @variables + ';

SELECT * FROM #inner;';

INSERT #outer EXEC master.sys.sp_executesql @sql;
18
Aaron Bertrand

Désolé de déterrer une vieille question, mais au cas où quelqu'un tomberait sur ce fil et voudrait une solution plus rapide.

Insertion en masse d'un fichier de largeur inconnue avec des terminateurs de ligne\n dans une table temporaire créée en dehors de l'instruction EXEC.

DECLARE     @SQL VARCHAR(8000)

IF OBJECT_ID('TempDB..#BulkInsert') IS NOT NULL
BEGIN
    DROP TABLE #BulkInsert
END

CREATE TABLE #BulkInsert
(
    Line    VARCHAR(MAX)
)

SET @SQL = 'BULK INSERT #BulkInser FROM ''##FILEPATH##'' WITH (ROWTERMINATOR = ''\n'')'
EXEC (@SQL)

SELECT * FROM #BulkInsert

Prise en charge supplémentaire que SQL dynamique dans une instruction EXEC a accès aux tables temporaires en dehors de l'instruction EXEC. http://sqlfiddle.com/#!3/d41d8/1934

DECLARE     @SQL VARCHAR(8000)

IF OBJECT_ID('TempDB..#BulkInsert') IS NOT NULL
BEGIN
    DROP TABLE #BulkInsert
END

CREATE TABLE #BulkInsert
(
    Line    VARCHAR(MAX)
)
INSERT INTO #BulkInsert
(
    Line
)
SELECT 1
UNION SELECT 2
UNION SELECT 3

SET @SQL = 'SELECT * FROM #BulkInsert'
EXEC (@SQL)

Prise en charge supplémentaire, écrite pour MSSQL2000 http://technet.Microsoft.com/en-us/library/aa175921 (v = sql.80) .aspx

Exemple en bas du lien

DECLARE @cmd VARCHAR(1000), @ExecError INT
CREATE TABLE #ErrFile (ExecError INT)
SET @cmd = 'EXEC GetTableCount ' + 
'''pubs.dbo.authors''' + 
'INSERT #ErrFile VALUES(@@ERROR)'
EXEC(@cmd)
SET @ExecError = (SELECT * FROM #ErrFile)
SELECT @ExecError AS '@@ERROR'
1
deeg

http://msdn.Microsoft.com/en-us/library/ms191503.aspx

je conseillerais de créer une table avec un nom unique avant l'insertion en vrac.

0
Andrey