Как я могу автоматически увеличивать первичный ключ в SQL Server 2016 слиянием вставки без последовательностей? - PullRequest
0 голосов
/ 12 июня 2018

Я пишу запрос для импорта данных из одной таблицы в новую таблицу.Мне нужно вставить записи, которые не существуют в новой таблице, и обновить записи, которые существуют.Я пытаюсь использовать MERGE "upsert" метод.

У меня есть некоторые уникальные проблемы из-за клиентской базы данных и структуры приложения.Таблица, в которую я вставляю, имеет поле уникального идентификатора, которое увеличивается на 1 для каждой новой строки, но таблица не выполняет автоматическое увеличение;оператор вставки должен получить самый высокий идентификатор в целевой таблице и добавить 1 для новой записи.

Из моего исследования я не могу понять, как это сделать с помощью MERGE.Я не базы данных разрешений на создание последовательности.Я пробовал много вещей, но в настоящее время мой запрос выглядит так:

MERGE
    dbo.targetTable as target
USING
    dbo.sourceTable AS source
ON
    target.account_no = source.account_ID

WHEN NOT MATCHED THEN
    INSERT (
        ID,
        FIELD1,
        FIELD2,
        FIELD3
) VALUES (
        (SELECT MAX(ID) + 1 FROM dbo.targetTable),
        'field1',
        'field2',
        'field3'
)

Проблема, с которой я затем сталкиваюсь с этим кодом, заключается в том, что он, кажется, только один раз запускает оператор выбора для нового идентификатора.То есть, если максимальный идентификатор в целевой таблице равен 10, он будет вставлять каждую новую запись с идентификатором 11. Это не сработает, поскольку я получаю ошибку Violation of PRIMARY KEY constraint. Cannot insert duplicate key in object.Я много гуглял и пробовал разные вещи, но не смог понять это.Спасибо за любую помощь, спасибо.

РЕДАКТИРОВАТЬ: Для пояснения, столбец уникальный идентификатор не заполняется автоматически.Если я не вставлю значение для столбца ID, я получу Cannot insert the value NULL into column 'ID', table 'dbo.targetTable'; column does not allow nulls. UPDATE fails.

И снова, как я уже говорил, у меня нет прав на создание последовательностей.Он просто выдает ошибку и говорит, что у меня нет разрешения на это.

Я согласен, что автоматическое изменение столбца ID на автоматическое увеличение было бы идеальным, но у меня нет возможности изменять таблицу, напримерчто либо.

Ответы [ 2 ]

0 голосов
/ 12 июня 2018

Это просто другой способ без использования слияния.Для временных таблиц разрешения не требуются, поэтому я бы использовал один для хранения номеров счетов, которые необходимо вставить, с полем идентификатора, чтобы помочь с обходом.Цикл while может проходить идентичность, вставляя значения относительно исходной таблицы account_no- в целевую таблицу.Поскольку вставка выполняется в цикле, функция MAX должна правильно захватывать MAX(account_no) таблицы назначения в каждом цикле.

DECLARE @tempTable TABLE (pkindex int IDENTITY(1,1) PRIMARY KEY, account_no int)
DECLARE @current int = 1
       ,@endcount int = 0

--account_no's that should be inserted
INSERT INTO @tempTable(account_no)
SELECT account_no 
  FROM sourceTable 
 WHERE account_no NOT IN (SELECT account_no FROM targetTable)

SET @endcount = (SELECT COUNT(*) FROM @tempTable)

--looping condition, should select the MAX(ID) with each subsequent loop
WHILE (@endcount > 0) AND (@current <= @endcount)
BEGIN
INSERT INTO dbo.targetTable(ID, FIELD1, FIELD2, FIELD3)
SELECT (SELECT MAX(T2.ID) + 1 FROM dbo.targetTable T2) AS MAXID
      ,S.field1
      ,S.field2
      ,S.field3
  FROM @tempTable T INNER JOIN sourceTable S ON T.account_no = S.account_no
 WHERE T.pkindex = @current --traversing temp table by its identity

SET @current += 1

END
0 голосов
/ 12 июня 2018

Если вам не нужно, чтобы идентификаторы были последовательными, вы можете добавить последний доступный ID к ROW_NUMBER() для создания новых неповторяющихся идентификаторов.

BEGIN TRANSACTION

    DECLARE @NextAvailableID INT = (SELECT ISNULL(MAX(ID), 0) FROM dbo.targetTable WITH (TABLOCKX))

    ;WITH SourceWithNewIDs AS
    (
        SELECT
            S.*,
            NewID = @NextAvailableID + ROW_NUMBER() OVER (ORDER BY S.account_ID)
        FROM
            dbo.sourceTable AS S
    )
    MERGE
        dbo.targetTable as target
    USING
        SourceWithNewIDs AS source
    ON
        target.account_no = source.account_ID
    WHEN NOT MATCHED THEN
        INSERT (
            ID,
            FIELD1,
            FIELD2,
            FIELD3
    ) VALUES (
            NewID,
            'field1',
            'field2',
            'field3'
    )

COMMIT

Имейте в видучто в этом примере отсутствует правильная обработка ошибок с откатом, а блокировка, используемая для получения максимального идентификатора, будет блокировать все остальные операции до тех пор, пока не будет зафиксировано или выполнено откат.

Если новые строки должны иметь последовательные идентификаторы, вы можете использоватьэтот же подход с обычным INSERTWHERE NOT EXISTS...) вместо MERGE (придется писать UPDATE отдельно).

...