Поиск строки во всех таблицах, строках и столбцах БД - PullRequest
76 голосов
/ 26 февраля 2009

Я потерян в большой базе данных и не могу найти, откуда берутся данные. Мне было интересно, возможно ли в SQL Server 2005 искать строку во всех таблицах, строках и столбцах базы данных?

У кого-нибудь есть идея, возможно ли это и как?

Ответы [ 15 ]

90 голосов
/ 26 февраля 2009

Этот код должен делать это в SQL 2005, но есть несколько предостережений:

  1. Это смешно медленно. Я проверил его на небольшой базе данных, которая у меня есть только с несколькими таблицами, и это заняло много минут. Если ваша база данных настолько велика, что вы не можете ее понять, то это, вероятно, в любом случае будет невозможно использовать.

  2. Я списал это с манжеты. Я не включил обработку ошибок, и может быть какая-то другая небрежность, тем более что я не часто использую курсоры. Например, я думаю, что есть способ обновить курсор столбцов вместо того, чтобы каждый раз закрывать / освобождать / пересоздавать его.

Если вы не можете понять базу данных или не знаете, откуда взялись вещи, то вам, вероятно, следует найти кого-то, кто знает. Даже если вы можете найти, где находятся данные, они могут где-то дублироваться или могут быть другие аспекты базы данных, которые вы не понимаете. Если никто в вашей компании не понимает базу данных, значит, вы в большом беспорядке.

DECLARE
    @search_string  VARCHAR(100),
    @table_name     SYSNAME,
    @table_schema   SYSNAME,
    @column_name    SYSNAME,
    @sql_string     VARCHAR(2000)

SET @search_string = 'Test'

DECLARE tables_cur CURSOR FOR SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'

OPEN tables_cur

FETCH NEXT FROM tables_cur INTO @table_schema, @table_name

