如何在 php 中使用 BULK INSERT DATA 添加外部变量?

Posted

技术标签:

【中文标题】如何在 php 中使用 BULK INSERT DATA 添加外部变量?【英文标题】:How to add an external variable with BULK INSERT DATA in php? 【发布时间】:2020-11-14 03:40:48 【问题描述】:

我有一个以 UTF-8-BOM 编码的大型 csv 文件(超过 100,000 行),如下所示:

27336;00203-AND1;90-BLACK;9.5;2
27336;00203-ET1;90-BLACK;10;1
27336;00203-ET1;90-BLACK;12;1
...

我的 SQL Server 数据库中的一个表包含这些列:

storenumber | stylecode | colour | size | units | timestamp

我使用批量插入数据一次加载文件,但我想将我的 $timestamp 变量添加到插入到表中的每一行,但它不起作用......我该怎么做?

 <?php

include("connexion.php");

ini_set('max_execution_time', 32400);
$timestamp= date("y-m-d H:i");


$csv= "D:/xampp/htdocs/retail_BI/files/BI2_20200720_1344_00076.txt";

                     $query = "BULK INSERT dbo.Y2_Inventory 
                    FROM '$csv' 
                    WITH (
                    FIELDTERMINATOR = ';', 
                    ROWTERMINATOR = '\n',
                    ERRORFILE = 'myfileerror.log'
                     )";

                     $stmt = $conn->query( $query );     
                     if (!$stmt)  echo $conn->error; 


$query2 = "UPDATE dbo.Y2_Inventory SET timestamp = ? WHERE timestamp IS NULL";

                     $stmt = $conn->query( $query2 );     
                    
            
        echo "good";        

?>

【问题讨论】:

你不能对BULK INSERT 进行参数化,你必须使用动态 SQL 并安全地注入值(你在这里没有做的事情)。 我想使用 BULK INSERT 因为否则处理我的文件需要几个小时...我可以提高插入速度吗? @Larnu @Eric27 你对LASTROW = '$timestamp' 有什么期望? LASTROW 只是指定要加载的最后一行的编号。您可以尝试在BULK INSERT 之后执行和附加UPDATE 但是如果我之后再做,他不会把我的 $timestamp 插入到我表格的每一行吗? @Zhorov 我假设在插入timestamp 列时是NULL,所以UPDATE .. SET timestamp = ? WHERE timestamp IS NULL 应该可以工作。 【参考方案1】:

您需要考虑以下几点:

始终尝试在语句中使用参数(当然,如果可能的话)或仔细清理输入数据。在这种特定情况下,您可以尝试检查输入文件是否存在,然后在您的语句中注入文件名。 输入数据与表定义不匹配,您可以尝试在临时表中导入数据。 使用明确的格式(例如 2020-07-25T12:00:00)将 datetime 值作为文本传递。

以下示例是您的问题的可能解决方案:

表:

CREATE TABLE Y2_Inventory (
    storenumber int,
    stylecode nvarchar(50),
    colour nvarchar(50),
    size numeric(10, 1), 
    units int,
    [timestamp] datetime
)

PHP 脚本:

<?php
//
include("connexion.php");
ini_set('max_execution_time', 32400);

// CSV file
$csv = "D:/xampp/htdocs/retail_BI/files/BI2_20200720_1344_00076.txt";  
$err = "D:/xampp/htdocs/retail_BI/files/BI2_20200720_1344_00076.err";
if (!file_exists($csv)) 
    die("CSV file not exists.");    

    
// Time stamp
$timestamp = date("Y-m-d\TH:i:s");

// INSERT Data
try 
    $query = "
        SET NOCOUNT ON;
        
        SELECT storenumber, stylecode, colour, size, units
        INTO #t
        FROM Y2_Inventory
        WHERE 1 = 0;
        
        BULK INSERT #t 
        FROM '$csv' 
        WITH (
            ERRORFILE = '$err',
            FIELDTERMINATOR = ';', 
            ROWTERMINATOR = '\n'
        );
        
        INSERT INTO Y2_Inventory (storenumber, stylecode, colour, size, units, [timestamp])
        SELECT storenumber, stylecode, colour, size, units, ?
        FROM #t;
    
        DROP TABLE #t;
    ";
    $stmt = $conn->prepare($query);     
    $stmt->bindParam(1, $timestamp, PDO::PARAM_STR);
    $stmt->execute();
    echo "OK";        
 catch (PDOException $e) 
    die ("Error executing query. ".$e->getMessage());

?>

【讨论】:

@Eric27 这是一个工作示例,使用 SQL Server 2017、PHP 7.4.8 和 PHP Driver for SQL Server 5.8 进行测试。 @Eric27 这是一个很好的起点。如果您在实施此解决方案时遇到困难,您可以针对您面临的问题提出问题。我相信我们大多数人都会尽力帮助你。 @Eric27 ERRORFILE BULK INSERT 中的选项。 好的,错误来自那里,因为我的文件在我的网络服务器上,而不是在我的 SQL Server @Zhorov @HrvojeT,它只是一个WHERE 子句,它返回FALSE,所以插入的行总是零。【参考方案2】:

就像我在评论中提到的那样,您不能参数化 BULK INSERT 语句。因此,您必须使用动态 SQL。我不知道/写 PHP,但是,我可以清楚地看到上面的 wide 可以注入,因为您只是将文件名注入到 SQL 语句中。你需要解决这个问题,并参数化你的声明How to: Perform Parameterized Queries。

至于 SQL,它看起来像这样:

DECLARE @FilePath nvarchar(256); --This would be your parameter, so might not bneed a declaration

DECLARE @SQL nvarchar(MAX);
SET @SQL = N'BULK INSERT dbo.Y2_Inventory 
FROM N''' + REPLACE(@FilePath,'''','''''') + N'''
     WITH(FIELDTERMINATOR = '';'',
          FIELDTERMINATOR = ''\n'',
          ERRORFILE = ''myfileerror.log'');';

--PRINT @SQL; --Your Best Friend

EXEC sys.sp_executesql @SQL;

【讨论】:

感谢您的回答,所以现在我必须在 php 中调整这个 sql 代码才能在我的代码中执行它? @Larnu 正确,正如我所说,我不写 PHP,所以我省略了,但是,我已经在文档上链接了你如何做到这一点。 您的代码更正了 sql 注入,但没有同时添加我的最后一个 $timestamp 列? @Larnu 这是一个完全不同的查询,@Eric27。我正在处理BULK INSERT,因为这是您遇到问题的部分。

以上是关于如何在 php 中使用 BULK INSERT DATA 添加外部变量?的主要内容,如果未能解决你的问题,请参考以下文章

Elasticsearch:使用 Python 进行 Bulk insert 及 Scan

Elasticsearch:使用 Python 进行 Bulk insert 及 Scan

如何在 SQL Standard 上导出 SSIS 数据并在 SQL Express 上使用 Bulk Insert 导入?

获取 Bulk.Insert() -Mongoskin 的插入 ID

获取 Bulk.Insert() -Mongoskin 的插入 ID

bulk.insert(doc) 默认值无法插入 MongoDB nodejs