9

I've looked for an answer to this, but all I can find is people asking how to search all columns of ALL tables in a database for a value. I just want to search all columns for a specific table. The code people have come up with for the all tables question is complicated and hard for me to figure out where exactly it's searching a specific table. Can somebody help me out? Thanks

Jesse
  • 91
  • 1
  • 1
  • 3

9 Answers9

5

Just use some third party tool. There are several that are 100% free and you can’t go wrong with any of these because they will save you a ton of time.

ApexSQL Search (searches both schema and data), SSMS Toolpack (searches schema and data but not free for SQL Server 2012), SQL Search (searches data only).

Frankly, I don’t really understand why even very experienced DBAs bother writing scripts for this if they can use some tool for free that will do the job.

David Smithers
  • 2,354
  • 1
  • 21
  • 13
2

I have no idea of the column types or data values you're searching for, but I'd guess you're trying to search for a substring among multiple text columns.

This is a job for Full-Text Search.

Don't waste time with LIKE '%' + @SearchStr + '%'. You have to write a lot of complicated code to support it, and that solution won't perform well anyway.

Bill Karwin
  • 538,548
  • 86
  • 673
  • 828
  • Setting up full-text search might be overkill if this is just a maintenance or internal task and not a primary (or end user at all) function of the application. – Aaron Bertrand Jun 30 '11 at 20:03
  • It's still easier than coding long, complex stored procedures to run dynamic SQL queris, as in @Decker97's answer. – Bill Karwin Jun 30 '11 at 20:44
  • That depends on the version of SQL Server and whether Full-Text was installed when SQL Server was installed. I haven't tried it but I've read horror stories about shoehorning it in after the fact. Still, are you going to populate a full-text index for every single string column in every single table? Even if this is an internal task? That's an expensive cost/benefit IMHO. – Aaron Bertrand Jun 30 '11 at 20:53
  • I assume MS SQL Server 2005 or later, which includes full text search. It doesn't have to be shoehorned in. The OP doesn't need every column in every table indexed; he specifically said this was for a set of columns in one table. – Bill Karwin Jun 30 '11 at 21:52
  • If we're talking about MS SQL Server 2000: I think it's reasonable to answer any such question with "you should upgrade if you want to use advanced features." – Bill Karwin Jun 30 '11 at 21:54
  • It's an *option* in setup and it is not checked by default, at least in 2008 R2 (see screen shot: http://bit.ly/lj4ZSg ). Also see http://bit.ly/iWsNFq : "Full-text search is an optional component of the SQL Server Database Engine." So I maintain that it is not as simple as you suggest; the user might not have it installed and as I suggested adding it later is non-trivial. You have to use add/remove programs and if you're at a later version than your install media it's trouble. Plus it defaults to manual startup. And for a single table I think that's even more reason why it's overkill. – Aaron Bertrand Jun 30 '11 at 22:12
  • You have a point, but I still think it's usually a better solution the job than the alternatives. – Bill Karwin Jun 30 '11 at 22:24
2

In a similar question I mentioned SQL Workbench/J.

The command that searches the database can also be limited to just one table. So even if that question was PostgreSQL specific, the tool works for SQL Server as well as far as I know.

Community
  • 1
  • 1
1

I modified this stored proc to take a table name as the second parameter and just search that table for the data:

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[SearchOneTable]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[SearchOneTable]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE PROC [dbo].[SearchOneTable]
(
    @SearchStr nvarchar(100) = 'A',
    @TableName nvarchar(256) = 'dbo.Alerts'
)
AS
BEGIN

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

    --SET NOCOUNT ON

    DECLARE @ColumnName nvarchar(128), @SearchStr2 nvarchar(110)
    SET @SearchStr2 = QUOTENAME('%' + @SearchStr + '%','''')
    --SET @SearchStr2 = QUOTENAME(@SearchStr, '''') --exact match
    SET @ColumnName = ' '


        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 ''' + @TableName + '.' + @ColumnName + ''', LEFT(' + @ColumnName + ', 3630) 
                    FROM ' + @TableName + ' (NOLOCK) ' +
                    ' WHERE ' + @ColumnName + ' LIKE ' + @SearchStr2
                )
            END
        END 
    SELECT ColumnName, ColumnValue FROM #Results
END


GO
Decker97
  • 1,643
  • 10
  • 11
  • This doesn't search `text` or `xml` data types, and with a requirement like this who knows what they may have in their tables! – JNK Jun 30 '11 at 19:20
  • The op mentioned that he had found stored procs that search the entire database but was having trouble understanding them. If this is just for use by a developer I don't see the problem with modifying one of the dynamic solutions for a single table as requested. – Decker97 Jun 30 '11 at 21:02
  • Me either, just pointing it out. Note that I didn't downvote you :) – JNK Jun 30 '11 at 21:04
