在表格列中搜索超过特定长度的值

时间:2013-11-20 21:03:31

标签: sql sql-server sql-server-2008

所以我有一个包含许多表的数据库,这些表的列包含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子句中的表。任何想法或帮助将不胜感激!

提前致谢!

5 个答案:

答案 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