2013-12-19 15 views
0

我正在寫一個存儲過程在SQL中,我目前處於有3個嵌套遊標(這不是我運行相當多我想)的位置。我欣賞我如何能擺脫光標的任何建議是溶液陷阱在SP中使用SQL遊標的不同方法?

我基本上有2個表(在SP期間都臨時通過從應用程序SELECT語句填充)

1 - 列表表,列和數據類型的

Table1 | SURNAME | VARCHAR 
     | SEX  | VARCHAR 
     | DOB  | DATETIME 
------------------------------ 
Table2 | ADDRESS | VARCHAR 
------------------------------ 
Table3 | SALARY | INT 
------------------------------ 
Table4 | USERNAME | VARCHAR 
     | PASSWORD | VARCHAR 

2 - 用戶號碼的一個列列表

我的應用程序必須通過每個用戶號碼循環,然後爲每個用戶我必須通過不同的表圈(表1 ,表2,表3,表le4)並查看該用戶是否有該表中的任何數據(使用dyanmic SQL,因此我可以將表名作爲參數傳遞)。如果有,然後我必須循環遍歷與該表相關的每列,並構建動態SQL INSERT語句

因此對於用戶編號2 ... 表1中的任何數據?否 - 跳過 表2中的任何數據?是 - 將行復制到臨時表中,然後爲ADDRESS值構建動態SQL 表3中的任何數據?否 - 跳過 表4中的任何數據?是 - 將行復制到臨時表中,然後爲USERNAME和PASSWORD列構建動態SQL

從功能的角度來看,光標方法可以正常工作,但性能有點慢。我已經確保2個源表儘可能緊密,當創建我的動態SQL時,我只將相關行復制到臨時表中進行處理,我已經制作了光標FAST_FORWARD和READ_ONLY

有什麼方法我可以取而代之?

原代碼張貼的要求:

IF OBJECT_ID('tempdb..##MembersToDelete') IS NOT NULL DROP TABLE ##MembersToDelete 
IF OBJECT_ID('tempdb..##TempDataTable') IS NOT NULL DROP TABLE ##TempDataTable 
DELETE FROM @CompSetTable 
DELETE FROM DataRefreshDeletes 
DELETE FROM DataRefreshInserts 