1

Here is a solution that, like @Decker97's approach, figures out from metadata which columns are eligible for string search. Assumes 2005+. Supports text/ntext (though you shouldn't be using those anymore), char/nchar/varchar/nvarchar, and even puts the leading N on the search string where appropriate. Does not support xml columns.

What it does do slightly differently is that it returns a single resultset for each table, not for every single column, so the output is only one row per match no matter how many columns match.

DECLARE @SearchTerm nvarchar(255) = N'foo',
        @TableName  nvarchar(128) = NULL,
        @sql        nvarchar(max) = N'';

;WITH tables(obj_name, obj_id, columns) AS
(
  SELECT obj_name = QUOTENAME(s.name) + N'.' + QUOTENAME(t.name),
         obj_id   = [object_id],
         columns  = (
             SELECT N',' + QUOTENAME(c.name)
               FROM sys.columns AS c 
               WHERE c.[object_id] = t.[object_id]
               ORDER BY c.column_id FOR XML PATH(N''), 
               TYPE).value(N'./text()[1]', N'nvarchar(max)')
    FROM sys.tables AS t INNER JOIN sys.schemas AS s
    ON t.[schema_id] = s.[schema_id]
    WHERE (t.name = @TableName OR @TableName IS NULL)
    AND EXISTS
    (
      SELECT 1 FROM sys.columns AS c
        WHERE c.[object_id] = t.[object_id]
        AND c.system_type_id IN (35,99,167,175,231,239)
    )
)
SELECT @sql += N'SELECT N' + char(39) 
  + REPLACE(obj_name, char(39), char(39) + char(39))
  + char(39) + columns + N' FROM ' + obj_name + N' WHERE ' 
  + STUFF((
      SELECT N' OR ' + QUOTENAME(name) + N' LIKE ' + CASE 
        WHEN c.system_type_id IN (99,231,239) 
        THEN 'N' ELSE N'' END
        + char(39) + N'%' + @SearchTerm + N'%' + char(39)
        FROM sys.columns AS c WHERE c.[object_id] = tables.obj_id
        AND c.system_type_id IN (35,99,167,175,231,239)
        ORDER BY name FOR XML PATH(''), TYPE
     ).value(N'./text()[1]', N'nvarchar(max)') 
      + char(59) + char(13) + char(10), 1, 4, N'')
FROM tables;

PRINT @sql;
--EXEC sys.sp_executeSQL @sql;

Depending on the number of searchable columns in your system, PRINT won't necessarily show you the full command, and you might think there is a bug in the code (or at least a bug in PRINT) that somehow truncates the text. You can increase the size of Results to Text output in SSMS settings, but that still won't be enough. You can use SELECT CONVERT(xml, @sql); instead (see this tip for more info).

If you are on SQL Server 2017 or greater

The new function STRING_AGG() allows you to simplify the code quite a bit, and if you have lots of existing code where you concatenate strings using FOR XML PATH, it can be useful to update those to more modern methods as you revisit them. So here's a version that uses STRING_AGG() in its place:

DECLARE @SearchTerm nvarchar(255) = N'foo',
        @TableName  nvarchar(128) = NULL,
        @sql        nvarchar(max) = N'';

;WITH tables(obj_name, obj_id, columns) AS
(
  SELECT obj_name = QUOTENAME(s.name) + N'.' + QUOTENAME(t.name),
         obj_id   = [object_id],
         columns  = (SELECT STRING_AGG(QUOTENAME(c.name), N',')
           WITHIN GROUP (ORDER BY c.column_id) 
           FROM sys.columns AS c WHERE c.[object_id] = t.[object_id]
             AND c.system_type_id IN (35,99,167,175,231,239))
    FROM sys.tables AS t INNER JOIN sys.schemas AS s
    ON t.[schema_id] = s.[schema_id]
    WHERE (t.name = @TableName OR @TableName IS NULL)
)
SELECT @sql += N'SELECT N' + char(39) 
  + REPLACE(obj_name, char(39), char(39) + char(39))
  + char(39) + N',' + columns + N' FROM ' + obj_name + N' WHERE ' 
  + (SELECT STRING_AGG(QUOTENAME(name) + N' LIKE ' + CASE 
        WHEN c.system_type_id IN (99,231,239) 
        THEN 'N' ELSE N'' END
        + char(39) + N'%' + @SearchTerm + N'%' + char(39),      
     N' OR ') + N';' + char(13) + char(10)
        FROM sys.columns AS c WHERE c.[object_id] = tables.obj_id
        AND c.system_type_id IN (35,99,167,175,231,239))
