SQL Server:搜索所有表中的特定 GUID

问题描述 投票:0回答:6

我遇到了清理一些数据的需要,并且我需要在 SQL Server° 中找到一些特定的 guid(即uniqueidentifiers)。

我提出了一个存储过程,它从当前数据库中每个表中的每个 uniqueidentifier 列执行 SELECT,如果找到 guid,则返回一个结果集。

它使用 INFORMATION_SCHEMA 视图来查找所有 基表中的所有 uniqueidentifier 列(而不是视图)。对于每一列,它发出一个选择,返回表的名称和找到它的列。

CREATE PROCEDURE dbo.FindGUID @searchValue uniqueidentifier AS
/*
    Search all tables in the database for a guid

      6/9/2009: Removed the IF EXISTS to double hit the database
*/

--DECLARE @searchValue uniqueidentifier
--SET @searchValue = '{2A6814B9-8261-452D-A144-13264433864E}'

DECLARE abc CURSOR FOR
    SELECT 
        c.TABLE_NAME, c.COLUMN_NAME
    FROM INFORMATION_SCHEMA.Columns c
        INNER JOIN INFORMATION_SCHEMA.Tables t
        ON c.TABLE_NAME = t.TABLE_NAME
        AND t.TABLE_TYPE = 'BASE TABLE'
    WHERE DATA_TYPE = 'uniqueidentifier'

DECLARE @tableName varchar(200)
DECLARE @columnName varchar(200)
DECLARE @szQuery varchar(8000)

OPEN ABC