--BUILD THE INDIVIDUAL SELECT STATEMENTS INTO A TEMP TABLE 
SET @RowPosition = 1; 
SET @inSelectFilter = @inSelectFilter + ';'; 
SET @inSelectFilter = REPLACE(@inSelectFilter,'/',''''); 
WHILE LEN(@inSelectFilter) > 0 
BEGIN 
    IF PATINDEX('%;%',@inSelectFilter) > 0 
    BEGIN 
     SET @SelectParameter = SUBSTRING(@inSelectFilter, 0, PATINDEX('%;%',@inSelectFilter)) 
     SET @inSelectFilter = SUBSTRING(@inSelectFilter, LEN(@SelectParameter + ';') + 1,LEN(@inSelectFilter)) 
     IF @RowPosition = 1 
      BEGIN 
      INSERT INTO @SelectParameterTable VALUES ('WHERE ' + @SelectParameter) 
      END 
     ELSE 
      BEGIN 
      INSERT INTO @SelectParameterTable VALUES (' AND ' + @SelectParameter) 
      END 
     SET @RowPosition = @RowPosition + 1   
    END 
    ELSE 
    BEGIN 
     SET @SelectParameter = @inSelectFilter 
     SET @inSelectFilter = NULL 
    END 
END 

--BUILD THE COMPLETE DELETE STATEMENT 
SET @SelectParameter = NULL; 
SELECT @SelectParameter = COALESCE(@SelectParameter, '') + statementString FROM @SelectParameterTable 

--INSERT THE MEMBER NUMBERS INTO THE TEMP TABLE 
IF OBJECT_ID('tempdb..##MembersToDelete') IS NOT NULL DROP TABLE ##MembersToDelete 
SET @SelectParameter = 'SELECT MEMBNO INTO ##MembersToDelete FROM BASIC ' + @SelectParameter 
BEGIN TRY 
    EXECUTE sp_executesql @SelectParameter 
END TRY 
BEGIN CATCH 
    Print 'The following statement could not be run - please check the syntax...' 
    Print @SelectParameter 
    GOTO cleanUpAndFinish 
END CATCH 
SELECT @MembersToDeleteCount = COUNT(*) FROM ##MembersToDelete 

Print '##MembersToDelete TABLE BUILT - ' + CONVERT(VARCHAR(26), GETDATE(), 109) 

--BUILD LIST OF COMPENDIA TABLES (ORDERED BY DSET) 
DELETE FROM @CompSetTable 
INSERT INTO @CompSetTable SELECT d.DSNAME, c.column_name, d.DSET, c.data_type FROM DICTIONARY d, INFORMATION_SCHEMA.COLUMNS c WHERE DNUM = -1 AND DSET < 250 AND c.table_name = d.DSNAME ORDER BY d.DSET 

Print '@CompSetTable TABLE BUILT - ' + CONVERT(VARCHAR(26), GETDATE(), 109) 
DECLARE setInsertCursor CURSOR GLOBAL FAST_FORWARD READ_ONLY FOR SELECT DISTINCT setName, setNumber FROM @CompSetTable ORDER BY setNumber 

--WE NOW HAVE THE LIST OF MEMBER NUMBERS AND THE LIST OF TABLES TO BUILD THE DELETE STATEMENT 
SELECT @MemberNumberString = COALESCE(@MemberNumberString + ', ', '') + LTRIM(STR(MEMBNO)) FROM ##MembersToDelete 

DECLARE setDeleteCursor CURSOR READ_ONLY SCROLL FOR SELECT DISTINCT setName, setNumber FROM @CompSetTable ORDER BY setNumber 
OPEN setDeleteCursor 
FETCH LAST FROM setDeleteCursor INTO @SetName, @SetNumber 
    WHILE @@FETCH_STATUS = 0 
     BEGIN 
     INSERT INTO @DeleteStatementTable VALUES ('DELETE FROM ' + UPPER(@SetName) + ' WHERE MEMBNO IN (' + @MemberNumberString + ')') 
     FETCH PRIOR FROM setDeleteCursor INTO @SetName, @SetNumber 
     END 
CLOSE setDeleteCursor 
DEALLOCATE setDeleteCursor 

Print '@DeleteStatementTable TABLE BUILT - ' + CONVERT(VARCHAR(26), GETDATE(), 109) 

DECLARE memberInsertCursor CURSOR FOR SELECT MEMBNO FROM ##MembersToDelete 
OPEN memberInsertCursor 
FETCH NEXT FROM memberInsertCursor INTO @MemberNumber 
WHILE @@FETCH_STATUS = 0 
BEGIN 
    --NOW BUILD THE INSERT STATEMENTS 
    OPEN setInsertCursor 
    FETCH NEXT FROM setInsertCursor INTO @SetName, @SetNumber 
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
      --CHECK IF MEMBER HAS ANY ROWS IN THIS SET - IF NOT, SKIP 
      SET @ROWCOUNT = 0 
      SELECT @COUNTSQL = N'SELECT @countOUT = COUNT(*) FROM ' + @SetName + ' WHERE MEMBNO = ' + LTRIM(STR(@MemberNumber)) 
      EXEC sp_executesql @COUNTSQL, N'@countOUT INT OUTPUT', @[email protected] OUTPUT; 
      IF @ROWCOUNT = 0 
       BEGIN 
           GOTO nextSet 
       END 

           SET @VALUES = NULL;  

      --DROP TEMPORARY TABLE 
      IF OBJECT_ID('tempdb..##TempDataTable') IS NOT NULL DROP TABLE ##TempDataTable 

      --POPULATE TEMPORARY TABLE 
      SET @SQL = 'SELECT * INTO ##TempDataTable FROM ' + @SetName + ' WHERE MEMBNO = ' + LTRIM(STR(@MemberNumber)) 
      EXECUTE sp_executesql @SQL 

      --BUILD SELECT STATEMENT 
      SET @INSERTSTRING = NULL 
      SET @INSERTSTRING = CAST('' as nVarChar(MAX)) + 'SELECT ''INSERT INTO ' + @SetName + ' VALUES (''' 

      DECLARE setColumnCursor CURSOR FAST_FORWARD READ_ONLY FOR SELECT columnName, dataType FROM @CompSetTable WHERE setName = @SetName 
      OPEN setColumnCursor 
      FETCH NEXT FROM setColumnCursor INTO @ColumnName, @DataType 
      WHILE @@FETCH_STATUS = 0 
      BEGIN  

       IF @DataType IN ('text','varchar','nvarchar','ntext','char') 
       BEGIN 
        SET @INSERTSTRING = CAST('' as nVarChar(MAX)) + @INSERTSTRING + ''''' + ISNULL(' + @ColumnName + ',''NULL'') + '''''',''' 
       END 
       ELSE IF @DataType IN ('int','decimal','smallint','numeric','tinyint','bigint','float') 
       BEGIN 
        --SET @INSERTSTRING = @INSERTSTRING + ' + ' + @COLUMNNAMENULL + ' + '',''' 
        SET @INSERTSTRING = CAST('' as nVarChar(MAX)) + @INSERTSTRING + ' + ISNULL(CONVERT(VARCHAR(MAX),' + @ColumnName + '),''NULL'')' + ' + '',''' 
       END 
       ELSE IF @DataType IN ('datetime') 
       BEGIN 
        --SET @INSERTSTRING = @INSERTSTRING + ' + ' + @COLUMNNAMENULL + ' + '',''' 
        SET @INSERTSTRING = CAST('' as nVarChar(MAX)) + @INSERTSTRING + ''''' + ISNULL(CONVERT(VARCHAR(MAX),' + @ColumnName + '),''NULL'')' + ' + '''''',''' 
       END 

       FETCH NEXT FROM setColumnCursor INTO @ColumnName, @DataType 
      END 
      CLOSE setColumnCursor 
      DEALLOCATE setColumnCursor  

      SET @INSERTSTRING = @INSERTSTRING + '+'')''' 
      SET @INSERTSTRING = @INSERTSTRING + ' FROM ##TempDataTable' 

      INSERT INTO @InsertStatementTable EXECUTE sp_executesql @INSERTSTRING 

     nextSet: 
      FETCH NEXT FROM setInsertCursor INTO @SetName, @SetNumber 
      END 

FETCH NEXT FROM memberInsertCursor INTO @MemberNumber 
END 
CLOSE memberInsertCursor 
DEALLOCATE memberInsertCursor 

CLOSE setInsertCursor 
DEALLOCATE setInsertCursor 
+1

如果您發佈現有代碼 - 即現有的存儲過程,可能會更容易理解您在做什麼 –

+1

我認爲,您的每個表都缺少用戶編號列。否則,你如何關聯你的表格? –

+0

@TimCoker - 沒有關聯 - 它是「對於用戶表中的每一行,使用表格中的每一行執行一個操作」。因此,如果我在用戶表中有20行,在表中有10行,我將循環20次,對於每個循環,循環10次。 – Mike

回答

1

也許我沒有正確地按照你的問題,但什麼問題,使用「插入...選擇」,其中選擇加入所有這些表?

+0

Thanks @Leo - 我正在構建動態SQL以插入表(我插入實際的動態SQL字符串而不是結果) – Mike

+0

有什麼區別?這是相同的方法,相同的原則 – Leo

1

這聽起來像你正試圖執行優先順序。我質疑你是否真的需要原始表格。如果沒有,只是用not exists

insert into . . . 
    select . . . 
    from Table1; 

insert into . . . 
    select . . . 
    from Table2 
    where not exists (select 1 
         from Table1 
         where Table1.userid = Table2.userid and 
          Table1.colname = Table2.colname 
        ); 

insert into . . . 
    select . . . 
    from Table3 
    where not exists (select 1 
         from Table1 
         where Table1.userid = Table3.userid and 
          Table1.colname = Table3.colname 
        ) and 
      not exists (select 1 
         from Table2 
         where Table2.userid = Table3.userid and 
          Table2.colname = Table3.colname 
        ); 

如果你實際上是有條件地選擇每個表中的列,那麼你可能要構建上述動態SQL。

0

如果您在每個表格中添加ID列並對其進行索引,則可以使選擇速度更快。爲ID列提供唯一索引,就是這樣。你可以提高整個動作的速度。