WHILE (@@FETCH_STATUS = 0)
BEGIN
    DECLARE columns_cur CURSOR FOR SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @table_schema AND TABLE_NAME = @table_name AND COLLATION_NAME IS NOT NULL  -- Only strings have this and they always have it

    OPEN columns_cur

    FETCH NEXT FROM columns_cur INTO @column_name
    WHILE (@@FETCH_STATUS = 0)
    BEGIN
        SET @sql_string = 'IF EXISTS (SELECT * FROM ' + QUOTENAME(@table_schema) + '.' + QUOTENAME(@table_name) + ' WHERE ' + QUOTENAME(@column_name) + ' LIKE ''%' + @search_string + '%'') PRINT ''' + QUOTENAME(@table_schema) + '.' + QUOTENAME(@table_name) + ', ' + QUOTENAME(@column_name) + ''''

        EXECUTE(@sql_string)

        FETCH NEXT FROM columns_cur INTO @column_name
    END

    CLOSE columns_cur

    DEALLOCATE columns_cur

    FETCH NEXT FROM tables_cur INTO @table_schema, @table_name
END

CLOSE tables_cur

DEALLOCATE tables_cur
35 голосов
/ 05 марта 2013

Я бы посоветовал вам найти сторонний инструмент для этого, такой как ApexSQL Search (возможно, есть и другие, но я использую этот, потому что он бесплатный).

Если вы действительно хотите использовать SQL, попробуйте использовать хранимую процедуру, созданную Sorna Kumar Muthuraj - скопированный код находится ниже. Просто выполните эту хранимую процедуру для всех таблиц в вашей схеме (легко с динамическим SQL)

CREATE PROCEDURE SearchTables 
 @Tablenames VARCHAR(500) 
,@SearchStr NVARCHAR(60) 
,@GenerateSQLOnly Bit = 0 
AS 

/* 
    Parameters and usage 

    @Tablenames        -- Provide a single table name or multiple table name with comma seperated.  
                        If left blank , it will check for all the tables in the database 
    @SearchStr        -- Provide the search string. Use the '%' to coin the search.  
                        EX : X%--- will give data staring with X 
                             %X--- will give data ending with X 
                             %X%--- will give data containig  X 
    @GenerateSQLOnly -- Provide 1 if you only want to generate the SQL statements without seraching the database.  
                        By default it is 0 and it will search. 

    Samples : 

    1. To search data in a table 

        EXEC SearchTables @Tablenames = 'T1' 
                         ,@SearchStr  = '%TEST%' 

        The above sample searches in table T1 with string containing TEST. 

    2. To search in a multiple table 

        EXEC SearchTables @Tablenames = 'T2' 
                         ,@SearchStr  = '%TEST%' 

        The above sample searches in tables T1 & T2 with string containing TEST. 

    3. To search in a all table 

        EXEC SearchTables @Tablenames = '%' 
                         ,@SearchStr  = '%TEST%' 

        The above sample searches in all table with string containing TEST. 

    4. Generate the SQL for the Select statements 

        EXEC SearchTables @Tablenames        = 'T1' 
                         ,@SearchStr        = '%TEST%' 
                         ,@GenerateSQLOnly    = 1 

*/ 

    SET NOCOUNT ON 

    DECLARE @CheckTableNames Table 
    ( 
    Tablename sysname 
    ) 

    DECLARE @SQLTbl TABLE 
    ( 
     Tablename        SYSNAME 
    ,WHEREClause    VARCHAR(MAX) 
    ,SQLStatement   VARCHAR(MAX) 
    ,Execstatus        BIT  
    ) 

    DECLARE @sql VARCHAR(MAX) 
    DECLARE @tmpTblname sysname 

    IF LTRIM(RTRIM(@Tablenames)) IN ('' ,'%') 
    BEGIN 

        INSERT INTO @CheckTableNames 
        SELECT Name 
          FROM sys.tables 
    END 
    ELSE 
    BEGIN 

        SELECT @sql = 'SELECT ''' + REPLACE(@Tablenames,',',''' UNION SELECT ''') + '''' 

        INSERT INTO @CheckTableNames 
        EXEC(@sql) 

    END 

    INSERT INTO @SQLTbl 
    ( Tablename,WHEREClause) 
    SELECT SCh.name + '.' + ST.NAME, 
            ( 
                SELECT '[' + SC.name + ']' + ' LIKE ''' + @SearchStr + ''' OR ' + CHAR(10) 
                  FROM SYS.columns SC 
                  JOIN SYS.types STy 
                    ON STy.system_type_id = SC.system_type_id 
                   AND STy.user_type_id =SC.user_type_id 
                 WHERE STY.name in ('varchar','char','nvarchar','nchar') 
                   AND SC.object_id = ST.object_id 
                 ORDER BY SC.name 
                FOR XML PATH('') 
            ) 
      FROM  SYS.tables ST 
      JOIN @CheckTableNames chktbls 
                ON chktbls.Tablename = ST.name  
      JOIN SYS.schemas SCh 
        ON ST.schema_id = SCh.schema_id 
     WHERE ST.name <> 'SearchTMP' 
      GROUP BY ST.object_id, SCh.name + '.' + ST.NAME ; 

      UPDATE @SQLTbl 
         SET SQLStatement = 'SELECT * INTO SearchTMP FROM ' + Tablename + ' WHERE ' + substring(WHEREClause,1,len(WHEREClause)-5) 

      DELETE FROM @SQLTbl 
       WHERE WHEREClause IS NULL 

    WHILE EXISTS (SELECT 1 FROM @SQLTbl WHERE ISNULL(Execstatus ,0) = 0) 
    BEGIN 

        SELECT TOP 1 @tmpTblname = Tablename , @sql = SQLStatement 
          FROM @SQLTbl  
         WHERE ISNULL(Execstatus ,0) = 0 



         IF @GenerateSQLOnly = 0 
         BEGIN 

            IF OBJECT_ID('SearchTMP','U') IS NOT NULL 
                DROP TABLE SearchTMP 
            EXEC (@SQL) 

            IF EXISTS(SELECT 1 FROM SearchTMP) 
            BEGIN 
                SELECT Tablename=@tmpTblname,* FROM SearchTMP 
            END 

         END 
         ELSE 
         BEGIN 
             PRINT REPLICATE('-',100) 
             PRINT @tmpTblname 
             PRINT REPLICATE('-',100) 
             PRINT replace(@sql,'INTO SearchTMP','') 
         END 

         UPDATE @SQLTbl 
            SET Execstatus = 1 
          WHERE Tablename = @tmpTblname 

    END 

    SET NOCOUNT OFF 

go
27 голосов
/ 24 сентября 2012

Несмотря на то, что решения, представленные ранее, действительны и работают, я скромно предлагаю код, который будет чище, элегантнее и с лучшей производительностью, по крайней мере, на мой взгляд.

Во-первых, можно спросить: зачем кому-то нужен фрагмент кода, чтобы глобально и вслепую искать строку? Эй, они уже изобрели полный текст, ты не знаешь?

Мой ответ: в основном я работаю над проектами по системной интеграции, и важно знать, где записываются данные, всякий раз, когда я изучаю новую и недокументированную базу данных, что случается редко.

Кроме того, код, который я представляю, является урезанной версией более мощного и опасного скрипта, который выполняет поиск и ЗАМЕНЯЕТ текст по всей базе данных.

CREATE TABLE #result(
  id      INT IDENTITY, -- just for register seek order
  tblName VARCHAR(255),
  colName VARCHAR(255),
  qtRows  INT
)
go

DECLARE @toLookFor VARCHAR(255)
SET @toLookFor = '[input your search criteria here]'

DECLARE cCursor CURSOR LOCAL FAST_FORWARD FOR
SELECT
  '[' + usr.name + '].[' + tbl.name + ']' AS tblName,
  '[' + col.name + ']' AS colName,
  LOWER(typ.name) AS typName
FROM
  sysobjects tbl
    INNER JOIN(
      syscolumns col
        INNER JOIN systypes typ
        ON typ.xtype = col.xtype
    )
    ON col.id = tbl.id
    --
    LEFT OUTER JOIN sysusers usr
    ON usr.uid = tbl.uid

WHERE tbl.xtype = 'U'
  AND LOWER(typ.name) IN(
        'char', 'nchar',
        'varchar', 'nvarchar',
        'text', 'ntext'
      )
ORDER BY tbl.name, col.colorder
--
DECLARE @tblName VARCHAR(255)
DECLARE @colName VARCHAR(255)
DECLARE @typName VARCHAR(255)
--
DECLARE @sql  NVARCHAR(4000)
DECLARE @crlf CHAR(2)

SET @crlf = CHAR(13) + CHAR(10)

OPEN cCursor
FETCH cCursor
INTO @tblName, @colName, @typName

WHILE @@fetch_status = 0
BEGIN
  IF @typName IN('text', 'ntext')
  BEGIN
    SET @sql = ''
    SET @sql = @sql + 'INSERT INTO #result(tblName, colName, qtRows)' + @crlf
    SET @sql = @sql + 'SELECT @tblName, @colName, COUNT(*)' + @crlf
    SET @sql = @sql + 'FROM ' + @tblName + @crlf
    SET @sql = @sql + 'WHERE PATINDEX(''%'' + @toLookFor + ''%'', ' + @colName + ') > 0' + @crlf
  END
  ELSE
  BEGIN
    SET @sql = ''
    SET @sql = @sql + 'INSERT INTO #result(tblName, colName, qtRows)' + @crlf
    SET @sql = @sql + 'SELECT @tblName, @colName, COUNT(*)' + @crlf
    SET @sql = @sql + 'FROM ' + @tblName + @crlf
    SET @sql = @sql + 'WHERE ' + @colName + ' LIKE ''%'' + @toLookFor + ''%''' + @crlf
  END

  EXECUTE sp_executesql
            @sql,
            N'@tblName varchar(255), @colName varchar(255), @toLookFor varchar(255)',
            @tblName, @colName, @toLookFor

  FETCH cCursor
  INTO @tblName, @colName, @typName
