Возможно ли выполнить хранимую процедуру по набору без использования курсора?

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

SELECT EXEC dbo.Sproc @Param1 = Table1.id
FROM Table1


Я использую T-SQL в SQL Server 2005. Я думаю, что это возможно с помощью функции, но я хотел бы использовать хранимую процедуру, если это возможно (стандарты компании)

Ответы

Ответ 1

9 из 10 раз вы можете делать то, что хотите, без курсора или цикла while. Однако, если вы должны использовать один, я обнаружил, что в то время как циклы, как правило, быстрее.

Также, если вы не хотите удалять или обновлять таблицу, вы можете использовать что-то вроде этого:

DECLARE @id [type]
SELECT @id = MIN([id]) FROM [table]
WHILE @id IS NOT NULL
BEGIN
    EXEC [sproc] @id
    SELECT @id = MIN([id]) FROM [table] WHERE [id] > @id
END

Ответ 2

Да. Если у вас есть столбец, который вы можете использовать в таблице, чтобы отметить обработанные, вы можете использовать WHILE EXISTS:

DECLARE @Id int
WHILE EXISTS(SELECT * FROM Table1 WHERE Processed='N')
BEGIN
 SELECT Top 1 @Id = id from Table1 WHERE Procesed='N'
 EXEC dbo.Sproc @Id
 UPDATE Table1 SET Processed = 'Y' WHERE Id = @Id
END

В качестве альтернативы, выведите идентификаторы в таблицу temp или табличную переменную и удалите по окончании:

DECLARE @HoldTable table (Id int PRIMARY KEY)
DECLARE @Id int
INSERT INTO @HoldTable SELECT Id FROM Table1
WHILE EXISTS(SELECT * FROM @HoldTable)
BEGIN
 SELECT @Id = id from @HoldTable
 EXEC dbo.Sproc @Id
 DELETE FROM @HoldTable where Id = @Id
END

Ответ 3

Если вы используете только SQL Server 2005 или новее, не заботитесь о обратной совместимости и можете конвертировать ваш код в определенную пользователем функцию (а не в хранимую процедуру), то вы можете использовать новый "CROSS" APPLY ", который использует синтаксис, очень похожий на то, что вы хотите. Я нашел здесь краткое введение (конечно, вы также можете прочитать BOL и MSDN)

Предположим, что ваш SP возвращает одно значение с именем out_int, ваш пример можно переписать как:

SELECT T.id, UDF.out_int
FROM 
    Table1 T
CROSS APPLY
    dbo.fn_My_UDF(T.id) AS UDF

Это будет извлекать каждый "id" из Таблицы 1 и использовать его для вызова fn_My_UDF, результат которого будет отображаться в конечном результирующем наборе помимо исходного параметра.

Вариант "CROSS APPLY" - "ВНЕШНИЕ ПРИМЕНЕНИЯ". Они являются эквивалентами "INNER JOIN" и "LEFT JOIN", но работают над присоединением к таблице и UDF (и одновременным вызовом второго).

Если вы должны (по явному порядку заостренного босса), используйте SPs insead, ну - неудача! Вам придется следить за курсорами или пытаться немного обмануть: изменить код на UDF и создать пакеты SP: D.

Ответ 4

Честно говоря, если мне нужно было выполнить сохраненный proc для набора данных, вместо того, чтобы записать одну запись, я бы написал код на основе набора вместо использования сохраненного proc. Это единственный эффективный способ сделать это, если вы собираетесь работать с большим набором данных. Вы можете перейти от часов, чтобы сделать вставку, которая хранится в секундах или даже миллисекундах. Не используйте обработку на основе записей, когда вам нужно обрабатывать набор данных, особенно не для глупой причины, как повторное использование кода. Эффективность козырей повторного использования кода.

Ответ 5

Если возможно, я бы написал вторую версию хранимого proc, которая читается из таблицы temp.

Если это невозможно, возможно, вам не повезло.

Ответ 6

Посмотрите на этот ответ, используя динамический SQL. Я собираюсь переписать запрос, чтобы удовлетворить ваши потребности.

DECLARE @TSQL NVARCHAR(MAX) = ''
SELECT @TSQL = @TSQL + N'EXEC dbo.Sproc ' + id + '; 
' -- To insert a line break
FROM Table1
EXEC sp_executesql @TSQL

Это равнозначно следующему, если вы PRINT @TSQL. Обратите внимание, как вставляется разрыв строки.

EXEC dbo.Sproc id1; 
EXEC dbo.Sproc id2; 
EXEC dbo.Sproc id3;
...

Ответ 7

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

DECLARE @id INT
SET @id = 0

DECLARE @max INT
SELECT TOP 1 @max = TableID
FROM dbo.Table
ORDER BY TableID desc

-- loop until BREAK
-- this is how you can perform a DO-WHILE loop in TSQL
WHILE (1 = 1) 
BEGIN
    SELECT      
        TOP 1 @id = TableID
        FROM dbo.Table
        WHERE TableID > @id 

    IF @id IS NOT NULL
    BEGIN        
        -- call you SP here
        PRINT @id        
    END

    -- break out of the loop once the max id has been reached
    IF @id = @max BREAK 
END