所以我有一个包含许多表的数据库,这些表的列包含GL帐户值(出于财务目的)。列名称因表格而异(即在一个表中该列称为“gldebitaccount”,在另一个表中称为“glcreditaccount”)。我能够使用以下查询找到表/列对的所有组合:
SELECT c.name AS ColName, t.name AS TableName
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name LIKE '%gl%acc%'
此查询返回接近100对表/列。我试图在任何超过25个字符的表/列对中找到任何值。对于单个表/列,我通常使用:
SELECT *
FROM tableName
WHERE LEN(columnName)>25
我希望避免每对运行100次查询。有什么方法可以做“为每个人”(我知道这在SQL中是不受欢迎的,因为一切都应该基于集合)。之前我已经完成了子SELECT语句,但没有涉及更改FROM子句中的表。任何想法或帮助将不胜感激!
提前致谢!
答案 0 :(得分:4)
如前所述,该解决方案需要动态SQL。这是一种同时使用动态SQL和游标的方法,并且您可以预期性能会降低,因此使用风险自负:
DECLARE @TableName NVARCHAR(128), @ColumnName NVARCHAR(128)
DECLARE @Query NVARCHAR(4000)
DECLARE CC CURSOR LOCAL FAST_FORWARD FOR
SELECT QUOTENAME(t.name), QUOTENAME(c.name)
FROM sys.columns c
INNER JOIN sys.tables t
ON c.object_id = t.object_id
WHERE c.collation_name IS NOT NULL
AND c.max_length > 25 AND c.name LIKE '%gl%acc%';
CREATE TABLE #Results(TableName NVARCHAR(128), ColumnName NVARCHAR(128));
OPEN CC
FETCH NEXT FROM CC INTO @TableName, @ColumnName
WHILE @@FETCH_STATUS = 0
BEGIN
SET @Query = 'IF EXISTS(SELECT 1 FROM '+@TableName+'
WHERE LEN('+@ColumnName+') > 25)
INSERT INTO #Results
VALUES(@TableName,@ColumnName)'
EXEC sp_executesql @Query,
N'@TableName NVARCHAR(128),@ColumnName NVARCHAR(128)',
@TableName,
@ColumnName;
FETCH NEXT FROM CC INTO @TableName, @ColumnName
END
CLOSE CC
DEALLOCATE CC
SELECT *
FROM #Results
答案 1 :(得分:4)
这是一个没有游标的选项,也不会增加XML开销。请注意,它还可以保护您免受潜在的类型冲突(例如,尝试使用hierarchyid列的数据库中的其他数据库,例如AdventureWorks),带有撇号的表名或列名,以及存在于多个模式中的表名。
DECLARE @sql NVARCHAR(MAX) = N'';
CREATE TABLE #Results
(
SchemaName NVARCHAR(128), TableName NVARCHAR(128), ColumnName NVARCHAR(128)
);
SELECT @sql += N'INSERT #Results SELECT '''
+ REPLACE(s.name,'''','''''') + ''','''
+ REPLACE(t.name,'''','''''') + ''','''
+ REPLACE(c.name,'''','''''') + '''
WHERE EXISTS (SELECT 1 FROM ' + QUOTENAME(s.name)
+ '.' + QUOTENAME(t.name) + ' WHERE
LEN(' + QUOTENAME(c.name) + ') > 25);
'
FROM sys.columns AS c
INNER JOIN sys.tables AS t
ON c.[object_id] = t.[object_id]
INNER JOIN sys.schemas AS s
ON t.[schema_id] = s.[schema_id]
WHERE
(
c.system_type_id IN (35,99) -- text,ntext
OR (c.system_type_id IN (167,231) -- varchar,nvarchar, could be max
AND c.max_length > 25 OR c.max_length = -1)
OR (c.system_type_id IN (175,239) -- char, nchar
AND c.max_length > 25)
)
AND c.name LIKE N'%gl%acc%';
EXEC sp_executesql @sql;
SELECT SchemaName, TableName, ColumnName FROM #Results;
答案 2 :(得分:1)
您需要创建动态SQL,因为您无法动态指定源表。您可以使用游标执行此操作,或者编写一个select语句,为您需要运行的每个语句创建一行。这显示了如何使用游标执行此操作。您的问题看起来像是游标的可接受用法:
DECLARE @ColName VARCHAR(MAX);
DECLARE @TableName VARCHAR(MAX);
DECLARE @SomeSQL VARCHAR(MAX);
DECLARE db_cursor CURSOR FOR
SELECT c.name AS ColName, t.name AS TableName
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name LIKE '%gl%acc%'
OPEN db_cursor;
FETCH NEXT FROM db_cursor INTO @ColName, @TableName;
WHILE @@FETCH_STATUS = 0
BEGIN
-- you need to make dynamic SQL
SELECT @SomeSQL = 'SELECT * FROM ' + @TableName + ' WHERE LEN(' + @ColName + ') > 25;'
PRINT(@SomeSQL + CHAR(10));
-- you could execute it directly if you wish.
--EXEC (@SomeSQL);
FETCH NEXT FROM db_cursor INTO @ColName, @TableName;
END
CLOSE db_cursor;
DEALLOCATE db_cursor;
答案 3 :(得分:1)
另一种动态SQL解决方案。
但现在没有游标。它使用FOR XML语句,应该更快。
DECLARE @sqlstatement VARCHAR(MAX);
SET @sqlstatement =
REPLACE (
STUFF ( (
SELECT 'UNION ALL SELECT ''' + t.name + ''' as TableName, '''
+ c.name + ''' AS ColumnName, '
+ c.name + ' AS Value FROM '
+ t.name + ' WHERE LEN (' + c.name + ') ' + CHAR(62) + ' 25'
FROM sys.columns c
INNER JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name LIKE '%gl%acc%'
FOR XML PATH('')
), 1, 10, '')
, '>', '>')
EXEC (@sqlstatement)
您可能希望按类型和max_length为列添加额外的过滤器:
INNER JOIN sys.types ty ON c.system_type_id = ty.system_type_id
AND (
ty.name IN ('text', 'ntext')
OR (
ty.name IN ('varchar', 'char', 'nvarchar', 'nchar')
AND (c.max_length > 25 OR c.max_length = -1)
)
答案 4 :(得分:1)
我不确定您是否需要对结果做任何事情,但这会返回符合您在问题中发布的条件的记录
Declare @TableName sysname
Declare @ColName sysname
Declare @dynamic_SQL varchar(MAX)
Declare some_cursor CURSOR FOR
SELECT c.name AS ColName, t.name AS TableName
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
WHERE c.name LIKE '%gl%acc%'
OPEN some_cursor
FETCH NEXT FROM some_cursor INTO @ColName, @TableName
WHILE @@FETCH_STATUS = 0
Begin
select @dynamic_SQL = '
Select *
From ' + @TableName + '
Where LEN('+ @ColName +') > 25
'
exec (@dynamic_SQL)
FETCH NEXT FROM some_cursor INTO @ColName, @TableName
End
CLOSE some_cursor
DEALLOCATE some_cursor