31

我有一些这样的代码,用于将数据文件批量插入到表中,其中数据文件和表名是变量:

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

EXEC (@sql)

这适用于标准表,但现在我需要做同样的事情来将数据加载到临时表中(例如,#MyTable)。但是当我尝试这个时,我得到了错误:

Invalid Object Name: #MyTable

我认为问题是由于该BULK INSERT语句是动态构建然后使用执行的EXEC,并且在调用#MyTable的上下文中无法访问。EXEC

我需要构造这样的BULK INSERT语句的原因是我需要将文件名插入到语句中,这似乎是唯一的方法。所以,似乎我可以一个变量文件名,或者使用一个临时表,但不能同时使用两者。

有没有另一种方法来实现这一点 - 也许通过使用OPENROWSET(BULK...)


更新: 好的,所以我听到的是 BULK INSERT 和临时表对我不起作用。感谢您的建议,但在我的情况下,将我的更多代码移动到动态 SQL 部分是不切实际的。

尝试过OPENROWSET(BULK...),似乎遇到了同样的问题,即它无法处理变量文件名,我需要像以前一样动态地构造 SQL 语句(因此无法访问临时表)。

所以,这让我只有一个选择,即使用非临时表并以不同的方式实现进程隔离(通过确保任何时候只有一个进程可以使用这些表 - 我可以想到几种方法来去做)。

它很烦人。按照我原本打算的方式来做会方便得多。只是其中一件应该是微不足道的事情,但最终会浪费你一整天的时间......

4

4 回答 4

21

您总是可以在动态 SQL 中构造 #temp 表。例如,现在我猜你一直在尝试:

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;

这使得维护(可读性)变得更加困难,但解决了范围问题:

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;

编辑 2011-01-12

鉴于我将近 2 岁的答案突然被一个答案也不完整的人认为是不完整和不可接受的,那么:

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;
于 2010-03-04T19:07:31.017 回答
15

可以做任何你想做的事情。亚伦的回答并不完整。

他的方法是正确的,直到在内部查询中创建临时表。然后,您需要将结果插入到外部查询中的表中。

以下代码片段抓取文件的第一行并将其插入到表 @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
于 2012-01-11T19:53:57.590 回答
2

很抱歉挖掘一个老问题,但万一有人偶然发现这个线程并想要一个更快的解决方案。

将带有 \n 行终止符的未知宽度文件批量插入在 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

进一步支持 EXEC 语句中的动态 SQL 可以访问 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)

进一步支持,为 MSSQL2000 编写http://technet.microsoft.com/en-us/library/aa175921(v=sql.80).aspx

链接底部的示例

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'
于 2013-08-22T17:31:25.917 回答
1

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

我建议在批量插入之前创建具有唯一名称的表。

于 2010-03-04T18:18:21.473 回答