Хранимая процедура вызова SQL для каждой строки без использования курсора - PullRequest
142 голосов
/ 01 ноября 2009

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

Ответы [ 16 ]

1 голос
/ 21 марта 2011

Обычно я делаю это так, когда довольно много строк:

  1. Выбор всех параметров sproc в наборе данных с помощью SQL Management Studio
  2. Щелкните правой кнопкой мыши -> Копировать
  3. Вставить в Excel
  4. Создайте однострочные SQL-операторы с формулой вроде '= "EXEC schema.mysproc @ param =" & A2' в новом столбце Excel. (Где A2 - ваш столбец Excel, содержащий параметр)
  5. Скопируйте список операторов Excel в новый запрос в SQL Management Studio и выполните.
  6. Готово.

(Для больших наборов данных я бы использовал одно из упомянутых выше решений).

0 голосов
/ 20 декабря 2016

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

ALTER procedure GetEmployees
    @ClientId varchar(50)
as
begin
    declare @EEList table (employeeId varchar(50));
    declare @EE20 table (employeeId varchar(50));

    insert into @EEList select employeeId from Employee where (ClientId = @ClientId);

    -- Do 20 at a time
    while (select count(*) from @EEList) > 0
    BEGIN
      insert into @EE20 select top 20 employeeId from @EEList;

      -- Call sp here

      delete @EEList where employeeId in (select employeeId from @EE20)
      delete @EE20;
    END;

  RETURN
end
0 голосов
/ 13 декабря 2016

Лучшее решение для этого -

  1. Копировать / передать код хранимой процедуры
  2. Объедините этот код с таблицей, для которой вы хотите запустить его снова (для каждой строки)

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

0 голосов
/ 14 марта 2016

В случае важности заказа

--declare counter
DECLARE     @CurrentRowNum BIGINT = 0;
--Iterate over all rows in [DataTable]
WHILE (1 = 1)
    BEGIN
        --Get next row by number of row
        SELECT TOP 1 @CurrentRowNum = extendedData.RowNum
                    --here also you can store another values
                    --for following usage
                    --@MyVariable = extendedData.Value
        FROM    (
                    SELECT 
                        data.*
                        ,ROW_NUMBER() OVER(ORDER BY (SELECT 0)) RowNum
                    FROM [DataTable] data
                ) extendedData
        WHERE extendedData.RowNum > @CurrentRowNum
        ORDER BY extendedData.RowNum

        --Exit loop if no more rows
        IF @@ROWCOUNT = 0 BREAK;

        --call your sproc
        --EXEC dbo.YOURSPROC @MyVariable
    END
0 голосов
/ 11 марта 2016

Это вариант уже предоставленных ответов, но он должен быть более эффективным, поскольку не требует ORDER BY, COUNT или MIN / MAX. Единственный недостаток этого подхода заключается в том, что вам необходимо создать временную таблицу для хранения всех идентификаторов (предполагается, что в вашем списке идентификаторов клиентов есть пробелы).

Тем не менее, я согласен с @Mark Powell, хотя, вообще говоря, подход, основанный на множествах, все же должен быть лучше.

DECLARE @tmp table (Id INT IDENTITY(1,1) PRIMARY KEY NOT NULL, CustomerID INT NOT NULL)
DECLARE @CustomerId INT 
DECLARE @Id INT = 0

INSERT INTO @tmp SELECT CustomerId FROM Sales.Customer

WHILE (1=1)
BEGIN
    SELECT @CustomerId = CustomerId, @Id = Id
    FROM @tmp
    WHERE Id = @Id + 1

    IF @@rowcount = 0 BREAK;

    -- call your sproc
    EXEC dbo.YOURSPROC @CustomerId;
END
0 голосов
/ 24 апреля 2013

Мне нравится делать что-то похожее на это (хотя это все еще очень похоже на использование курсора)

[код]

-- Table variable to hold list of things that need looping
DECLARE @holdStuff TABLE ( 
    id INT IDENTITY(1,1) , 
    isIterated BIT DEFAULT 0 , 
    someInt INT ,
    someBool BIT ,
    otherStuff VARCHAR(200)
)

-- Populate your @holdStuff with... stuff
INSERT INTO @holdStuff ( 
    someInt ,
    someBool ,
    otherStuff
)
SELECT  
    1 , -- someInt - int
    1 , -- someBool - bit
    'I like turtles'  -- otherStuff - varchar(200)
UNION ALL
SELECT  
    42 , -- someInt - int
    0 , -- someBool - bit
    'something profound'  -- otherStuff - varchar(200)

-- Loop tracking variables
DECLARE @tableCount INT
SET     @tableCount = (SELECT COUNT(1) FROM [@holdStuff])

DECLARE @loopCount INT
SET     @loopCount = 1

-- While loop variables
DECLARE @id INT
DECLARE @someInt INT
DECLARE @someBool BIT
DECLARE @otherStuff VARCHAR(200)

-- Loop through item in @holdStuff
WHILE (@loopCount <= @tableCount)
    BEGIN

        -- Increment the loopCount variable
        SET @loopCount = @loopCount + 1

        -- Grab the top unprocessed record
        SELECT  TOP 1 
            @id = id ,
            @someInt = someInt ,
            @someBool = someBool ,
            @otherStuff = otherStuff
        FROM    @holdStuff
        WHERE   isIterated = 0

        -- Update the grabbed record to be iterated
        UPDATE  @holdAccounts
        SET     isIterated = 1
        WHERE   id = @id

        -- Execute your stored procedure
        EXEC someRandomSp @someInt, @someBool, @otherStuff

    END

[/ код]

Обратите внимание, что вам не нужно идентификатор или столбец isIterated в таблице временных / переменных, я просто предпочитаю делать это таким образом, чтобы мне не пришлось удалять верхнюю запись коллекция, как я повторяю цикл.

...