Оператор UPDATE использует EXEC для выполнения хранимой процедуры для установки значения столбца - PullRequest
7 голосов
/ 03 ноября 2010

Я почти закончил перевод хранимой процедуры на основе курсора на набор. Почти, потому что мне осталось выяснить только одну вещь.

Они используют хранимую процедуру с именем GetSequence, чтобы запросить таблицу, обновить ее новым порядковым номером (старый + 1) и вернуть новое значение порядкового номера. Это не было проблемой, когда они использовали курсоры, потому что они присваивали выходное значение переменной, а затем использовали переменную.

Единственный способ сохранить новый набор хранимых процедур - это выполнить GetSequence в операторе INSERT или UPDATE. Тем не менее, я получаю эту удивительно конкретную ошибку "Неверный синтаксис рядом с ключевым словом" EXEC "", когда я пытаюсь это сделать.

Это старый код:

  DECLARE @new_UD_campaignID BIGINT -- Get the new ud_lead_id for the new lead set
  EXEC ppGlobal.dbo.Getsequence
    'ud_campaign_id',
    @new_UD_campaignID OUTPUT
  DECLARE @OrderNum VARCHAR(9);
  IF @corpCamp LIKE '%LEP%'
    BEGIN
        SELECT @OrderNum = ( 'L' + RIGHT('00000000' + CAST(@new_UD_campaignID AS VARCHAR(8)), 8) )
    END
  ELSE
    BEGIN
        SELECT @OrderNum = ( 'C' + RIGHT('00000000' + CAST(@new_UD_campaignID AS VARCHAR(8)), 8) )
    END

Это работает, но очень медленно, потому что оно находится в курсоре и обновляет более двух миллионов строк.

Новый код, который я пробую, выглядит следующим образом:

  UPDATE @List
  SET   OrderNumBigInt = EXEC (ipCore.dbo.Getsequence
                                     'ud_campaign_id',
                                     @new_UD_campaignID OUTPUT)

Я не могу найти какую-либо конкретную документацию, указывающую, что вы не можете выполнить хранимую процедуру в операторе SELECT или UPDATE для установки значения столбца.

Кто-нибудь пробовал что-то подобное, но с успехом?

Ответы [ 3 ]

2 голосов
/ 03 ноября 2010

То, что вы предлагаете, не может быть сделано в MSSQL (AFAIK). На самом деле, я сомневаюсь, что предложения по преобразованию GetSequence в функцию, вероятно, тоже не будут работать, поскольку последний ud_campaing_id, вероятно, хранится в некоторой «глобальной» таблице ...

Предполагая, что хранимая процедура GetSequence вызывается разными процессами "одновременно", я бы посоветовал вам либо

  • необходимо адаптировать указанный sp, чтобы вы могли запрашивать сразу несколько кодов (дополнительный параметр, например, @number_of_ids, который по умолчанию равен 1), чтобы выходной параметр возвращал первый запрошенный идентификатор, но внутренне также резервировал следующие n для вас, который вы затем можете использовать для обновления @ list
  • необходимо создать жесткий цикл, который будет получать количество идентификаторов, а затем применить их за один раз к вашей целевой таблице.

Хотя я, безусловно, поддерживаю первое решение, оно требует внесения изменений в то, что представляется самой основной хранимой процедурой, что может не понравиться или не позволить dba. Тем не менее, это сделало бы вещи НАМНОГО быстрее. Второе решение все еще требует некоторого зацикливания, а также имеет некоторые серьезные требования к индексации при применении результирующих данных к конечной таблице, поэтому оно далеко от совершенства, но может быть, по крайней мере, немного быстрее, чем зацикливание непосредственно над целевая таблица и выборка и применение новой записи данных по записи.

Судя по подходу UPDATE @list, который вы используете, я думаю, вы уже в курсе второго предложения. Предполагая, что у вас есть поле идентификации в @list (с ограничением UNIQUE OR PK на нем и без пробелов), вы можете попробовать что-то вроде этого:

DECLARE @RecordID, @LastRecordID int
DECLARE @new_UD_campaignID bigint