FROM tables WHERE columns IS NOT NULL;

PRINT @sql;
--EXEC sys.sp_executeSQL @sql;
Aaron Bertrand
  • 272,866
  • 37
  • 466
  • 490
0

This sounds like you just want to know which table and column some data is stored, not that you want to know that during the execution of your code, or change it. I also had this problem and this solved it:

Download your database in SQL format (using phpmyadmin, for example), open it with a text editor and search for the occurrences you want.

Sérgio
  • 1,093
  • 1
  • 9
  • 14
0

Cutesie little work-around that involves a bit less copy-paste, since the command can be produced easily using queries.

Invert the IN operator in a WHERE clause as VALUE IN <fields> (as opposed to the more common use case of FIELD IN <values>).

SELECT col_1, col_2, ... , col_n 
FROM <table>
WHERE CAST(<value> AS varchar(max)) IN 
   (
   CAST(col_1 AS varchar(max)),
   CAST(col_2 AS varchar(max)),
   ...,
   CAST(col_n AS varchar(max))
   )

Since varchar is a pretty malleable data type, this becomes pretty foolproof (you can throw ISNULL/NULLIF to modify as needed), and depending on the use case can probably be used across more than one search value.

A more robust solution, using dynamic execution and PL/SQL would be to write a procedure to dynamically build a view of the target table (via reading e.g. MySQL's information_schema schema, Oracle's SYS schema, etc.), constrained to a where clause containing the input string hard-coded into a series of 'OR'-concatenated/IN clauses for filter conditions.

JJ Ward
  • 99
  • 7
0

I have come across this issue, normally after uploading data from a CSV file where I had to modify the commas ',' in text fields so the data would load properly & once in SQL Server, the need comes to change the modified character back to a comma & it's helpful to be able to search the entire table. Greg Robidoux at mssqltips has posted a Stored Procedure that does just this, searches the Columns of a specified Table for a particular String value. You can find it along with a SPROC that does not use the cursor & more details here:

https://www.mssqltips.com/sqlservertip/1522/searching-and-finding-a-string-value-in-all-columns-in-a-sql-server-table/

I have posted the original SPROC below:

USE master 
GO 

CREATE PROCEDURE dbo.sp_FindStringInTable @stringToFind VARCHAR(100), @schema sysname, @table sysname 
AS 

DECLARE @sqlCommand VARCHAR(8000) 
DECLARE @where VARCHAR(8000) 
DECLARE @columnName sysname 
DECLARE @cursor VARCHAR(8000) 

BEGIN TRY 
   SET @sqlCommand = 'SELECT * FROM [' + @schema + '].[' + @table + '] WHERE' 
   SET @where = '' 

   SET @cursor = 'DECLARE col_cursor CURSOR FOR SELECT COLUMN_NAME 
   FROM ' + DB_NAME() + '.INFORMATION_SCHEMA.COLUMNS 
   WHERE TABLE_SCHEMA = ''' + @schema + ''' 
   AND TABLE_NAME = ''' + @table + ''' 
   AND DATA_TYPE IN (''char'',''nchar'',''ntext'',''nvarchar'',''text'',''varchar'')' 

   EXEC (@cursor) 

   OPEN col_cursor    
   FETCH NEXT FROM col_cursor INTO @columnName    

   WHILE @@FETCH_STATUS = 0    
   BEGIN    
       IF @where <> '' 
           SET @where = @where + ' OR' 

       SET @where = @where + ' [' + @columnName + '] LIKE ''' + @stringToFind + '''' 
       FETCH NEXT FROM col_cursor INTO @columnName    
   END    

   CLOSE col_cursor    
   DEALLOCATE col_cursor  

   SET @sqlCommand = @sqlCommand + @where 
   PRINT @sqlCommand 
   EXEC (@sqlCommand)  
END TRY 
BEGIN CATCH 
   PRINT 'There was an error. Check to make sure object exists.'
   PRINT error_message()

   IF CURSOR_STATUS('variable', 'col_cursor') <> -3 
   BEGIN 
       CLOSE col_cursor    
       DEALLOCATE col_cursor  
   END 
END CATCH 
Sihusir
  • 1
  • 2
  • 1
    Please try and add more details to your answer from the link you provided. In the case that the link ever changes or becomes unavailable, your answer will not be helpful. – dckuehn Sep 01 '16 at 21:26
  • @dckuehn thanks for the feedback, I've added the SPROC – Sihusir Sep 07 '16 at 22:15
0

I've found the best answer is just to select * from the table and then copy & paste into Excel and hit Ctrl+F

SUMguy
  • 1,505
  • 4
  • 31
  • 61