Записи исчезают в цикле транзакций PDO mssql

У меня есть следующий код (более или менее) для импорта от 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, который отлично работает, но если у кого-то есть другие советы по устранению этой проблемы, сообщите мне. Поскольку я предпочитаю использовать свой оригинальный метод.


person Derk Arts    schedule 02.07.2012    source источник
comment
Запуск кода без beginTransaction и объединение всех запросов на вставку в одну транзакцию приводит к исчезновению около 40 000 записей...   -  person Derk Arts    schedule 02.07.2012
comment
Если я повторю этот цикл без транзакций, он будет работать нормально. Ни одна запись не потеряна...   -  person Derk Arts    schedule 02.07.2012
comment
Проблема не вызвана PDO. Это уж точно.   -  person Derk Arts    schedule 02.07.2012
comment
Я бы попробовал это msdn.microsoft.com/en-us/library/ms188365.aspx так как там много данных   -  person allen213    schedule 02.07.2012
comment
Вы имеете в виду импорт файла CSV непосредственно в SQL? Это означало бы, что мне пришлось бы читать CSV-файл, очищать его, записывать обратно в другой CSV-файл и вставлять его в базу данных. Я мог бы это сделать, но это не очень эффективно.   -  person Derk Arts    schedule 02.07.2012
comment
Вы уверены, что проблема не в вашем самодельном парсере CSV? Если вы упростите проблему до вставки массива (a$i, b$i, c$i) для каждого $i в range(0, 50000) и удалите весь проблемный код (спящие, try .. catch и промежуточные коммиты), вы все равно сможете воспроизвести проблему. ? Если да, можете ли вы дать ссылку на полный пример скрипта?   -  person phihag    schedule 04.07.2012
comment
Возможные исключения из $pdo->commit(); и $pdo->beginTransaction(); таким кодом не перехватываются, если я правильно понял.   -  person vyegorov    schedule 04.07.2012
comment
Фи: Как уже было сказано, когда я пропускаю транзакцию, все идет медленно, но хорошо, нет пропущенных записей, поэтому нет проблем с моим парсером CSV. ВЭЙ: Какие исключения? Я думал, что эти исключения будут возникать, когда я выполняю запрос. Кроме того, когда я замедляю работу, я теряю меньше записей, поэтому неотловленные ошибки в SQL не являются проблемой.   -  person Derk Arts    schedule 04.07.2012
comment
В сценарии $sSql отсутствует ключевое слово VALUES. Это слово также отсутствует в скрипте в вашем рабочем коде?   -  person Andriy M    schedule 05.07.2012
comment
Всегда ли commit и begintansactoan возвращает true? ¿отсутствующие записи коррелятивны?   -  person    schedule 07.07.2012


Ответы (4)


Рассматривали ли вы возможность использования Sprocs вместо операторов вставки? запись ЛЮБОГО количества записей последовательно - по одной за раз - это пустая трата времени / энергии ... это просто не так быстро, как должно быть.

Вы уверены, что не можете использовать BULK INSERT или XML вместо того, чтобы вставлять несколько строк одновременно?

person Aaron Kempf    schedule 05.07.2012
comment
Это то, что я делаю прямо сейчас в качестве обходного пути. Но я думаю, что это просто ужасно, что записи исчезают без предупреждения... - person Derk Arts; 05.07.2012

У меня была эта проблема раньше. Для меня мне пришлось сделать «SET NOCOUNT ON» перед INSERTS, потому что SQL Server пытался вернуть мне «одна добавленная строка» для каждой INSERT, и очередь сообщений была заполнена, и он просто перестал вставлять данные, не возвращая никаких ошибок!

Так что вам обязательно нужно попробовать сделать «SET NOCOUNT ON» перед INSERTS. Держу пари, это решит твою проблему.

person Danielle Paquette-Harvey    schedule 04.07.2012
comment
Это звучит вполне правдоподобно! Попробую сегодня! - person Derk Arts; 05.07.2012
comment
Перед каждым оператором вставки или только один раз? - person Derk Arts; 05.07.2012
comment
Не решает к сожалению. '14:57:10[119] | РЕЗУЛЬТАТ ДЛЯ таблицы: Всего строк: 466792 Успешно: 466789 Неудачно: 2 ' -> select count(*) from table = 441925 - person Derk Arts; 05.07.2012

Вы используете 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 .= '(..., ..., ...),'
}

А потом беги!

person Maykonn    schedule 06.07.2012

@Саратис,

Рассматривали ли вы создание простого sproc, который выполняет желаемое действие с помощью 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

Теперь, чтобы получить информацию в SQL Server из CSV, по следующей ссылке объясняется, как этого можно добиться, используя путь к файлу как часть предложения 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, поэтому мне придется протестировать это дома сегодня вечером, чтобы получить рабочий пример для работы.

person EastOfJupiter    schedule 05.07.2012
comment
Это хорошо, но я не думаю, что это применимо к импорту CSV-файла, или я ошибаюсь? - person Derk Arts; 10.07.2012
comment
Прошу прощения, я не увидел в вашем исходном сообщении, что вы импортировали из CSV. Эта ссылка может предложить решение. sqlserverpedia.com/blog /sql-server-bloggers/ Выберите CSV в общем табличном выражении, затем выполните слияние. Я собираюсь обновить свой ответ, чтобы включить эту ссылку. - person EastOfJupiter; 10.07.2012