Обновление столбца с identity через ADO (MSSQL)

Работа VB и СУБД (Access, MSSQL, MySQL, Oracle и пр.)
Правила форума
При создании новой темы не забывайте указывать используемую СУБД.
alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Обновление столбца с identity через ADO (MSSQL)

Сообщение alibek » 21.07.2009 (Вт) 20:42

Столкнулся с такой проблемой.
Есть утилита, которая осуществляет импорт и экспорт баз данных (MS SQL 2000 и MS SQL 2005).
При экспорте данных все данные из БД выгружаются в файл. При импорте данных вначале производится очистка БД, затем в таблицы загружаются данные из файла.
Импорт осуществляется примерно таким циклом:
Код: Выделить всё
Recordset.AddNew
For I = 0 To Recordset.Fields.Count - 1
  Recordset.Fields(I).Value = ...
Next I
Recordset.Update

При этом, разумеется, возникает ошибка в случае, когда обновляемый столбец является счетчиком (identity).
Проверить это можно с помощью выражения Recordset.Fields(I).Properties("ISAUTOINCREMENT").Value (для счетчика свойство вернет True), однако что с этим делать дальше — непонятно. Я так и не нашел способ, как все-таки заставить записать данные в такое поле.

При загрузке данных с помощью SQL-скрипта есть возможность обойти такое ограничение, указав команду
Код: Выделить всё
set identity_insert ... on

После этого можно с помощью insert into ... values ... присваивать значения и полям-счетчикам.
Но когда я переделал загрузку на SQL, снова возникли проблемы.
Такой код:
Код: Выделить всё
  cnnDatabase.Execute "set identity_insert [" & D.Tables(C) & "] on", , ADODB.ExecuteOptionEnum.adExecuteNoRecords
  SQL = "insert into [" & D.Tables(C) & "] ("
  For I = LBound(T.Fields) To UBound(T.Fields)
    If I > LBound(T.Fields) Then SQL = SQL & ","
    SQL = SQL & "[" & T.Fields(I) & "]"
  Next I
  SQL = SQL & ")" & vbSQLLine
  For ...
    rs.AddNew
    S = "values ("
    For I = LBound(T.Fields) To UBound(T.Fields)
      If I > LBound(T.Fields) Then S = S & ","
      If IsNull(R.Values(I)) Then
        S = S & "null"
      Else
        Select Case rs.Fields(I).Type
          Case adBoolean
            S = S & IIf(R.Values(I), "1", "0")
          Case adTinyInt, adSmallInt, adInteger, adBigInt
            S = S & Trim$(Str$(R.Values(I)))
          Case adSingle, adDouble, adNumeric, adDecimal, adCurrency
            S = S & Trim$(Str$(R.Values(I)))
          Case adDate, adDBDate, adDBTime, adDBTimeStamp
            S = S & Format$(R.Values(I), "{\t\s 'yyyy-mm-dd hh:nn:ss'}")
          Case adBSTR, adChar, adVarChar, adWChar, adVarWChar, adLongVarChar, adLongVarWChar
            S = S & QuoteString(R.Values(I))
          Case Else
            Stop
            S = S & "null"
        End Select
      End If
    Next I
    S = S & ")"
    cnnDatabase.Execute SQL & S, , ADODB.ExecuteOptionEnum.adExecuteNoRecords Or ADODB.CommandTypeEnum.adCmdText
  Next
  cnnDatabase.Execute "set identity_insert [" & D.Tables(C) & "] off", , ADODB.ExecuteOptionEnum.adExecuteNoRecords

снова вызывает ошибку, при выполнении cnnDatabase.Execute SQL & S с identity-столбцом выдается ошибка, что подобное возможно только когда identity_insert установлено в ON. Хотя перед началом цикла я это выполнял.
Если все делать одной командой:
Код: Выделить всё
Connection.Execute "set identity_insert table on " & vbNewLine & "insert into ... values ..." & vbNewLine & set identity_insert table off"

тогда все работает. Но мне бы не хотелось при загрузке каждой строки (а их могут быть сотни тысяч) устанавливать, а затем сбрасывать identity_insert.
Было у меня подозрение, что каким-то образом там включен auto-commit, однако выполнение команды set implicit_transactions off ничего не меняет.

Если нужна еще какая-то информация, то этот вопрос я также задал тут.
Lasciate ogni speranza, voi ch'entrate.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Re: Обновление столбца с identity через ADO (MSSQL)

Сообщение alibek » 21.07.2009 (Вт) 21:30

Вопрос снимается.
По первому вопросу (добавление через ADO) — похоже не судьба. На сайте MS есть KB253157, из которого следует, что багу этому уже много лет и исправлять его, похоже, не собираются.
По второму вопросу (добавление через insert) тоже прояснилось, надо было открывать транзакцию, внутри нее identity_insert задается. Т.е. алгоритм такой: BeginTrans : Execute "set identity_insert ... on" : Execute "insert into ... values ..." : Execute "set identity_insert ... off" : CommitTrans. Так вроде бы все работает.
Lasciate ogni speranza, voi ch'entrate.


Вернуться в Базы данных

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 55

    TopList