6

我有以下代码(或多或少)可以导入从 500.000 到 4.000.000 行的任何位置:

$sSql = "Insert into table (a,b,c) VALUES(?,?,?)"
$oSQLStmnt = $pdo->prepare($sSql);
$oSQLStmnt->setAttribute(PDO::SQLSRV_ATTR_ENCODING, PDO::SQLSRV_ENCODING_SYSTEM);
if (!$oSQLStmnt) {
    echo $pdo->errorInfo(); // Handle errors
}
$pdo->beginTransaction();
$iLineCounter = 1;
while (($sLine = fgets ($oCSV, 8000)) !== FALSE) {
      $aLine = explode('|', $sLine); //Fgetscsv did not work properly 
       if ($iLineCounter % 100 == 0) {
            lo("Inserting row " . $iLineCounter);
            $pdo->commit();
            sleep(0.15);
            $pdo->beginTransaction();
       }
       try {
            $oSQLStmnt->execute($aLine);
            $iSuccesulInserts++;
       }
       catch (exception $e) {
            print_r($e);
            $iFailedInserts++;
       }

       $iLineCounter++;
}
$pdo->commit();

如您所见,我每 100 行执行一次提交,甚至添加了一些睡眠。我过去每 25.000 行只运行一次提交,而且我没有使用任何睡眠。然而,在某一时刻,我发现我丢失了记录。我开始使用这些设置(睡眠和行数)。这样,我将丢失记录的数量从 50.000 减少到大约 100。但我仍然缺少记录!他们要去哪?我知道 SQL 没问题,因为当出现问题时我会立即收到错误消息。

我以为我可以在事务期间堆叠很多插入?调用 beginTransaction 会不会有问题?

更新:

赏金结束了,我不得不奖励它。谢谢大家的答案。或者实际上是提示,因为你们都没有真正回答我的问题。尽管非常感谢您的建议,但我并没有要求解决方法。赏金被授予的答案是因为它最接近实际回答我的问题。不幸的是,它没有用。

现在我正在使用 CSV 批量导入,效果很好,但如果有人有任何其他解决此问题的提示,请告诉我。因为我更喜欢使用我原来的方法。

4

4 回答 4

3

我以前遇到过这个问题。对我来说,我必须在 INSERTS 之前执行“SET NOCOUNT ON”,因为 SQL Server 试图为每个 INSERT 返回“添加一行”,并且消息队列已满,它只是停止插入数据,没有返回任何错误!

因此,您绝对应该尝试在 INSERTS 之前执行“SET NOCOUNT ON”。我敢打赌它会解决你的问题。

于 2012-07-04T17:43:41.610 回答
3

您使用 sleep() 0.15 秒来延迟执行,但是,问题:如果 INSERT 花费的时间超过 0.15 秒会发生什么?由于先前的提交,要运行的脚本和表可能会被阻止。

然后尝试在数据库中一次运行多个 INSERT 的方法。尝试这样的事情:

INSERT INTO example (example_id, name, value, other_value)VALUES
(100, 'Name 1', 'Value 1', 'Other 1'), (101, 'Name 2', 'Value 2', 'Other 2'),
(102, 'Name 3', 'Value 3', 'Other 3'), (103, 'Name 4', 'Value 4', 'Other 4');

为此,请执行以下操作:

$sql = ' INSERT INTO example (example_id, name, value, other_value)VALUES';
while (($sLine = fgets ($oCSV, 8000)) !== FALSE) {
    // generate VALUES to INSERT in a $sql .= '(..., ..., ...),'
}

然后跑!

于 2012-07-06T14:40:02.253 回答
2

@萨拉蒂斯,

您是否考虑过创建一个使用 MERGE 执行所需操作的简单存储过程?合并会消耗一些相当大的开销,但是,我一直都知道它是将记录从“主”数据源同步到依赖数据源的一种非常可靠的方法。

我的理念是数据库应该控制数据的使用方式,代码应该控制数据库何时执行它的操作。我更喜欢做的是将任何接触数据的东西保存在存储过程中,并在某些条件/事件发生时用代码调用存储过程。但是,您的情况可能非常独特,以至于这并不是最佳实践。

以下代码片段来自 Microsoft,作为如何完成合并的示例:

MERGE Production.UnitMeasure AS target
USING (SELECT @UnitMeasureCode, @Name) AS source (UnitMeasureCode, Name)
ON (target.UnitMeasureCode = source.UnitMeasureCode)
WHEN MATCHED THEN 
    UPDATE SET Name = source.Name
WHEN NOT MATCHED THEN   
    INSERT (UnitMeasureCode, Name)
    VALUES (source.UnitMeasureCode, source.Name)
    OUTPUT deleted.*, $action, inserted.* INTO #MyTempTable;

这是整篇文章的链接,涵盖了几个不同的场景: http ://technet.microsoft.com/en-us/library/bb510625.aspx

现在,要将信息从 CSV 获取到 SQL Server,以下链接说明了如何使用文件路径作为 FROM 子句的一部分并在 WITH 子句中指定分隔符来实现这一点。

它也涵盖了 BULK INSERT,如果这对您最有效,但是,我偏爱 MERGE,因为它处理新记录的 INSERT 和 UPDATES 现有记录。 http://sqlserverpedia.com/blog/sql-server-bloggers/so-you-want-to-read-csv-files-huh/

仅供参考,仅当文件与 SQL Server 实例位于相同的磁盘上时,BULK INSERT 才有效。可以理解的是,我的公司不会授予我访问 SQL Server 本地驱动器的权限,因此我今晚必须在家中对此进行测试,以便为您提供一个可以使用的工作示例。

于 2012-07-05T16:45:28.680 回答
1

您是否考虑过使用 Sprocs 而不是插入语句?按顺序写入任意数量的记录 - 一次一个 - 有点浪费时间/精力。它只是没有应有的速度。

您确定不能使用 BULK INSERT 或 XML 来一次插入多行吗?

于 2012-07-05T10:35:44.343 回答