SQL - вызов хранимой процедуры для каждой записи - PullRequest
44 голосов
/ 16 января 2010

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

SELECT @SomeIds = (
    SELECT spro.Id 
    FROM SomeTable as spro
    INNER JOIN [Address] addr ON addr.Id = spro.Id 
    INNER JOIN City cty ON cty.CityId = addr.CityId
    WHERE cty.CityId = @CityId
)


WHILE @SomeIds  IS NOT NULL
BEGIN
    EXEC UpdateComputedFullText @SomeIds
END

Такая вещь, конечно, не работает, конечно, но есть ли способ сделать что-то подобное?

Ответы [ 8 ]

65 голосов
/ 16 января 2010

Для этого нужно использовать курсор.

DECLARE @oneid int -- or the appropriate type

DECLARE the_cursor CURSOR FAST_FORWARD
FOR SELECT spro.Id  
    FROM SomeTable as spro 
        INNER JOIN [Address] addr ON addr.Id = spro.Id  
        INNER JOIN City cty ON cty.CityId = addr.CityId 
    WHERE cty.CityId = @CityId

OPEN the_cursor
FETCH NEXT FROM the_cursor INTO @oneid

WHILE @@FETCH_STATUS = 0
BEGIN
    EXEC UpdateComputedFullText @oneid

    FETCH NEXT FROM the_cursor INTO @oneid
END

CLOSE the_cursor
DEALLOCATE the_cursor
21 голосов
/ 18 января 2010

Удивлен, никто не дал вам актуальный ответ. Курсоры плохие. Вам нужно переместить логику SP в табличную функцию (TVF) и затем использовать CROSS APPLY

Вот запрос, который я написал вчера (не останавливайтесь на деталях, просто посмотрите на CROSS APPLY). CROSS APPLY создает объединение таблиц. Каждый элемент этого объединения генерируется из TVF, который параметризован в записях строк оператора select.

SELECT supt.hostname,supt.scriptname, COUNT(*)
FROM Event_Pagehit eph
    INNER JOIN Symboltable_urlpair supf
    ON eph.fromPagePair=supf.id
    INNER JOIN Symboltable_urlpair supt
    ON supt.id=eph.toPagePair
CROSS APPLY dbo.TDFCompanyFormationsUrlClassification(supf.hostname,supf.scriptname) as x
CROSS APPLY dbo.TDFCompanyFormationsUrlClassification(supt.hostname,supt.scriptname) as y
WHERE x.isCompanyFormations=1
AND y.isCompanyFormations=0
GROUP BY supt.hostname,supt.scriptname
ORDER BY COUNT(*) desc

Я могу использовать x и y, как если бы они были таблицами, извлеченными из предложений FROM или JOIN. Если бы мне пришлось написать этот запрос без TVF, он бы занимал пару сотен строк.

Примечание:

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

18 голосов
/ 16 января 2010

Поместите идентификаторы во временную переменную таблицы, а затем выполните итерации по каждой строке: (Вам не нужно использовать курсор, который будет значительно медленнее)

   Declare @Keys Table (key integer Primary Key Not Null)
   Insert @Keys(key)
   SELECT spro.Id  
   FROM SomeTable as spro 
       JOIN [Address] addr ON addr.Id = spro.Id  
       JOIN City cty ON cty.CityId = addr.CityId 
   WHERE cty.CityId = @CityId
   -- -------------------------------------------
   Declare @Key Integer
   While Exists (Select * From @Keys)
     Begin
         Select @Key = Max(Key) From @Keys
         EXEC UpdateComputedFullText @Key
         Delete @Keys Where Key = @Key
     End 

EDIT Удаление не медленное при использовании с предикатом фильтра, ориентированным на очень узкий уникальный индекс, как это. Но этого легко избежать, просто сделав цикл следующим образом:

Declare @Key Integer = 0
While Exists (Select * From @Keys
              Where key > @Key)
 Begin
     Select @Key = Min(Key) From @Keys
                   Where key > @Key
     EXEC UpdateComputedFullText @Key
     -- Delete @Keys Where Key = @Key No Longer necessary 
 End    
5 голосов
/ 16 января 2010

Попробуйте это без курсора

DECLARE @id int 

SELECT top 1 @id = spro.Id   
    FROM SomeTable as spro  
        INNER JOIN [Address] addr ON addr.Id = spro.Id   
        INNER JOIN City cty ON cty.CityId = addr.CityId  
    WHERE cty.CityId = @CityId
    ORDER BY spro.id

WHILE @@ROWCOUNT > 0 
BEGIN 
    EXEC UpdateComputedFullText @id 

    SELECT top 1 @id = spro.Id   
    FROM SomeTable as spro  
        INNER JOIN [Address] addr ON addr.Id = spro.Id   
        INNER JOIN City cty ON cty.CityId = addr.CityId  
    WHERE cty.CityId = @CityId 
    and spro.id > @id
    ORDER BY spro.id
END 
2 голосов
/ 16 января 2010

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

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

1 голос
/ 16 января 2010

Вам нужно будет использовать курсор: Примеры курсоров SQL Server

DECLARE @id int
DECLARE cursor_sample CURSOR FOR  
SELECT spro.Id 
FROM SomeTable as spro
    INNER JOIN [Address] addr ON addr.Id = spro.Id 
    INNER JOIN City cty ON cty.CityId = addr.CityId
WHERE cty.CityId = @CityId

OPEN cursor_sample
FETCH NEXT FROM cursor_sample INTO @id 
WHILE @@FETCH_STATUS = 0   
BEGIN  
    EXEC UpdateComputedFullText @id
    FETCH NEXT FROM cursor_sample INTO @id
END   

CLOSE cursor_sample
DEALLOCATE cursor_sample
0 голосов
/ 30 июня 2017

Стандартное решение для курсора - зло на зло. Два идентичных оператора FETCH NEXT - просто кошмар обслуживания.

лучше

...declare cursor etc.
While 1=1
 Fetch ...
 if @@FETCH_STATUS <> 0  BREAK
...
End -- While 
..Close cursor etc.

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

0 голосов
/ 17 января 2010

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

Вы можете поместить результаты SELECT во временную таблицу, а затем вызвать proc для выполнения массового SQL с содержимым временной таблицы. Временная таблица будет доступна вызываемому процессу на основе правил области видимости T-SQL.

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