SELECT @RecordID = Min(RecordID), 
       @LastRecordID = Max(RecordID)
  FROM @list

DECLARE @newCampaingIDs TABLE (RecordID int PRIMARY KEY, new_UD_campaignID varchar(8))

WHILE @RecordID <= @LastRecordID
    BEGIN

        EXEC ppGlobal.dbo.Getsequence 'ud_campaign_id', @new_UD_campaignID OUTPUT

        INSERT @newCampaingIDs (RecordID, new_UD_campaignID) VALUES (@RecordID, RIGHT('00000000' + CAST(@new_UD_campaignID AS VARCHAR(8)), 8))

        SELECT @RecordID = @RecordID + 1
    END

UPDATE @list
   SET OrderNum = (CASE WHEN corpCamp LIKE '%LEP%' THEN 'L' ELSE 'C' END) + new_UD_campaignID
  FROM @list upd
  JOIN @newCampaingIDs new
    ON new.RecordID = upd.RecordID

Причина, по которой я думаю это будет быстрее, заключается в том, что последовательные вставки будут иметь (намного?) Меньше накладных расходов, чем обновление исходной записи таблицы по записи. С другой стороны, вы все еще застряли за неоднократным вызовом хранимого процесса GetSequence, который может быть вашим основным потребителем времени.

В любом случае, единственный способ узнать наверняка - это проверить его =)

Удачи.

2 голосов
/ 03 ноября 2010

Это задокументировано - BNF для UPDATE в BOL (выдержка) читает

...
UPDATE 
    ...
    SET
        { column_name = { expression | DEFAULT | NULL }
    ...

Определение для expression:

выражение

Является переменной, литеральным значением, выражением или оператором выборки (в скобках), что возвращает одно значение Значение возвращаемое выражение заменяет существующее значение в column_name или @ Переменная.

выполнение SP не является ни одним из них.

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

В качестве альтернативы, можно ли переписать SP (или записать новый SP) для работы на основе набора над группой записей?

0 голосов
/ 30 мая 2018

Вы не можете вызвать функцию или хранимую процедуру из оператора UPDATE (если кто-то узнает в MS SQL, пожалуйста, дайте мне знать, как это сделать), но вы определенно можете запустить UPDATE между вашими кодами.

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

Например: однажды моему клиенту потребовался индивидуальный идентификатор в базе данных вместе с общим идентификатором. Таким образом, я запекла настроенный идентификатор со строкой типа данных и запустила UPDATE в процессе выпечки. Я собираюсь опубликовать пример кода ниже ...

INSERT INTO ID (
F_Name
, L_Name
, [Date]
)
VALUES(
'Josie'
, 'Smith'
, getdate()
)

BEGIN
DECLARE @VAR int    -- after all other columns have been populated @VAR holds generic ID
        , @ID VARCHAR(8)    -- the INT value @VAR holds is passed down as string to @ID 
        , @R VARCHAR(8)     -- @R holds custome value, what ever you want to customise
        , @RID VARCHAR(16); -- @RID holds the string value of @ID and @R togather
SET @VAR = (SELECT MAX( ID) FROM ID)
SET @ID = @VAR
SET @R = 'RID-000'
SET @RID = @R+@ID
Update ID   -- I am running update statement right inside this block, to update custom ID column with customised ID
SET RID = @RID
WHERE ID = @VAR -- in where clause I am bounding this customised ID to populate only current ID filed
END

SELECT * FROM ID

The results are as follows:
    ID  F_Name  L_Name  Date        RID
1   218 Joe     Smith   2018-05-29  RID-000218
2   219 Jane    Smith   2018-05-29  RID-000219
3   220 Jane    Smith   2018-05-29  RID-000220
4   221 Josie   Smith   2018-05-29  RID-000221

В качестве альтернативы я мог бы взять весь кусок кода между BEGIN и END, поместить его в хранимую процедуру и запустить «сразу после» оператора INSERT. Просто помня об этом принципе, я использовал этот трюк как обходной путь к необходимости использовать хранимую процедуру или функцию в выражении UPDATE.

enter image description here

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...