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;
首先我要让大家知道我几年前向自己保证永远不会在 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;