2013-06-18 25 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 

这很有效,至少按我的预期。游标在大约19秒内处理8000行。 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的SP)

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 

每RBarryYoung的建议下,我选择看实际的SP插入处理了一下。我发现的其中一个问题是,我们的许多Execs并未将域名连接成电子邮件地址。扩展脚本可以查找域信息的区域已将该过程改进为接近用户指定的完成。虽然它还不完美,但它朝着正确的方向迈出了一步。

至于寻找动态强制游标拉动特定行数的方法,我想我可能将这个特定的SP嵌套在使用WHILE循环的第二个SP中。所以基本上,当列表大小小于用户指定的列表大小时,它将重新执行插入SP。这可能会起作用。如果有效,我会重新编辑并将其作为解决方案发布。

+0

'TowerData_guess_ *'程序中的代码是什么? – RBarryYoung

+0

这是一个非常基本的INSERT语句,它使用作为INSERT的一部分提供的解析值。我们预计不会100%插入成功。因此光标继续射击直到击中用户指定数量的结果的原因。 – RockiesMagicNumber

+0

然后,坦率地说,我很惊讶,19秒被认为是可以接受的。在大多数环境中,插入8000行的任何事情都会被认为非常缓慢。如果你发布proc的'INSERT'代码并解释你为什么期望它有时会失败,我相信我们可以让它更好地运行。 – RBarryYoung

回答

0

正如该文章的最后一段中所建议的,我最终使用了嵌套的SP。最外层的SP运行一个WHILE循环,以跟踪请求的许多执行者。然后它运行具有指定数量的exec的代SP。如果返回的号码小于请求的号码,它将保留在WHILE循环中。

我们的数据团队正在记录下一代SP中的错误。