У меня есть указатель в моей базе данных 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 вставки. Это может сработать. Если это сработает, я отредактирую и опубликую это как решение.
Что такое код в процедурах 'TowerData_guess_ * '? – RBarryYoung
Это очень простой оператор INSERT, который использует разобранные значения, входящие в состав INSERT. Мы не ожидаем 100% успешных вставок. Следовательно, причина, по которой курсор продолжает стрелять, пока он не достигнет указанного пользователем количества результатов. – RockiesMagicNumber
Тогда, честно говоря, я удивлен, что 19 секунд считается приемлемым. Все, что больше, чем часть секунды для вставки 8000 строк, будет считаться очень медленным в большинстве сред. Если вы опубликуете код 'INSERT' proc и объясните, почему вы ожидаете, что он будет иногда терпеть неудачу, я уверен, что мы сможем заставить его работать лучше. – RBarryYoung