FETCH NEXT FROM abc INTO @tableName, @columnName
WHILE (@@FETCH_STATUS = 0)
BEGIN
    SET @szQuery = 
        'SELECT '''+@tableName+''' AS TheTable, '''+@columnName+''' AS TheColumn '+
        'FROM '+@tableName+' '+
        'WHERE '+@columnName+' = '''+CAST(@searchValue AS varchar(50))+''''

    PRINT 'Searching '+@tableName+'.'+@columnName+'..'
    PRINT @szQuery
    EXEC (@szQuery)

    FETCH NEXT FROM abc INTO @tableName, @columnName
END

CLOSE abc
DEALLOCATE abc  

我的问题是:

问题1
任何人都可以找到一种方法来更改它以执行对同一个表中的多个 uniqueidentifier 列的 OR 搜索,而不是单独的查询

SELECT ... FROM Prices WHERE BookGUID = '{...}'
SELECT ... FROM Prices WHERE AuthorGUID = '{...}'
SELECT ... FROM Prices WHERE PublisherGUID = '{...}'
SELECT ... FROM Prices WHERE StoreGUID = '{...}'

将变成:

SELECT ... 
FROM Prices 
WHERE BookGUID = '{...}'
OR AuthorGUID = '{...}'
OR PublisherGUID = '{...}'
OR StoreGUID = '{...}'

我尝试在游标内使用游标,但是 FETCH_STATUS 存在冲突。

问题2 谁能想到更好的方法吗?‡


脚注:

° SQL Server 2000

‡ 受到在关系数据库中使用唯一标识符的约束。

sql-server search guid uniqueidentifier
6个回答
18
投票

您可以推迟执行,直到光标循环完成。然后,只需跟踪循环内的表名称,如果相同,则添加一个 OR,否则结束 SELECT 并开始新的 SELECT。

DECLARE @lasttable varchar(255);
SET @lasttable='';
FETCH NEXT FROM abc INTO @tableName, @columnName;
WHILE (@@FETCH_STATUS = 0)
BEGIN
   IF(@lasttable=@tablename) BEGIN
       SET @szQuery = @szQuery + ' OR [' + @columnName + ']=''' + CAST(@searchValue AS varchar(50)) + '''';
   END ELSE BEGIN
       SET @lasttable = @tablename;
       SET @szQuery = @szQuery + 
         'SELECT '''+@tableName+''' AS TheTable, '''+@columnName+''' AS TheColumn '+
         'FROM '+@tableName+' '+
         'WHERE '+@columnName+' = '''+CAST(@searchValue AS varchar(50))+''''
   END
   FETCH NEXT FROM abc INTO @tableName, @columnName;
END
PRINT @szQuery;
EXEC (@szQuery);

您还可以创建存储过程来构建一个 VIEW,对所有表和 uniqueidentifier 字段执行 UNION ALL。具有这样的模式的东西:

CREATE VIEW all_uuids AS (
    SELECT 'prices' AS tablename, 'BookGUID' as fieldname, ID as primarykey, BookGUID AS guid FROM prices
    UNION ALL SELECT 'prices', 'AuthorGUID', ID, AuthorGUID FROM prices
    UNION ALL SELECT 'othertable', 'otherfield', ID, otherfield FROM othertable
    )

然后,您只需在这个可重用的 VIEW 上执行单个 SELECT 语句即可获取所有匹配的 GUID。要在单个表中搜索,请使用相关子查询,例如:

SELECT * FROM prices WHERE EXISTS (SELECT null FROM all_uuids u WHERE u.primarykey=prices.id AND u.guid=@searchfor AND u.tablename='prices')

这将搜索价格表中的所有 GUID 字段。 SQL Server 足够聪明,不会去查找其他表,它会使用现有表的索引。

通过重用单个视图,您只需在更改架构时循环访问 information_schema,而不是每次查询,并且视图的结果比存储过程的结果更容易连接。


回答

原始海报最终解决方案,基于此答案:

CREATE PROCEDURE dbo.FindGUID @searchValue uniqueidentifier AS

/*
    Search all tables in the database for a guid

    Revision History
    6/9/2009: Initally created
    6/10/2009: Build or clause of multiple columns on one table
*/

--DECLARE @searchValue uniqueidentifier
--SET @searchValue = '{2A6814B9-8261-452D-A144-13264433864E}'

DECLARE abc CURSOR FOR
    SELECT 
        c.TABLE_SCHEMA, c.TABLE_NAME, c.COLUMN_NAME
    FROM INFORMATION_SCHEMA.Columns c
        INNER JOIN INFORMATION_SCHEMA.Tables t
        ON c.TABLE_NAME = t.TABLE_NAME
        AND t.TABLE_TYPE = 'BASE TABLE'
    WHERE DATA_TYPE = 'uniqueidentifier'

DECLARE @tableSchema varchar(200)
DECLARE @tableName varchar(200)
DECLARE @columnName varchar(200)
DECLARE @szQuery varchar(8000)
SET @szQuery = ''

DECLARE @lasttable varchar(255);
SET @lasttable='';

OPEN ABC

FETCH NEXT FROM abc INTO @tableSchema, @tableName, @columnName;
WHILE (@@FETCH_STATUS = 0)
BEGIN
   IF(@lasttable=@tablename) 
   BEGIN
      SET @szQuery = @szQuery + ' OR [' + @columnName + ']=''' + CAST(@searchValue AS varchar(50)) + '''';
   END 
   ELSE 
   BEGIN
       SET @lasttable = @tablename;

       IF @szQuery <> '' 
       BEGIN
          PRINT @szQuery
          EXEC ('IF EXISTS (' + @szQuery + ') BEGIN ' + @szQuery + ' END');
       END

       SET @szQuery = 
         'SELECT '''+@tableSchema+'.'+@tableName+''' AS TheTable, '''+@columnName+''' AS TheColumn '+
         'FROM '+@tableName+' '+
         'WHERE '+@columnName+' = '''+CAST(@searchValue AS varchar(50))+''''
   END
   FETCH NEXT FROM abc INTO @tableSchema, @tableName, @columnName;
END

CLOSE abc
DEALLOCATE abc

IF @szQuery <> '' 
BEGIN
    PRINT @szQuery
    EXEC ('IF EXISTS (' + @szQuery + ') BEGIN ' + @szQuery + ' END');
END
GO

9
投票

您可以将所有内容包装到一个 SELECT 中并一次搜索所有表:

ALTER PROCEDURE dbo.FindGUID @searchValue uniqueidentifier AS
BEGIN
SET NOCOUNT ON;
DECLARE @sql NVARCHAR(MAX);
WITH cte_all_tables(SQL) AS (
    SELECT N'SELECT ''' + QUOTENAME(t.TABLE_SCHEMA) + '.' +QUOTENAME(t.TABLE_NAME) + 
        + N''' FROM ' + QUOTENAME(t.TABLE_SCHEMA) + '.' +QUOTENAME(t.TABLE_NAME)
        + N' WHERE ' +
        (
            SELECT QUOTENAME(c.COLUMN_NAME) + N'= @searchValue OR '
            FROM INFORMATION_SCHEMA.Columns c
            WHERE c.TABLE_NAME = t.TABLE_NAME
                AND c.TABLE_SCHEMA = t.TABLE_SCHEMA
                AND c.DATA_TYPE = 'uniqueidentifier'
            FOR XML PATH('')
        ) + N' 0=1 ' 
   FROM INFORMATION_SCHEMA.Columns c
        INNER JOIN INFORMATION_SCHEMA.Tables t
        ON c.TABLE_NAME = t.TABLE_NAME
        AND t.TABLE_SCHEMA = c.TABLE_SCHEMA
        AND t.TABLE_TYPE = 'BASE TABLE'
    WHERE DATA_TYPE = 'uniqueidentifier')
SELECT @sql = (SELECT [SQL] + N' UNION ALL ' FROM cte_all_tables
FOR XML PATH('')) + N' SELECT NULL WHERE 0=1';
PRINT @SQL;
exec sp_executesql @sql, N'@searchValue uniqueidentifier', @searchValue;
END

我使用了像“OR 0=1”这样的墓碑终止符,甚至是整个 UNION,但这只是因为我懒得从构建的连接字符串中修剪结尾。


5
投票

这是 SQL 2000 的解决方案,无偿使用游标:

declare @searchvalue uniqueidentifier
set @searchValue = '{2A6814B9-8261-452D-A144-13264433864E}'

if object_id('tempdb..#results') is not null drop table #results
create table #results (TableSchema sysname, TableName sysname)

declare @sql nvarchar(4000)

declare @cursor1 cursor
declare @tablename sysname
declare @tableschema sysname

declare @cursor2 cursor
declare @columnname sysname
declare @searchFields nvarchar(4000)

set @cursor1 = cursor for
  select t.TABLE_SCHEMA, t.TABLE_NAME
  from INFORMATION_SCHEMA.Tables t
  where t.TABLE_TYPE = 'BASE TABLE'
    and exists (
      select * from INFORMATION_SCHEMA.Columns c
      where c.TABLE_NAME = t.TABLE_NAME
        and c.TABLE_SCHEMA = t.TABLE_SCHEMA
        and c.DATA_TYPE = 'uniqueidentifier'
      )

open @cursor1
while 1=1 begin
  fetch next from @cursor1 into @tableschema, @tablename
  if @@fetch_status <> 0 break

  set @searchFields = ''
  set @cursor2 = cursor for 
    select c.COLUMN_NAME
    from INFORMATION_SCHEMA.Columns c
    where c.TABLE_NAME = @tablename
      and c.TABLE_SCHEMA = @tableschema
      and c.DATA_TYPE = 'uniqueidentifier'

  open @cursor2
  while 1=1 begin
    fetch next from @cursor2 into @columnname
    if @@fetch_status <> 0 break
    set @searchFields = @searchFields + ', ' + quotename(@columnname)
  end      

  set @searchFields = substring(@searchFields,3,len(@searchFields))
  set @sql = ' insert #results'
           + ' select '''+@tableschema+''','''+@tablename+''''
           + ' from '+quotename(@tableschema)+'.'+quotename(@tablename)
           + ' where @searchValue in ('+@searchFields+')'

  print @sql
  exec sp_executesql @sql, N'@searchValue uniqueidentifier', @searchValue
end

select * from #results

这是 SQL 2005 的解决方案,基于 Remus 的解决方案,带有临时表以实现更好的扩展:

DECLARE @searchValue uniqueidentifier
SET @searchValue = '{2A6814B9-8261-452D-A144-13264433864E}'

IF OBJECT_ID('tempdb..#results') IS NOT NULL DROP TABLE #results
CREATE TABLE #results (TableSchema SYSNAME, TableName SYSNAME);
DECLARE @sql NVARCHAR(MAX);
WITH cte_all_tables(SQL) AS (
    SELECT
          N' INSERT #results (TableSchema, TableName)'
        + N' SELECT ''' + t.TABLE_SCHEMA + ''', ''' + t.TABLE_NAME + N'''' 
        + N' FROM ' + QUOTENAME(t.TABLE_SCHEMA) + '.' +QUOTENAME(t.TABLE_NAME)
        + N' WHERE ' +
        (
                SELECT QUOTENAME(c.COLUMN_NAME) + N' = @searchValue OR '
                FROM INFORMATION_SCHEMA.Columns c
                WHERE c.TABLE_NAME = t.TABLE_NAME
                        AND c.TABLE_SCHEMA = t.TABLE_SCHEMA
                        AND c.DATA_TYPE = 'uniqueidentifier'
                FOR XML PATH('')
        ) + N'0=1'
   FROM INFORMATION_SCHEMA.Columns c
        INNER JOIN INFORMATION_SCHEMA.Tables t
        ON c.TABLE_NAME = t.TABLE_NAME
        AND t.TABLE_SCHEMA = c.TABLE_SCHEMA
        AND t.TABLE_TYPE = 'BASE TABLE'
    WHERE DATA_TYPE = 'uniqueidentifier')
SELECT @sql = (SELECT [SQL]+nchar(10) FROM cte_all_tables FOR XML PATH(''));

PRINT @SQL;
exec sp_executesql @sql, N'@searchValue uniqueidentifier', @searchValue;
SELECT * FROM #results

0
投票

听起来您基本上想将列列表连接到动态 sql 中。 mssql 中没有一流的 concat 函数,您可以编写自己的 CLR udf 来执行此操作,但我不喜欢该解决方案。检查 this Question 以获取一些 mssql concat 解决方案。


0
投票

看起来这里的工程有点过度......你说你只需要“找到一些特定的指导”。导出整个数据库然后在记事本++中打开它并搜索您想要的指南可能会更容易。然后你就会看到当时的整行数据等等

您可以阅读有关将数据库导出到文本文件的 SQL Server 发布向导 这里。


0
投票

我发现评分最高的脚本有点慢,所以我把这个放在一起。它对我来说速度更快,并且还返回行数。享受吧。

DECLARE 
    @GuidToSearch uniqueidentifier = '23D91E70-245F-42FE-9587-882EA9D52432';

DROP TABLE IF EXISTS #GuidSearchResult
CREATE TABLE #GuidSearchResult
(
    SchemaName nvarchar(300) NOT NULL,
    TableName  nvarchar(300) NOT NULL,
    ColumnName nvarchar(300) NOT NULL,
    NoOfRows   int           NOT NULL
);

DECLARE
    @Schema nvarchar(300),
    @Table nvarchar(300),
    @Column nvarchar(300),
    @Script nvarchar(MAX),
    @ScriptTemplate  nvarchar(MAX) = CONCAT(
        'INSERT #GuidSearchResult SELECT ''#SCHEMA#'', ''#TABLE#'', ''#COLUMN#'', COUNT(*) FROM #SCHEMA#.[#TABLE#] WHERE #COLUMN# = ''',@GuidToSearch,''' HAVING COUNT(*) > 0');

DECLARE cur CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY FOR
    SELECT
        t.TABLE_SCHEMA,
        t.TABLE_NAME,
        c.COLUMN_NAME
    FROM
        INFORMATION_SCHEMA.TABLES t
        JOIN INFORMATION_SCHEMA.COLUMNS c ON 
            t.TABLE_NAME = c.TABLE_NAME AND 
            t.TABLE_SCHEMA = c.TABLE_SCHEMA
    WHERE
        c.DATA_TYPE = 'uniqueidentifier' AND 
        t.TABLE_TYPE = 'BASE TABLE';
OPEN cur;

WHILE 1 = 1
BEGIN
    FETCH FROM cur INTO @Schema, @Table, @Column;
    IF @@FETCH_STATUS <> 0
        BREAK;

    SELECT
        @Script = REPLACE(REPLACE(REPLACE(@ScriptTemplate, 
                    '#SCHEMA#', @Schema),
                    '#TABLE#', @Table),
                    '#COLUMN#', @Column)

    PRINT @Script
    EXEC(@Script)
END;

CLOSE cur;
DEALLOCATE cur;

-- Return result
SELECT
    SR.SchemaName,
    SR.TableName,
    SR.ColumnName,
    SR.NoOfRows
FROM
    #GuidSearchResult SR
© www.soinside.com 2019 - 2024. All rights reserved.