如何在 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 这是一个很好的起点。如果您在实施此解决方案时遇到困难,您可以针对您面临的问题提出问题。我相信我们大多数人都会尽力帮助你。 @Eric27ERRORFILE
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