2013-06-18 3 views
1

У меня есть указатель в моей базе данных SQL 2008R2. Этот курсор принимает список данных, анализирует каждую строку данных, а затем запускает анализируемую строку данных через хранимую процедуру.Использование курсоров для возврата указанного количества результатов

DECLARE ExecsDataCursor CURSOR FAST_FORWARD FOR 
    SELECT TOP (@GuessListSize) 
     ExecutiveId, 
     CompanyExecutiveId, 
     Email, 
     CompanyId, 
     @EmailPatternID EmailPatternID, 
     ExecNameForSorting 
    FROM 
     CompanyExecutive 
    WHERE 
     CurrentlyWithCompany = 1 
     AND 
     Email IS NULL 
     AND 
     CompanyExecutiveId NOT IN 
     (
      SELECT CompanyExecutiveId 
      FROM ExecsData_ExecutiveCandidates 
      WHERE EmailPatternID = @EmailPatternID 
     ) 
    ORDER BY 
     CompanyExecutiveId 

    OPEN ExecsDataCursor 

    DECLARE 
     @ExecutiveId INT, 
     @CompanyExecutiveId INT, 
     @Email NVARCHAR(255), 
     @CompanyId INT, 
     @EmailPatternID_ForCursor TINYINT, 
     @ExecName NVARCHAR(255) 

    FETCH NEXT FROM ExecsDataCursor 
    INTO 
     @ExecutiveId , 
     @CompanyExecutiveId , 
     @Email , 
     @CompanyId , 
     @EmailPatternID_ForCursor, 
     @ExecName 

    DECLARE 
     @FirstName NVARCHAR(50) = '', 
     @MiddleName NVARCHAR(50) = '', 
     @LastName NVARCHAR(50) = '', 
     @ExampleEmail NVARCHAR(255), 
     @Domain NVARCHAR(50) = '' 

    WHILE @@FETCH_STATUS = 0 
    BEGIN 

     IF (SELECT COUNT(*) FROM dbo.splitString(@ExecName,' ')) = 1 
     BEGIN 
      SELECT @FirstName = Data FROM dbo.splitString(@ExecName,' ') WHERE id = 1 
     END 

     IF (SELECT COUNT(*) FROM dbo.splitString(@ExecName,' ')) = 2 
     BEGIN 
      SELECT @FirstName = Data FROM dbo.splitString(@ExecName,' ') WHERE id = 1 
      SELECT @LastName = Data FROM dbo.splitString(@ExecName,' ') WHERE id = 2 
     END 

     IF (SELECT COUNT(*) FROM dbo.splitString(@ExecName,' ')) >= 3 
     BEGIN 
      SELECT @FirstName = Data FROM dbo.splitString(@ExecName,' ') WHERE id = 1 
      SELECT @MiddleName = Data FROM dbo.splitString(@ExecName,' ') WHERE id = 2 
      SELECT @LastName = Data FROM dbo.splitString(@ExecName,' ') WHERE id = (SELECT MAX(id) FROM dbo.splitString(@ExecName,' ')) 
     END 

     SELECT @ExampleEmail = MAX(Email) FROM CompanyExecutive WHERE Email IS NOT NULL AND CompanyId = @CompanyId 
     SELECT @Domain = SUBSTRING(@ExampleEmail, CHARINDEX('@', @ExampleEmail), LEN(@ExampleEmail))    

     IF @EmailPatternID = 1 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID END TRY BEGIN CATCH END CATCH END 
     IF @EmailPatternID = 2 BEGIN BEGIN TRY EXEC [email protected]_DataMe @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID END TRY BEGIN CATCH END CATCH END 
     IF @EmailPatternID = 3 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID END TRY BEGIN CATCH END CATCH END 
     IF @EmailPatternID = 4 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID END TRY BEGIN CATCH END CATCH END 
     IF @EmailPatternID = 5 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID END TRY BEGIN CATCH END CATCH END 
     IF @EmailPatternID = 6 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID END TRY BEGIN CATCH END CATCH END 
     IF @EmailPatternID = 7 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID END TRY BEGIN CATCH END CATCH END 
     IF @EmailPatternID = 8 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID END TRY BEGIN CATCH END CATCH END 
     IF @EmailPatternID = 9 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID END TRY BEGIN CATCH END CATCH END 
     IF @EmailPatternID = 10 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID END TRY BEGIN CATCH END CATCH END 
     IF @EmailPatternID = 11 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID END TRY BEGIN CATCH END CATCH END 
     IF @EmailPatternID = 12 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID END TRY BEGIN CATCH END CATCH END 
     IF @EmailPatternID = 13 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID END TRY BEGIN CATCH END CATCH END 

      FETCH NEXT FROM ExecsDataCursor 
       INTO 
        @ExecutiveId , 
        @CompanyExecutiveId , 
        @Email , 
        @CompanyId , 
        @EmailPatternID_ForCursor, 
        @ExecName 
    END 
    CLOSE ExecsDataCursor 
    DEALLOCATE ExecsDataCursor 

