如何在批量插入中正确使用动态 SQL?

How to correctly use dynamic SQL with bulk insert?

我有一个像这样的存储过程:

CREATE PROCEDURE [dbo].[sp_writeBulk] 
    @usersID            NVARCHAR(30)
   ,@fileName           NVARCHAR(50)
   ,@nameMeasuringPoint NVARCHAR(30)
   ,@filesID            NVARCHAR(30)
   ,@filePath           NVARCHAR(1000)
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRANSACTION
    DECLARE
         @timestamp DATETIME2(3) = SYSDATETIME()
        ,@nameMeasuringPointID INT
        ,@sql_select NVARCHAR(1000)
        ,@sql_insert NVARCHAR(1000)

    IF OBJECT_ID(N'tempdb..#tmp') IS NOT NULL
    BEGIN
        DROP TABLE #tmp
    END

    CREATE TABLE #tmp
    (
        A VARCHAR(30)
       ,B VARCHAR(30)
       ,C VARCHAR(30)
    )

    SET @sql_insert = N'BULK INSERT #tmp
            FROM @filePath
            WITH
            (
                FIELDTERMINATOR = '';'',
                ROWTERMINATOR = ''\n'',
                ROWS_PER_BATCH = 10000, 
                TABLOCK
            )'

    EXEC sp_executesql @sql_insert,
        N'@filePath NVARCHAR(1000)', @filePath;

    -- get @nameMeasuringPointID 
    SET @sql_select = N'SELECT @nameMeasuringPointID = mm.MeasuringPointID FROM dbo.MeasuringPoints AS mm
                        WHERE mm.LocationName = @nameMeasuringPoint'

    EXEC sp_executesql @sql_select, 
        N'@nameMeasuringPoint NVARCHAR(30), @nameMeasuringPointID INT OUTPUT', @nameMeasuringPoint, @nameMeasuringPointID OUTPUT;
    
    INSERT INTO dbo.Readings(UsersID, FilesID, A, B, C, FileName, XDateTime, NameMeasuringPointID)
    SELECT @usersID, @filesID, A, B, C, @fileName, @timestamp, @nameMeasuringPointID 
    FROM #tmp;
    
    DROP TABLE #tmp;

    IF @@ERROR <> 0
    BEGIN
        GOTO error;
    END;

    COMMIT TRANSACTION;
    RETURN 0;

    error:
    IF @@TRANCOUNT > 0
    BEGIN
        ROLLBACK TRANSACTION;
    END;

    RETURN 1;
END;
GO

GRANT EXEC ON [dbo].[sp_writeBulk] TO myuser;
GO

当我执行它时:

exec sp_writeBulk '2', 'name_test', 'MP Test', '1', 'C:\Users\myuser\Downloads\myTest.csv'

我得到:

Incorrect syntax near '@filePath'.

我不明白它有什么问题?

遗憾的是,您无法参数化 BULK INSERT 语句。相反,您将不得不 安全地 注入它,使用 QUOTENAME

 SET @sql_insert = N'BULK INSERT #tmp
         FROM ' + QUOTENAME(@filePath, '''') + N'
         WITH
         (
             FIELDTERMINATOR = '';'',
             ROWTERMINATOR = ''\n'',
             ROWS_PER_BATCH = 10000, 
             TABLOCK
         )';

 EXEC sp_executesql @sql_insert;

The grammar for BULK INSERT 表示文件名必须是引号中的文字:

BULK INSERT
   { database_name.schema_name.table_or_view_name | schema_name.table_or_view_name | table_or_view_name }
      FROM 'data_file'