记录在PDO mssql事务循环中消失

时间:2012-07-02 09:09:38

标签: php sql sql-server transactions pdo

我有以下代码(或多或少)可以导入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 个答案:

答案 0 :(得分:3)

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

所以你绝对应该尝试在INSERTS之前做一个“SET NOCOUNT ON”。 我打赌它会解决你的问题。

答案 1 :(得分: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 .= '(..., ..., ...),'
}

然后跑!

答案 2 :(得分:2)

@Saratis,

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

我的理念是数据库应该控制如何使用数据,并且代码应该控制数据库何时执行它所做的事情。我更喜欢做的是保留任何触及存储过程中数据的内容,并在某些条件/事件发生时用代码调用存储过程。但是,您的情况可能足够独特,这不是最佳做法。

下面的代码片段来自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/

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

答案 3 :(得分:1)

您是否考虑过使用Sprocs而不是insert语句?按顺序编写任意数量的记录 - 一次一个 - 浪费时间/精力......它没有它应该的那么快。

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