我正在用SQL编写存储过程,我目前处于拥有3个嵌套游标的位置(这不是我想要的运行方式)。我很感激有关如何摆脱光标的任何建议是解决方案陷阱
我基本上有2个表(在应用程序的SELECT语句填充的SP期间都是临时的)
1 - 表,列和数据类型列表
Table1 | SURNAME | VARCHAR
| SEX | VARCHAR
| DOB | DATETIME
------------------------------
Table2 | ADDRESS | VARCHAR
------------------------------
Table3 | SALARY | INT
------------------------------
Table4 | USERNAME | VARCHAR
| PASSWORD | VARCHAR
2 - 一列用户号码
我的应用程序必须循环遍历每个用户编号,然后对于每个用户,我必须遍历不同的表(Table1,Table2,Table3,Table4)并查看该用户的该表中是否有任何数据(使用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', @countOUT=@ROWCOUNT 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
答案 0 :(得分:1)
也许我没有正确地回答你的问题,但是使用'insert ... select'哪个select加入所有这些表有什么问题?
答案 1 :(得分: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。
答案 2 :(得分:0)
如果在每个表中添加ID列并对其进行索引,则可以更快地进行选择。为ID列提供唯一索引,就是这样。你可以大大提高整个动作的速度。