END

SELECT *
FROM #result
WHERE qtRows > 0
ORDER BY id
GO

DROP TABLE #result
go
13 голосов
/ 26 февраля 2009

Если вы «получаете данные» из приложения, разумно было бы использовать профилировщик и профилировать базу данных при запуске приложения. Проследите это, затем ищите результаты для этой строки.

8 голосов
/ 03 июля 2009

Надстройка PACK для инструментов SSMS (надстройка) для Microsoft SQL Server Management Studio и Microsoft SQL Server Management Studio Express сделает именно то, что вам нужно. В более крупной базе данных поиск занимает некоторое время, но этого и следовало ожидать. Он также включает в себя массу интересных функций, которые в первую очередь должны были быть включены в SQL Server Management Studio. Попробуйте www.ssmstoolspack.com /

Для запуска инструментов необходимо установить SP2 для SQL Server Management Studio.

6 голосов
/ 20 мая 2009

Я адаптировал сценарий, изначально написанный Нараяна Вьяс Кондредди в 2002 . Я изменил предложение where, чтобы также проверять поля text / ntext, используя patindex, а не like. Я также немного изменил таблицу результатов. Неоправданно я изменил имена переменных и выровнял их по своему усмотрению (неуважение к мистеру Кондретти). Пользователь может захотеть изменить искомые типы данных. Я использовал глобальную таблицу, чтобы разрешить обработку в середине обработки, но постоянная таблица могла бы быть более разумным способом.