Это работает очень хорошо, по крайней мере, по моим ожиданиям. Курсор обрабатывает 8000 строк за 19 секунд. 8000 строк задаются пользователем, который передает параметр @GuessListSize. Однако анализируемые данные не всегда обрабатываются правильно, что и следовало ожидать. Следовательно, код try-catch. Нам не нужно, чтобы курсор делал что-либо с неудачными выполнением хранимых процедур. Мы начали отслеживать их в отдельной таблице, чтобы мы могли выяснить, как лучше обрабатывать эти точки данных в будущем.

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

Итак, следующая вещь, которую я пробовал, была петля WHILE. Теперь цикл WHILE работал нормально. Он возвратил все строки, запрошенные пользователем. Тем не менее потребовалось почти 30 минут, чтобы выполнить один и тот же набор данных. Это, очевидно, неприемлемо.

DECLARE 
     @ExecutiveId INT, 
     @CompanyExecutiveId INT, 
     @Email NVARCHAR(255), 
     @CompanyId INT, 
     @EmailPatternID_ForCursor TINYINT, 
     @ExecName NVARCHAR(255) 


    DECLARE 
     @FirstName NVARCHAR(50) = '', 
     @MiddleName NVARCHAR(50) = '', 
     @LastName NVARCHAR(50) = '', 
     @ExampleEmail NVARCHAR(255), 
     @Domain NVARCHAR(50) = '', 
     @Counter SMALLINT = 0 

    --WHILE @@FETCH_STATUS = 0 
    WHILE @Counter < @GuessListSize 
    BEGIN 

     SELECT @CompanyExecutiveId = 
      MIN(CompanyExecutiveID) 
     FROM CompanyExecutive 
     WHERE CurrentlyWithCompany = 1 AND Email IS NULL 
      AND 
      CompanyExecutiveId NOT IN 
      (SELECT CompanyExecutiveId FROM ExecsData_ExecutiveCandidates WHERE EmailPatternID = @EmailPatternID) 
      AND 
      CompanyExecutiveID NOT IN 
      (SELECT CompanyExecutiveId FROM ExecsData_Errors) 

     SELECT 
      @ExecutiveId = ExecutiveId, 
      @Email = Email, 
      @CompanyId = CompanyId, 
      @EmailPatternID_ForCursor = @EmailPatternID, 
      @ExecName = ExecNameForSorting 
     FROM 
      CompanyExecutive 
     WHERE 
      CompanyExecutiveId = @CompanyExecutiveId 

     IF (SELECT COUNT(*) FROM dbo.splitString(@ExecName,' ')) = 1 
     BEGIN 
      SELECT @FirstName = Data FROM dbo.splitString(@ExecName,' ') WHERE id = 1 
     END 

     IF (SELECT COUNT(*) FROM dbo.splitString(@ExecName,' ')) = 2 
     BEGIN 
      SELECT @FirstName = Data FROM dbo.splitString(@ExecName,' ') WHERE id = 1 
      SELECT @LastName = Data FROM dbo.splitString(@ExecName,' ') WHERE id = 2 
     END 

     IF (SELECT COUNT(*) FROM dbo.splitString(@ExecName,' ')) >= 3 
     BEGIN 
      SELECT @FirstName = Data FROM dbo.splitString(@ExecName,' ') WHERE id = 1 
      SELECT @MiddleName = Data FROM dbo.splitString(@ExecName,' ') WHERE id = 2 
      SELECT @LastName = Data FROM dbo.splitString(@ExecName,' ') WHERE id = (SELECT MAX(id) FROM dbo.splitString(@ExecName,' ')) 
     END 

     SELECT @ExampleEmail = MAX(Email) FROM CompanyExecutive WHERE Email IS NOT NULL AND CompanyId = @CompanyId 
     SELECT @Domain = SUBSTRING(@ExampleEmail, CHARINDEX('@', @ExampleEmail), LEN(@ExampleEmail)) 

    IF @EmailPatternID = 1 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID; SET @Counter = @Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (@CompanyExecutiveId,@EmailPatternID) END CATCH END 
    IF @EmailPatternID = 2 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID; SET @Counter = @Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (@CompanyExecutiveId,@EmailPatternID) END CATCH END 
    IF @EmailPatternID = 3 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID; SET @Counter = @Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (@CompanyExecutiveId,@EmailPatternID) END CATCH END 
    IF @EmailPatternID = 4 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID; SET @Counter = @Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (@CompanyExecutiveId,@EmailPatternID) END CATCH END 
    IF @EmailPatternID = 5 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID; SET @Counter = @Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (@CompanyExecutiveId,@EmailPatternID) END CATCH END 
    IF @EmailPatternID = 6 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID; SET @Counter = @Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (@CompanyExecutiveId,@EmailPatternID) END CATCH END 
    IF @EmailPatternID = 7 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID; SET @Counter = @Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (@CompanyExecutiveId,@EmailPatternID) END CATCH END 
    IF @EmailPatternID = 8 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID; SET @Counter = @Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (@CompanyExecutiveId,@EmailPatternID) END CATCH END 
    IF @EmailPatternID = 9 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID; SET @Counter = @Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (@CompanyExecutiveId,@EmailPatternID) END CATCH END 
    IF @EmailPatternID = 10 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID; SET @Counter = @Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (@CompanyExecutiveId,@EmailPatternID) END CATCH END 
    IF @EmailPatternID = 11 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID; SET @Counter = @Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (@CompanyExecutiveId,@EmailPatternID) END CATCH END 
    IF @EmailPatternID = 12 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID; SET @Counter = @Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (@CompanyExecutiveId,@EmailPatternID) END CATCH END 
    IF @EmailPatternID = 13 BEGIN BEGIN TRY EXEC [email protected]_DataME @ExecutiveID ,@CompanyExecutiveID ,@FirstName,@MiddleName ,@LastName ,@Domain ,@CompanyID; SET @Counter = @Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (@CompanyExecutiveId,@EmailPatternID) END CATCH END 

    END 

