是否可以从此动态查询中删除光标?

时间:2016-05-25 10:20:20

标签: sql sql-server tsql

我所拥有的简化示例:

两个表(table_1和table_2),它们具有一个相似的列(Id),但也有几个具有不同名称的“有效负载”列(col_1_1,col_2_1,col_2_2)。对于不同的表,“有效负载”列的数量是不同的。

我感兴趣的是将两个表中的ID提取到另一个表中,以查找所有“有效负载”列为空的行。

可以使用的所有表的所有“有效负载”列的列表(#temp)

这是用光标完成的:

CREATE TABLE #temp (tab nvarchar(20) not null, col nvarchar(20) not null)
INSERT INTO #temp SELECT 'table_1','col_1_1' UNION SELECT 'table_2','col_2_1' UNION SELECT 'table_2','col_2_2'

DECLARE @table_name nvarchar(20)
DECLARE @sql nvarchar(max)

DECLARE curs CURSOR FOR (SELECT DISTINCT tab FROM #temp)
OPEN curs
FETCH NEXT FROM curs INTO @table_name
WHILE @@FETCH_STATUS = 0
BEGIN
    SELECT @sql = ISNULL(@sql,'')+col+' IS NULL AND ' FROM #temp WHERE tab = @table_name
    SET @sql += 'Id IS NOT NULL'
    SET @sql = 'INSERT INTO #temp_master SELECT ID FROM '+@table_name+' WHERE '+@sql
    print @sql
    SET @sql = ''
    FETCH NEXT FROM curs INTO @table_name
END
CLOSE curs
DEALLOCATE curs

结果如下:

INSERT INTO #temp_master SELECT ID FROM table_1 WHERE col_1_1 IS NULL AND Id IS NOT NULL
INSERT INTO #temp_master SELECT ID FROM table_2 WHERE col_2_1 IS NULL AND col_2_2 IS NULL AND Id IS NOT NULL

是否可以删除光标以获得相同的结果动态查询?问题是当我移除光标时,我无法为不同的表创建动态“IS NULL AND”部分。

2 个答案:

答案 0 :(得分:2)

可以摆脱那个光标。这可能就是你所需要的:

CREATE TABLE #temp (tab nvarchar(20) not null, col nvarchar(20) not null)
INSERT INTO #temp SELECT 'table_1','col_1_1' UNION SELECT 'table_2','col_2_1' UNION SELECT 'table_2','col_2_2'

DECLARE @table_name nvarchar(20)
DECLARE @sql nvarchar(max) = ''

select @sql = 'INSERT INTO #temp_master SELECT ID FROM ' + t.tab + ' WHERE Id IS NOT NULL AND ' + substring(t.cols, 0, len(t.cols)-3) + '
' + @sql from
(
SELECT
distinct
    t2.tab,
    stuff(
    (
    select t1.col + cast(' IS NULL AND ' as varchar(max))
    from #temp t1
    WHERE t1.tab = t2.tab
    order by t1.tab
    for xml path('')
    ), 1, 0, '') AS cols
FROM
    #temp t2
    ) as t
order by t.tab desc

print @sql 

drop table #temp

答案 1 :(得分:1)

这是一个常规的CONCAT问题,你可以找到很多方法来完成它而不用光标。其中一种方法是光标,对于这样的任务来说并不坏。

另一个也更受欢迎 - FOR XML可以保证行顺序(如果有的话):

DECLARE @sql VARCHAR(MAX)

CREATE TABLE #temp (tab nvarchar(20) not null, col nvarchar(20) not null)
INSERT INTO #temp SELECT 'table_1','col_1_1' UNION SELECT 'table_2','col_2_1' UNION SELECT 'table_2','col_2_2'

SET @sql = (SELECT (  
SELECT '
INSERT INTO #temp_master (ID) SELECT t.ID FROM '+t.tab +' t WHERE t.Id IS NOT NULL'
    + (select ' AND t.' + tt.col + ' is NULL' from #temp tt WHERE tt.tab = t.tab FOR XML PATH(''), TYPE).value('.', 'varchar(max)')
FROM #temp t
GROUP BY t.tab
FOR XML PATH(''), TYPE).value('.', 'varchar(max)'))

PRINT @sql

DROP TABLE #temp

一点点"棘手"事情是你有两件事需要解决:

  • 许多查询(针对特定表格分开)
  • 每张桌子上有很多列

所以你有一个内部FOR XML来折叠每个表的列和另一个 - 将所有查询组合成一个大的脚本。