搜索服务器上所有数据库中的所有表以获取字符串

时间:2016-08-08 15:01:46

标签: sql sql-server tsql search

编辑:此问题被标记为重复,但事实并非如此。 SO上的其他答案显示了如何搜索单个数据库中的所有表,我需要搜索给定服务器上每个数据库中的所有表。

我需要搜索服务器上所有数据库的所有表以搜索字符串。我的电子邮件地址遍布整个表格,这些地址将会更改域名,我需要准备一份报告,显示这些电子邮件地址的位置。我无法向所有数据库添加存储过程,所以我需要一个查询来执行此操作,这不会涉及执行sp重复。我将此代码从the net中删除,并使用它来搜索所有表,但我无法弄清楚如何在所有数据库上运行它。

drop table #Results
CREATE TABLE #Results (ColumnName nvarchar(370), ColumnValue nvarchar(3630))

SET NOCOUNT ON

DECLARE @SearchStr nvarchar(100), @TableName nvarchar(256), @ColumnName nvarchar(128), @SearchStr2 nvarchar(110)
SET @SearchStr = '@domaintobereplaced.com'
SET  @TableName = ''
SET @SearchStr2 = QUOTENAME('%' + @SearchStr + '%','''')

WHILE @TableName IS NOT NULL
BEGIN
    SET @ColumnName = ''
    SET @TableName = 
    (
        SELECT MIN(QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME))
        FROM    INFORMATION_SCHEMA.TABLES
        WHERE       TABLE_TYPE = 'BASE TABLE'
            AND QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME) > @TableName
            AND OBJECTPROPERTY(
                    OBJECT_ID(
                        QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME)
                         ), 'IsMSShipped'
                           ) = 0
    )

    WHILE (@TableName IS NOT NULL) AND (@ColumnName IS NOT NULL)
    BEGIN
        SET @ColumnName =
        (
            SELECT MIN(QUOTENAME(COLUMN_NAME))
            FROM    INFORMATION_SCHEMA.COLUMNS
            WHERE       TABLE_SCHEMA    = PARSENAME(@TableName, 2)
                AND TABLE_NAME  = PARSENAME(@TableName, 1)
                AND DATA_TYPE IN ('char', 'varchar', 'nchar', 'nvarchar')
                AND QUOTENAME(COLUMN_NAME) > @ColumnName
        )

        IF @ColumnName IS NOT NULL
        BEGIN
            INSERT INTO #Results
            EXEC
            (
                'SELECT top 10 ''' + @TableName + '.' + @ColumnName + ''', LEFT(' + @ColumnName + ', 3630) 
                FROM ' + @TableName + ' (NOLOCK) ' +
                ' WHERE ' + @ColumnName + ' LIKE ' + @SearchStr2
            )
        END
    END 
END

SELECT ColumnName, ColumnValue FROM #Results

6 个答案:

答案 0 :(得分:1)

首先,您必须从sys.databases收集所有数据库名称的列表。
然后,您必须创建动态SQL,以[database].[schema].[table name]'. You can do it by linking following tables: [数据库] .sys.schemas'和&amp ;; [database].sys.tables'<BR/> Then you get list of all text columns by linking found tables to [数据库] .sys.columns'
获得所有这些后,您可以为所有表和文本列创建动态查询。

BTW如果有人隐藏TEXT列中的数据,您必须将其包含在搜索中并进行转换。

答案 1 :(得分:1)

编辑:

我的回答是存储过程。 这里是你的查询:

更改@ SearchTerm以进行搜索。

declare @SearchTerm nvarchar(12)
set @SearchTerm = 'WORD'
CREATE TABLE #results
  (
    [database]   SYSNAME,
    [schema]     SYSNAME,
    [table]      SYSNAME,
    [column]     SYSNAME,
    ExampleValue NVARCHAR(1000)
  );

  DECLARE
    @DatabaseCommands  NVARCHAR(MAX) = N'', 
    @ColumnCommands NVARCHAR(MAX) = N'';

  SELECT @DatabaseCommands = @DatabaseCommands + N'
    EXEC ' + QUOTENAME(name) + '.sys.sp_executesql
        @ColumnCommands, N''@SearchTerm NVARCHAR(MAX)'', @SearchTerm;'
    FROM sys.databases
    WHERE database_id  > 4  -- non-system databases
      AND[state]      = 0-- online
      AND user_access  = 0; -- multi-user

    SET @ColumnCommands = N'DECLARE @q NCHAR(1),
          @SearchCommands NVARCHAR(MAX);

SELECT @q = NCHAR(39),
  @SearchCommands = N''DECLARE @VSearchTerm VARCHAR(255) = @SearchTerm;'';

    SELECT @SearchCommands = @SearchCommands + CHAR(10) + N''

      SELECT TOP(1)
        [db]     = DB_NAME(),
        [schema] = N'' + @q + s.name + @q + '', 
        [table]  = N'' + @q + t.name + @q + '',
        [column] = N'' + @q + c.name + @q + '',
        ExampleValue = LEFT('' + QUOTENAME(c.name) + '', 1000)
      FROM '' + QUOTENAME(s.name) + ''.'' + QUOTENAME(t.name) + ''
      WHERE '' + QUOTENAME(c.name) + N'' LIKE @'' + CASE
        WHEN c.system_type_id IN(35, 167, 175) THEN ''V'' 
        ELSE '''' END + ''SearchTerm;'' 

    FROM sys.schemas AS s
    INNER JOIN sys.tables AS t
    ON s.[schema_id] = t.[schema_id]
    INNER JOIN sys.columns AS c
    ON t.[object_id] = c.[object_id]
    WHERE c.system_type_id IN (35, 99, 167, 175, 231, 239)
      AND c.max_length >= LEN(@SearchTerm);

PRINT @SearchCommands;
EXEC sys.sp_executesql @SearchCommands,
  N''@SearchTerm NVARCHAR(255)'', @SearchTerm;';

  INSERT #Results
  (
    [database],
    [schema],
    [table],
    [column],
    ExampleValue
  )
  EXEC[master].sys.sp_executesql @DatabaseCommands,
   N'@ColumnCommands NVARCHAR(MAX), @SearchTerm NVARCHAR(255)', 
    @ColumnCommands, @SearchTerm;

  SELECT[Searched for] = @SearchTerm;

  SELECT[database],[schema],[table],[column],ExampleValue
FROM #Results 
    ORDER BY[database],[schema],[table],[column];

答案 2 :(得分:0)

我不知道这是否会有所帮助,我写的是vb.net代码,它找到了一个字符串所在的表和字段,可能会搜索所有数据库 - 它&#39 ;只是一个调查助手,我已经添加了字段类型,但它可能会让你知道我是怎么做的

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click

    Dim scon As String = String.Format("Data Source={0};Initial Catalog=MASTER;Integrated Security=True", cmbInstance.SelectedItem.ToString)

    Button1.Enabled = False
    Dim Matches As New List(Of Locator)

    With ListVresults

        .Clear()

        .View = View.Details


        .Columns.Add("Database", 200)
        .Columns.Add("Table", 200)
        .Columns.Add("Field", 200)

    End With


    Using con As New SqlConnection(scon),
            da As New SqlDataAdapter("select *, SCHEMA_NAME(schema_id )  as SkeemaName from sys.tables where type = 'U'", con),
            daDB As New SqlDataAdapter("select * from sys.databases WHERE name NOT IN ('master', 'tempdb', 'model', 'msdb') and state = 0 ORDER BY NAME", con),
            dtDB As New DataTable

        Dim UseLike As Boolean = chkLIKE.Checked
        Dim CompOperator As String = If(chkLIKE.Checked, "LIKE", "=")

        con.Open()


        daDB.Fill(dtDB)

        For Each drdb As DataRow In dtDB.Select(If(cmbDB.SelectedIndex = 0, "", String.Format("name = '{0}'", cmbDB.SelectedItem.ToString)), "name")
            'For Each drdb As DataRow In dtDB.Rows

            con.ChangeDatabase(drdb!name.ToString)

            Using dt As New DataTable
                da.Fill(dt)
                For Each dr As DataRow In dt.Select("", "name")
                    lblProc.Text = String.Format("({2} - {0} - {1}", drdb!name.ToString, dr!name.ToString, Matches.Count)
                    Application.DoEvents()
                    Dim sql As New System.Text.StringBuilder
                    Using dsDatNull As New DataSet,
                        daDatNull As New SqlDataAdapter(String.Format("SELECT * FROM [{0}].[{1}] WHERE 1=0; SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '{0}' AND TABLE_NAME = '{1}'", dr!skeemaName, dr!name), con)

                        daDatNull.Fill(dsDatNull)

                        'SELECT * FROM(INFORMATION_SCHEMA.COLUMNS) WHERE TABLE_NAME = 'absence'

                        sql.AppendFormat("SELECT * FROM [{0}].[{1}] WHERE ", dr!skeemaname, dr!name)



                        Dim bOR As Boolean = False

                        Dim col As DataColumn
                        Dim Q As System.Data.EnumerableRowCollection(Of System.Data.DataRow) = (From x As DataRow In dsDatNull.Tables(1) Where String.Compare(x!column_name.ToString, col.ColumnName, True) = 0 Select x)

                        For Each col In dsDatNull.Tables(0).Columns

                            Dim drColType As DataRow = Q(0)
                            Dim dataType As String = drColType!Data_Type.ToString


                            If "x".GetType Is col.DataType AndAlso (String.Compare(dataType, "nvarchar", True) = 0 _
                                                                        OrElse String.Compare(dataType, "varchar", True) = 0 _
                                                                        OrElse String.Compare(dataType, "nchar", True) = 0 _
                                                                        OrElse String.Compare(dataType, "char", True) = 0) Then



                                If bOR Then sql.Append(" OR ")
                                bOR = True
                                sql.Append("[" + col.ColumnName + "]").Append(If(UseLike, " LIKE ", " = ")).AppendFormat("'{0}{1}{2}'", If(UseLike, "%", ""), txtCode.Text.Replace("'", "''"), If(UseLike, "%", ""))


                            ElseIf (New Guid).GetType Is col.DataType Then

                                If bOR Then sql.Append(" OR ")
                                bOR = True
                                sql.Append("cast([" + col.ColumnName + "] as nvarchar(80))").Append(If(UseLike, " LIKE ", " = ")).AppendFormat("'{0}{1}{2}'", If(UseLike, "%", ""), txtCode.Text.Replace("'", "''"), If(UseLike, "%", ""))


                            End If

                        Next

                        If Not bOR Then sql.Append(" 1=0 ")

                    End Using



                    Using dtDat As New DataTable,
                        daDat As New SqlDataAdapter(sql.ToString, con)
                        daDat.SelectCommand.CommandTimeout = 3600

                        daDat.Fill(dtDat)

                        For Each drDat As DataRow In dtDat.Rows
                            For Each col As DataColumn In dtDat.Columns

                                Dim obj = drDat(col)

                                If String.Compare(obj.ToString, txtCode.Text, True) = 0 OrElse UseLike And obj.ToString.ToLower.Contains(txtCode.Text.ToLower) Then

                                    If Not (From x As Locator In Matches Where x.DB.ToString = drdb!name.ToString And x.Table = dr!name.ToString And x.Column = col.ColumnName).Any Then
                                        Dim newOne As New Locator(drdb!name.ToString, dr!name.ToString, col.ToString)
                                        Matches.Add(newOne)
                                        With newOne
                                            ListVresults.Items.Add(New ListViewItem({.DB, .Table, .Column}))
                                        End With

                                    End If
                                End If

                            Next
                        Next


                    End Using


                Next
            End Using

        Next
    End Using

    lblProc.Text = ""


    Button1.Enabled = True
End Sub

答案 3 :(得分:0)

假设这是一次性的事情,这是游标方法的替代方案。这将构建一个动态字符串,然后您可以执行该字符串。它不适用于每个数据库,但您可以手动为每个数据库运行此操作,也可以将其修改为使用sp_msforeachdb。您可能会发现这种方法更好,因为未记录的foreachdb有时会遗漏数据库。 https://sqlblog.org/2010/12/29/a-more-reliable-and-more-flexible-sp_msforeachdb

DECLARE @MySearchCriteria VARCHAR(500)
SET @MySearchCriteria = '''YourSearchStringHere''' --you do need all these quotation marks because this string is injected to another string.

SELECT 'SELECT ''' + t.name + ''' as TableName, ' + c.columnlist + '] FROM [' + s.name + '].[' + t.name + '] WHERE ' + w.whereclause  as SelectStatement
FROM sys.tables t 
join sys.schemas s on s.schema_id = t.schema_id
CROSS APPLY (
    SELECT STUFF((    
        SELECT '], [' + c.Name AS [text()]
        FROM sys.columns c
        join sys.types t2 on t2.user_type_id = c.user_type_id
        WHERE t.object_id = c.object_id 
            AND c.collation_name IS NOT NULL
            AND c.max_length > 6
            and t2.name not in ('text', 'ntext')
        FOR XML PATH('') 
    ), 1, 2, '' )
) c (columnlist)
CROSS APPLY (
    SELECT STUFF((    
        SELECT ' OR [' + c.Name + '] IN (' + @MySearchCriteria + ')' AS [text()]
        FROM sys.columns c
        join sys.types t2 on t2.user_type_id = c.user_type_id
        WHERE t.object_id = c.object_id 
            AND c.collation_name IS NOT NULL
            AND c.max_length > 6
            and t2.name not in ('text', 'ntext')
        FOR XML PATH('') 
    ), 1, 4, '' )
) w (whereclause)
where c.columnlist is not null
ORDER BY t.name

答案 4 :(得分:0)

这是我一直使用的脚本...设置要在顶部搜索的字符串(请参阅注释)并让它运行。

DECLARE @tableName sysname
DECLARE @columnName sysname
DECLARE @value varchar(100)
DECLARE @sql varchar(2000)
DECLARE @sqlPreamble varchar(100)
DECLARE @minLength int;

SET @value = '%SomeString%' -- *** Set this to the value you're searching for *** --
SET @minLength = LEN(REPLACE(@value, '%', ''));

SET @sqlPreamble = 'IF EXISTS (SELECT 1 FROM '

DECLARE theTableCursor CURSOR FAST_FORWARD FOR 
    SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
     WHERE TABLE_SCHEMA = 'dbo' AND TABLE_TYPE = 'BASE TABLE' 
       AND TABLE_NAME != 'dtproperties' AND TABLE_NAME != 'sysdiagrams'
     ORDER BY TABLE_NAME

OPEN theTableCursor
FETCH NEXT FROM theTableCursor INTO @tableName

WHILE @@FETCH_STATUS = 0 -- spin through Table entries
BEGIN
    DECLARE theColumnCursor CURSOR FAST_FORWARD FOR 
        SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
         WHERE TABLE_NAME = @tableName 
           AND (DATA_TYPE = 'nvarchar' OR DATA_TYPE = 'varchar')
           AND (CHARACTER_MAXIMUM_LENGTH >= @minlength OR CHARACTER_MAXIMUM_LENGTH = -1)
         ORDER BY ORDINAL_POSITION

    OPEN theColumnCursor
    FETCH NEXT FROM theColumnCursor INTO @columnName

    WHILE @@FETCH_STATUS = 0 -- spin through Column entries
    BEGIN
        SET @sql = N'[' + @tableName + N'] (nolock) WHERE [' + @columnName + N'] LIKE ''' + @value + 
                   N''') PRINT ''Value found in Table: ' + @tableName + N', Column: ' +  @columnName + N''''
        EXEC (@sqlPreamble + @sql)
        FETCH NEXT FROM theColumnCursor INTO @columnName
    END
    CLOSE theColumnCursor
    DEALLOCATE theColumnCursor

    FETCH NEXT FROM theTableCursor INTO @tableName
END
CLOSE theTableCursor
DEALLOCATE theTableCursor

答案 5 :(得分:0)

此问题在这一点上有点陈旧,大多数答案似乎都错过了在每台服务器上搜索所有服务器和所有数据库的标记。事实证明,我的经理只是试图给我一个不可能和耗时的任务,也就是忙于工作以阻止我,因为我们的团队在重组中解散了,我们都被送到了公司的不同部门。

因此,为了编写一个全面的答案,我将说我现在的方法是创建一个接受逗号分隔的服务器名称列表的控制台应用程序。它将循环遍历所有服务器名称并逐个连接到它们。一旦进入服务器,我将查询并遍历所有用户创建的数据库,并在每个数据库上运行搜索所有表查询。如果找到结果,我会将它们写入存储在具有[ServerName] _ [DatabaseName] _Results标题的目录中的文件,然后使用输出的文件列表来查看和开发以及进一步查询/修复的行动计划。从上面的答案中可以看出,在SQL Server中有一种方法可以做到这一点。

相关问题