Comment insérer en bloc un fichier dans une table * temporaire * où le nom de fichier est une variable?
J'ai un code comme celui-ci que j'utilise pour insérer en bloc 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)
Le fonctionne bien pour les tables standard, mais maintenant je dois faire le même genre de chose pour charger des données dans une tabletemporaire (par exemple, #MyTable
). Mais quand j'essaie 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 en utilisant EXEC
, et que #MyTable
n'est pas accessible dans le contexte de la {[4],} appeler.
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 la seule façon de le faire. Donc, il semble que je puisse soit avoir un nom de fichier variable, ou utiliser une table temporaire, mais pas les deux.
Y a - t-il un autre moyen d'y parvenir-peut-être en utilisant OPENROWSET(BULK...)
?
Mise à jour: OK, alors ce que j'entends, c'est que les tables D'insertion et temporaires en vrac ne vont pas fonctionner 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 gérer un nom de fichier variable, et j'aurais besoin de construire l'instruction SQL dynamiquement comme avant (et donc ne pas pouvoir accéder à la table temporaire).
Donc, cela ne me laisse qu'une seule option qui consiste à utiliser une table non-temp et à réaliser l'isolement du 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 le faire).
C'est agaçant. Il aurait été beaucoup plus pratique de le faire comme je l'avais initialement prévu. Juste une de ces choses qui devraient être triviales, mais finit par manger une journée entière de votre temps...
4 réponses
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'à créer la table temporaire dans la requête interne. Ensuite, vous devez insérer les résultats dans une table dans la requête externe.
L'extrait de code suivant attrape la première ligne d'un fichier et l'insère dans le tableau @Lignes:
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
Vous pouvez toujours construire la table # temp dans dynamic SQL. Par exemple, je suppose que vous avez essayé:
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 rend plus difficile à maintenir (lisibilité) mais obtient par 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;
Modifier 2011-01-12
À la lumière de la façon dont ma réponse 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;
Http://msdn.microsoft.com/en-us/library/ms191503.aspx
Je conseillerais de créer une table avec un nom unique avant l'insertion en bloc.
Désolé de déterrer une vieille question mais au cas où quelqu'un trébuche sur ce fil et veut une solution plus rapide.
Insertion En bloc d'un fichier de largeur inconnue avec \ n terminateurs de ligne 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
Support 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/19343
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)
Support supplémentaire, écrit 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'