Поскольку курсор предназначен жевать через заранее определенного списка данных, я не знаю, как сделать курсор сказал «динамический» и возвращает заданное пользователем количество результатов, независимо от ошибок. Цикл WHILE имеет «счетчик» только возрастает, если SP выполняется без удара блока CATCH, но я не знаю, как интегрировать его в Курсор, или если я даже могу.

Есть ли что-то очевидное, что я здесь отсутствует?

(В соответствии с просьбой INSERT SQL П) предложение

ALTER PROCEDURE [dbo].[[email protected]_DataMe] 
( 
    @ExecutiveID int, 
    @CompanyExecutiveID int, 
    @FirstName nvarchar(50), 
    @MiddleName nvarchar(50), 
    @LastName nvarchar(50), 
    @DomainName nvarchar(255), 
    @CompanyID int 
) 
AS 

SET NOCOUNT ON 

SET TRANSACTION ISOLATION LEVEL READ COMMITTED 
DECLARE @GUESS nvarchar(255) 

DECLARE @FirstInitial nvarchar(1) 
DECLARE @MiddleInitial nvarchar(1) 
DECLARE @LastInitial nvarchar(1) 

set @FirstInitial = SUBSTRING(@FirstName, 1, 1) 
set @MiddleInitial = SUBSTRING(@MiddleName, 1, 1) 
set @LastInitial = SUBSTRING(@LastName, 1, 1) 