/* original script by Narayana Vyas Kondreddi, 2002 */
/* adapted by Oliver Holloway, 2009 */

/* these lines can be replaced by use of input parameter for a proc */
declare @search_string varchar(1000);
set @search_string = 'what.you.are.searching.for';

/* create results table */
create table ##string_locations (
  table_name varchar(1000),
  field_name varchar(1000),
  field_value varchar(8000)
)
;
/* special settings */
set nocount on
;
/* declare variables */
declare
  @table_name varchar(1000),
  @field_name varchar(1000)
;
/* variable settings */
set @table_name = ''
;
set @search_string = QUOTENAME('%' + @search_string + '%','''')
;
/* for each table */
while @table_name is not null
begin

  set @field_name = ''
  set @table_name = (
    select MIN(QUOTENAME(table_schema) + '.' + QUOTENAME(table_name))
    from INFORMATION_SCHEMA.TABLES
    where 
      table_type = 'BASE TABLE' and
      QUOTENAME(table_schema) + '.' + QUOTENAME(table_name) > @table_name and
      OBJECTPROPERTY(OBJECT_ID(QUOTENAME(table_schema) + '.' + QUOTENAME(table_name)), 'IsMSShipped') = 0
  )

  /* for each string-ish field */
  while (@table_name is not null) and (@field_name is not null)
  begin
    set @field_name = (
      select MIN(QUOTENAME(column_name))
      from INFORMATION_SCHEMA.COLUMNS
      where 
        table_schema    = PARSENAME(@table_name, 2) and
        table_name  = PARSENAME(@table_name, 1) and
        data_type in ('char', 'varchar', 'nchar', 'nvarchar', 'text', 'ntext') and
        QUOTENAME(column_name) > @field_name
    )

    /* search that field for the string supplied */
    if @field_name is not null
    begin
      insert into ##string_locations
      exec(
        'select ''' + @table_name + ''',''' + @field_name + ''',' + @field_name + 
        'from ' + @table_name + ' (nolock) ' +
        'where patindex(' + @search_string + ',' + @field_name + ') > 0'  /* patindex works with char & text */
      )
    end
    ;
  end
  ;
end
;

/* return results */
select table_name, field_name, field_value from ##string_locations (nolock)
;
/* drop temp table */
--drop table ##string_locations
;
4 голосов
/ 22 июня 2010

Другие опубликованные ответы могут работать одинаково хорошо или лучше, но я ими не пользовался. Тем не менее, следующий SQL-код, который я использовал, действительно помог мне, когда я пытался выполнить реинжиниринг большой системы с огромной (и очень неорганизованной) базой данных SQL Server.

Это не мой код. Хотелось бы отдать должное первоначальному автору, но я больше не могу найти ссылку на статью: (

Use 
go

declare @SearchChar varchar(8000)
Set @SearchChar =  -- Like 'A%', '11/11/2006'

declare @CMDMain varchar(8000), @CMDMainCount varchar(8000),@CMDJoin varchar(8000)
declare @ColumnName varchar(100),@TableName varchar(100)

declare dbTable cursor for 
SELECT 
Distinct b.Name as TableName
FROM 
sysobjects b
WHERE 
b.type='u' and b.Name  'dtproperties'
order by b.name
open dbTable
fetch next from dbTable into @TableName

WHILE @@FETCH_STATUS = 0
BEGIN
declare db cursor for 
SELECT 
c.Name as ColumnName
FROM 
sysobjects b,
syscolumns c
WHERE 
C.id = b.id and
b.type='u' and b.Name = @TableName
order by b.name
open db
fetch next from db into @ColumnName
set @CMDMain = 'SELECT ' + char(39) + @TableName + char(39) + ' as TableName,'+ 
' ['+ @TableName + '].* FROM [' + @TableName + ']'+
' WHERE '
set @CMDMainCount = 'SELECT Count(*) FROM [' + @TableName + '] Where '
Set @CMDJoin = ''
WHILE @@FETCH_STATUS = 0
BEGIN
set @CMDJoin = @CMDJoin + 'Convert(varchar(5000),[' +@ColumnName + ']) like ' + char(39) + @SearchChar + char(39) + ' OR '

fetch next from db into @ColumnName
end
close db
deallocate db

Set @CMDMainCount = 'If ('+ @CMDMainCount + Left(@CMDJoin, len(@CMDJoin) - 3)+ ') > 0 Begin '
Set @CMDMain = @CMDMainCount + @CMDMain + Left(@CMDJoin, len(@CMDJoin) - 3)
Set @CMDMain = @CMDMain + ' End '

Print @CMDMain

exec (@CMDMain)
fetch next from dbTable into @TableName
end
close dbTable
deallocate dbTable
4 голосов
/ 26 февраля 2009

На самом деле я согласен с MikeW (+1), для этого лучше использовать профилировщик.

В любом случае, если вам действительно нужно собрать все (n) столбцов varchar в БД и выполнить поиск. Увидеть ниже. Я предполагаю использовать INFORMATION_SCHEMA.Tables + динамический SQL. Простой поиск:

DECLARE @SearchText VARCHAR(100) 
SET @SearchText = '12'
DECLARE @Tables TABLE(N INT, TableName VARCHAR(100), ColumnNamesCSV VARCHAR(2000), SQL VARCHAR(4000))

INSERT INTO @Tables (TableName, ColumnNamesCSV)
SELECT  T.TABLE_NAME AS TableName, 
        ( SELECT C.Column_Name + ',' 
          FROM   INFORMATION_SCHEMA.Columns C 
          WHERE  T.TABLE_NAME = C.TABLE_NAME 
                 AND C.DATA_TYPE IN ('nvarchar','varchar') 
                 FOR XML PATH('')
        )
FROM    INFORMATION_SCHEMA.Tables T 

DELETE FROM @Tables WHERE ColumnNamesCSV IS NULL

INSERT INTO @Tables (N, TableName, ColumnNamesCSV)
SELECT ROW_NUMBER() OVER(ORDER BY TableName), TableName, ColumnNamesCSV  
FROM   @Tables

DELETE FROM @Tables WHERE N IS NULL

UPDATE @Tables 
SET ColumnNamesCSV = SUBSTRING(ColumnNamesCSV, 0, LEN(ColumnNamesCSV))

UPDATE @Tables 
SET SQL = 'SELECT * FROM ['+TableName+'] WHERE '''+@SearchText+''' IN ('+ColumnNamesCSV+')'

DECLARE @C INT, 
        @I INT, 
        @SQL VARCHAR(4000)

SELECT @I = 1, 
       @C = COUNT(1) 
FROM   @Tables

WHILE @I <= @C BEGIN
    SELECT @SQL = SQL FROM @Tables WHERE N = @I
    SET @I = @I+1
    EXEC(@SQL)
END

и один с предложением LIKE:

DECLARE @SearchText VARCHAR(100) 
SET @SearchText = '12'

DECLARE @Tables TABLE(N INT, TableName VARCHAR(100), ColumnNamesCSVLike VARCHAR(2000), LIKESQL VARCHAR(4000))

INSERT INTO @Tables (TableName, ColumnNamesCSVLike)
SELECT   T.TABLE_NAME AS TableName, 
         (   SELECT  C.Column_Name + ' LIKE ''%'+@SearchText+'%'' OR ' 
             FROM    INFORMATION_SCHEMA.Columns C 
             WHERE   T.TABLE_NAME = C.TABLE_NAME 
                     AND C.DATA_TYPE IN ('nvarchar','varchar') 
          FOR XML PATH(''))
FROM     INFORMATION_SCHEMA.Tables T

DELETE FROM @Tables WHERE ColumnNamesCSVLike IS NULL

INSERT INTO @Tables (N, TableName, ColumnNamesCSVLike)
SELECT ROW_NUMBER() OVER(ORDER BY TableName), TableName, ColumnNamesCSVLike 
FROM @Tables

DELETE FROM @Tables WHERE N IS NULL

UPDATE @Tables 
SET  ColumnNamesCSVLike = SUBSTRING(ColumnNamesCSVLike, 0, LEN(ColumnNamesCSVLike)-2)

UPDATE @Tables SET LIKESQL = 'SELECT * FROM ['+TableName+'] WHERE '+ColumnNamesCSVLike

DECLARE @C INT, 
        @I INT, 
        @LIKESQL VARCHAR(4000)

SELECT @I = 1, 
       @C = COUNT(1) 
FROM @Tables

WHILE @I <= @C BEGIN
    SELECT @LIKESQL = LIKESQL FROM @Tables WHERE N = @I
    SET @I = @I +1
    EXEC(@LIKESQL)
END
3 голосов
/ 08 августа 2013

@ NLwino, очень хороший запрос с несколькими ошибками при использовании ключевых слов. Мне пришлось немного изменить его, чтобы обернуть ключевые слова [], а также посмотреть столбцы char и ntext.

    DECLARE @searchstring  NVARCHAR(255)
    SET @searchstring = '%WDB1014%'

    DECLARE @sql NVARCHAR(max)

    SELECT @sql = STUFF((
      SELECT ' UNION ALL SELECT ''' + TABLE_NAME + ''' AS tbl, ''' + COLUMN_NAME + ''' AS col, [' + COLUMN_NAME + '] AS val' + 
        ' FROM ' + TABLE_SCHEMA + '.[' + TABLE_NAME + 
        '] WHERE [' + COLUMN_NAME + '] LIKE ''' + @searchstring + ''''
        FROM INFORMATION_SCHEMA.COLUMNS 
        WHERE DATA_TYPE in ('nvarchar', 'varchar', 'char', 'ntext')
                  FOR XML PATH('')
             ) ,1, 11, '')

    Exec (@sql)

Я запустил его на 2,5 ГБ базы данных, и он вернулся через 51 секунду

3 голосов
/ 29 мая 2013

При этом не используются курсоры или что-то в этом роде, только один динамический запрос.

Также обратите внимание, что здесь используется LIKE. Так как это случилось то, что мне было нужно. Он работает для всех схем, всех таблиц и запрашивает только те столбцы, которые NVARCHAR или VARCHAR, даже если они имеют UDDT.

DECLARE @searchstring  NVARCHAR(255)
SET @searchstring = '%searchstring%'

DECLARE @sql NVARCHAR(max)

SELECT @sql = STUFF((
    SELECT ' UNION ALL SELECT ''' + TABLE_NAME + ''' AS tablename, ''' + COLUMN_NAME + ''' AS columnname, ' + COLUMN_NAME + ' AS valuename' + 
    ' FROM ' + TABLE_SCHEMA + '.' + TABLE_NAME + 
    ' WHERE ' + COLUMN_NAME + ' LIKE ''' + @searchstring + ''''
    FROM INFORMATION_SCHEMA.COLUMNS 
    WHERE DATA_TYPE in ('nvarchar', 'varchar')
    FOR XML PATH('')
) ,1, 11, '')

EXEC(@sql)

В результате вы получите таблицу, столбец и значение. Время выполнения в небольшой базе данных составило ~ 3 секунды, было около 3000 результатов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...