SQL 使用 Table 的游标和临时 Table 中的字段名称

SQL Cursor to use Table and Field Names from Temp Table

首先我要让大家知道我几年前向自己保证永远不会在 SQL 不需要的地方使用游标。不幸的是,我认为在我目前的情况下我可能不得不使用一个,但它已经太久了,以至于我很难记住正确的语法。

基本上,我在查询中遇到了 CONVERT_IMPLICIT 的问题,因为我在不同 table 中的同一字段的数据类型不同,所以我想最终转换这些 int。但是要做到这一点我需要检查是否所有数据都可以转换为 int 以查看工作量有多大。

下面的查询为我提供了数据库中包含相关字段的所有 table 的列表;

IF OBJECT_ID('tempdb..#BaseData') IS NOT NULL DROP TABLE #BaseData
GO
CREATE TABLE #BaseData (Table_Name varchar(100), Field_Name varchar(100), Data_Type_Desc varchar(20), Data_Max_Length int, Convertible bit)

DECLARE @FieldName varchar(20); SET @FieldName = 'TestFieldName'

INSERT INTO #BaseData (Table_Name, Field_Name, Data_Type_Desc, Data_Max_Length)
SELECT
o.name ,c.name ,t.name ,t.max_length 
FROM sys.columns c
JOIN sys.types t
    ON c.user_type_id = t.user_type_id
JOIN sys.objects o
    ON c.object_id = o.object_id
WHERE c.name LIKE '%' + @FieldName + '%'
    AND o.type_desc = 'USER_TABLE'

给出这样的结果;

Table_Name  Field_Name      Data_Type_Desc  Data_Max_Length Convertible
Table1      TestFieldName   varchar         8000            NULL
Table2      TestFieldName   nvarchar        8000            NULL
Table3      TestFieldName   int             4               NULL
Table4      TestFieldName   varchar         8000            NULL
Table5      TestFieldName   varchar         8000            NULL