--*****Example****** 
--FirstName = Andy, 
--Middle Name = Xanadu, 
--Last Name = Farag, 
--Domain = @umphreys.com 
--****************** 

--ex. [email protected] 
set @GUESS = LTRIM(@FirstName)+ @DomainName 
EXEC ExecsData_InsertEmailGuessByExec_DataMe 
     @ExecutiveID, 
     @CompanyExecutiveID, 
     @GUESS, 
     @CompanyID, 
     9 


RETURN (@@ERROR) 

ALTER PROCEDURE [dbo].[ExecsData_InsertEmailGuessByExec_DataMe] 
( 
    @ExecutiveID int, 
    @CompanyExecutiveID int, 
    @EmailAddress nvarchar(50), 
    @CompanyID int, 
    @EmailPatternID tinyint 
) 
AS 

BEGIN 
    INSERT ExecsData_ExecutiveCandidates 
    (
     ExecutiveID, 
     CompanyExecutiveID, 
     EmailAddress, 
     CompanyID, 
     EmailPatternID, 
     GuessTimestamp 
    ) 

    VALUES 
    ( 
     @ExecutiveID, 
     @CompanyExecutiveID, 
     @EmailAddress, 
     @CompanyID, 
     @EmailPatternID, 
     CURRENT_TIMESTAMP 
    ) 
END 

Per RBarryYoung, я решил посмотреть на реальном процессе SP вставки немного. Одна из проблем, которые я обнаружил, заключалась в том, что многие из наших Execs не тянули домен, чтобы объединиться в адрес электронной почты. Расширение областей, в которых сценарий мог искать информацию о домене, улучшил процесс до завершения, указанного пользователем. Хотя это все еще не идеально, это шаг в правильном направлении.

Что касается поиска способов динамического принуждения курсора к вытягиванию определенного количества строк, я думаю, что я мог бы вложить этот конкретный SP во второй SP, который использует цикл WHILE. Таким образом, в основном, в то время как размер списка меньше заданного пользователем размера списка, он будет повторно выполнять SP вставки. Это может сработать. Если это сработает, я отредактирую и опубликую это как решение.

+0

Что такое код в процедурах 'TowerData_guess_ * '? – RBarryYoung

+0

Это очень простой оператор INSERT, который использует разобранные значения, входящие в состав INSERT. Мы не ожидаем 100% успешных вставок. Следовательно, причина, по которой курсор продолжает стрелять, пока он не достигнет указанного пользователем количества результатов. – RockiesMagicNumber

+0

Тогда, честно говоря, я удивлен, что 19 секунд считается приемлемым. Все, что больше, чем часть секунды для вставки 8000 строк, будет считаться очень медленным в большинстве сред. Если вы опубликуете код 'INSERT' proc и объясните, почему вы ожидаете, что он будет иногда терпеть неудачу, я уверен, что мы сможем заставить его работать лучше. – RBarryYoung

ответ

0

Как было предложено в последнем абзаце сообщения, я закончил использование вложенных SP. Outermost SP запускает цикл WHILE, который отслеживает, сколько запросов было запрошено. Затем он запускает SP поколения с указанным числом execs. Если возвращаемое число меньше запрашиваемого номера, оно остается в цикле WHILE.

Ошибки в генерации SP регистрируются для проверки нашей командой данных.

Смежные вопросы