SQL - вызов хранимой процедуры для каждой записи
Я ищу способ вызова хранимой процедуры для каждой записи оператора 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
Такая вещь выше, конечно, не работает, но есть ли способ сделать что-то подобное?
Ответы
Ответ 1
Для этого вам нужно использовать курсор.
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
Ответ 2
Удивленный никто не дал вам последний ответ. Курсоры плохие. Вы хотите переместить логику 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-сервера есть предостережения. Поэтому, если кто-то не говорит иначе, я предполагаю, что это так.
Ответ 3
Поместите идентификаторы в временную переменную таблицы, а затем итерации по каждой строке: (вам не нужно использовать курсор, который будет значительно медленнее)
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
РЕДАКТИРОВАНИЕ Удаление не является медленным при использовании с предикатом фильтра, управляемым с очень узким уникальным индексом, как это. Но его можно легко избежать, просто сделав цикл следующим образом:
Declare @Key Integer Set @Key = 0
While Exists (Select * From @Keys
Where key > @Key)
Begin
Select @Key = Min(Key) From @Keys
Where key > @KeyId
EXEC UpdateComputedFullText @Key
-- Delete @Keys Where Key = @Key No Longer necessary
End
Ответ 4
Оба ответа выше курсоров RE верны. Однако, исходя из сложности кода, выполняемого внутри курсора, вам может быть лучше отброшено это на выбранный вами язык и выполнить ваши вычисления в коде, прежде чем отбрасывать результаты в базу данных.
Я обнаружил, что возвращаюсь и просматриваю много операций с курсором, и во многих случаях переводит их в код по соображениям производительности.
Ответ 5
Попробуйте это без курсора
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
Ответ 6
Вам нужно будет использовать курсор: Примеры курсоров 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
Ответ 7
Вам действительно нужно выполнять пошаговую обработку, когда доступна заданная обработка?
Вы можете поместить результаты SELECT в временную таблицу, а затем вызвать proc для выполнения массива SQL с содержимым таблицы temp. Таблица temp будет доступна для вызванного proc на основе правил определения T-SQL.
Ответ 8
Стандартное решение курсора - зло для зла.
Два идентичных оператора FETCH NEXT - всего лишь кошмар для обслуживания.
лучше
...declare cursor etc.
While 1=1
Fetch ...
if @@FETCH_STATUS <> 0 BREAK
...
End -- While
..Close cursor etc.
Зло иногда оправдывается.
Просто попробуйте разработать подход на основе набора для отправки уведомлений с помощью sp_send_dbmail или другой хранимой процедуры.