我想做的是检查相关 table & 字段中的所有数据是否可以转换为 int 并更新 'convertible' 字段(如果有数据可以转换为 1 ' 被转换,如果数据正常则为 0)。我进行了以下计算,效果非常好;

'SELECT  
CASE 
    WHEN COUNT(' + @FieldName + ') - SUM(ISNUMERIC(' + @FieldName + ')) > 0 
        THEN 1 
    ELSE 0 
END 
FROM ' + @TableName

并给出我想要的结果。但我正在努力寻找正确的语法来创建游标,该游标将相应地查看我的临时 table 和 运行 中的每一行,此 SQL。然后它需要用查询结果(1 或 0)更新临时 table 的最后一列。

在数百个数据库中,这必须是 运行,这就是为什么我需要这个列表是动态的,在某些数据库中可能会有自定义的 table(事实上,很有可能)。

如果有人能提供任何指导,我们将不胜感激。

谢谢

如果我理解你的问题,我会做这样的事情来识别那些没有转换为 [int] 的记录。

您没有说明您使用的 SQL 服务器版本; TRY_CAST 和 TRY_CONVERT 是 2012 年或以后。

DECLARE @test AS TABLE ( [field] [sysname] );

插入@test ( [场地] ) 值(N'1'), ( N'a' );

SELECT [字段] 来自@test WHERE TRY_CAST([field] AS [INT]) 为空;

-- 这是游标的基本 sql 语法 光标 (https://msdn.microsoft.com/en-us/library/ms180169.aspx)

  DECLARE @parameter [sysname];
  BEGIN
        DECLARE [field_cursor] CURSOR
        FOR
                SELECT  [value]
                FROM    [<schema>].[<table>];

        OPEN [field_cursor];

        FETCH NEXT FROM [field_cursor] INTO @parameter;

        WHILE @@FETCH_STATUS = 0
              BEGIN
                    -- do something really interesting here

                    FETCH NEXT FROM [field_cursor] INTO @parameter;
              END;

        CLOSE [field_cursor];

        DEALLOCATE [field_cursor];
  END;

我无法对此进行测试,但它应该可以满足您的需求。创建临时文件后,只需将其放入 table:

DECLARE @tName VARCHAR(20),
        @fName VARCHAR(20),
        @dType VARCHAR(20),
        @dLength INT,
        @sql VARCHAR(MAX);

DECLARE c CURSOR LOCAL FAST_FORWARD FOR
    SELECT Table_Name,
           Field_Name,
           Data_Type_Desc,
           Data_Max_Length
    FROM #BaseData;
OPEN c;
FETCH NEXT FROM c INTO @tName, @fName, @dType, @dLength;

WHILE @@FETCH_STATUS = 0
BEGIN
    IF((COUNT(@FieldName) - SUM(ISNUMERIC(@FieldName))) > 0)
    BEGIN
        SET @sql = 'UPDATE ' + @tName + ' SET Convertible = 1 WHERE Table_Name = ''' + @tName + '''';
    END
    ELSE
    BEGIN
        SET @sql = 'UPDATE ' + @tName + ' SET Convertible = 0 WHERE Table_Name = ''' + @tName + '''';
    END

    EXEC(@sql); 

    FETCH NEXT FROM c INTO @TableName, @FieldName, @DataType, @DataLength;
END

CLOSE c;
DEALLOCATE c;

我对您的原始查询进行了一些更改,但这里应该有效。我过去做过类似的事情:-)

变化:

  • 向源添加了模式table - 我的测试数据库在多个模式中有匹配项
  • 将数据类型更改为 sysname、smallint 以匹配 table 定义或名称可能会被截断

    IF OBJECT_ID('tempdb..#BaseData') IS NOT NULL DROP TABLE #BaseData;
    GO
    
    CREATE TABLE #BaseData (Schema_Name sysname, Table_Name sysname, Field_Name sysname, Data_Type_Desc sysname, Data_Max_Length smallint, Convertible bit);
    
    DECLARE @FieldName varchar(20); SET @FieldName = 'TestFieldName';
    
    INSERT INTO #BaseData (Schema_Name, Table_Name, Field_Name, Data_Type_Desc, Data_Max_Length)
    SELECT
    s.name, o.name ,c.name ,t.name ,t.max_length 
    FROM sys.columns c
    JOIN sys.types t
        ON c.user_type_id = t.user_type_id
    JOIN sys.objects o
        ON c.object_id = o.object_id
    JOIN sys.schemas s ON o.schema_id=s.schema_id
    WHERE c.name LIKE '%' + @FieldName + '%'
        AND o.type_desc = 'USER_TABLE';
    
    --select * from #BaseData;
    
    DECLARE @sName sysname,
            @tName sysname,
            @fName sysname,
            @sql VARCHAR(MAX);
    
    DECLARE c CURSOR LOCAL FAST_FORWARD FOR
        SELECT  Schema_Name,
                Table_Name,
                Field_Name
        FROM #BaseData;
    OPEN c;
    FETCH NEXT FROM c INTO @sName, @tName, @fName;
    
    WHILE @@FETCH_STATUS = 0
    BEGIN
    SET @sql = 'UPDATE #BaseData SET Convertible =   
                    (SELECT 
                    CASE 
                        WHEN COUNT(' + @fName + ') - SUM(ISNUMERIC(' + @fName + ')) > 0 
                            THEN 1 
                        ELSE 0
                    END Convertible
                    FROM ' + @sName + '.' + @tName + ')
                FROM #BaseData WHERE Schema_Name = ''' + @sName + ''' AND Table_Name = ''' + @tName + ''' AND Field_Name = ''' + @fName + '''';
    
    --select @sql;
    EXEC(@sql); 
    
    FETCH NEXT FROM c INTO @sName, @tName, @fName;
    END
    
    CLOSE c;
    DEALLOCATE c;
    
    select *
    from #BaseData;