Search and Find String Value in all SQL Server Table Columns

Problem

As a DBA, sometimes there is a need to find if a string value exists in any column in your table of your SQL Server database. Although there are system stored procedures that do a “for each database” or a “for each table”, there is not a system stored procedure that does a “for each column” from Microsoft. So, trying to find a value in any column within your database requires a query to look through each column to be searched using an OR operator between each column. Is there any way this can be dynamically generated?

Solution

Once again, this is where T-SQL comes in handy, along with the use of system tables or system views. The code below searches for a value in all text data type columns such as (char, nchar, ntext, nvarchar, text, and varchar).

The stored procedure gets created in the master database so you can use it in any database. It takes three parameters:

  • stringToFind – this is the search string you are looking for. This could be a simple value as ‘test’ or you can also use the % wildcard such as ‘%test%’, ‘%test’ or ‘test%’.
  • schema – this is the schema owner of the object.
  • table – this is the table name you want to search, the procedure will search all char, nchar, ntext, nvarchar, text and varchar columns in the base table.

Option 1 – Uses a Cursor (Original Method)

The first thing is to create this stored procedure. Copy the SQL statement below and execute it in a query window in SQL Server Management Studio (SSMS).

USE master 
GO 
 
CREATE PROCEDURE dbo.sp_FindStringInTable @stringToFind VARCHAR(100), @schema sysname, @table sysname 
AS 
 
SET NOCOUNT ON
 
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 

Option 2 – Does Not Use a Cursor (new method)

This is a newer and simpler approach and eliminates the need for a cursor.

We recently ran into an issue that I worked on with Russ Mittler. The database setting had cursors set to local so, the script above did not work. The setting needed to be set to global.  Use the below code to check the setting:

SELECT is_local_cursor_default FROM sys.databases WHERE name = DB_NAME()

Based on that issue, I wrote a different version of this stored procedure that does not use cursors:

USE master
GO
 
CREATE PROCEDURE dbo.sp_FindStringInTable @stringToFind VARCHAR(max), @schema sysname, @table sysname 
AS
 
SET NOCOUNT ON
 
BEGIN TRY
   DECLARE @sqlCommand varchar(max) = 'SELECT * FROM [' + @schema + '].[' + @table + '] WHERE ' 
    
   SELECT @sqlCommand = @sqlCommand + '[' + COLUMN_NAME + '] LIKE ''' + @stringToFind + ''' OR '
   FROM INFORMATION_SCHEMA.COLUMNS 
   WHERE TABLE_SCHEMA = @schema
   AND TABLE_NAME = @table 
   AND DATA_TYPE IN ('char','nchar','ntext','nvarchar','text','varchar')
   SET @sqlCommand = left(@sqlCommand,len(@sqlCommand)-3)
   EXEC (@sqlCommand)
   PRINT @sqlCommand
END TRY
 
BEGIN CATCH 
   PRINT 'There was an error. Check to make sure object exists.'
   PRINT error_message()
END CATCH 

After creating the stored procedure, make sure to mark it as a system stored procedure.

USE master
GO
 
EXEC sys.sp_MS_marksystemobject sp_FindStringInTable
GO

Example Uses

After creating the stored procedure, you can run some tests with the following SQL queries.

Below are some tests against the AdventureWorks database.

1: Find the value ‘Irv%’ in the Person.Address table.

USE AdventureWorks 
GO 
EXEC sp_FindStringInTable 'Irv%', 'Person', 'Address' 
search 1

2: Find the value ‘%land%’ in the Person.Address table.

USE AdventureWorks 
GO 
EXEC sp_FindStringInTable '%land%', 'Person', 'Address' 
search 2

3: Find the value ‘%land%’ in the Person.Contact table.

USE AdventureWorks 
GO 
EXEC sp_FindStringInTable '%land%', 'Person', 'Contact'
search 3

Another Version to Show Columns that Match

Similarly, the following is another version that shows which columns match, along with the data from the table.

CREATE PROCEDURE dbo.sp_FindStringInTable_with_flag @stringToFind VARCHAR(max), @schema sysname, @table sysname 
AS
 
SET NOCOUNT ON
 
BEGIN TRY
   DECLARE @sqlCommand varchar(max) = 'SELECT ' 
 
   SELECT @sqlCommand = @sqlCommand + 'case when [' + COLUMN_NAME + '] LIKE ''' + @stringToFind + ''' then 1 else 0 end as ' + COLUMN_NAME + '_found, ' 
   FROM INFORMATION_SCHEMA.COLUMNS 
   WHERE TABLE_SCHEMA = @schema
   AND TABLE_NAME = @table 
   AND DATA_TYPE IN ('char','nchar','ntext','nvarchar','text','varchar')
 
   SELECT @sqlCommand = @sqlCommand + ' * FROM [' + @schema + '].[' + @table + '] WHERE '
    
   SELECT @sqlCommand = @sqlCommand + '[' + COLUMN_NAME + '] LIKE ''' + @stringToFind + ''' OR '
   FROM INFORMATION_SCHEMA.COLUMNS 
   WHERE TABLE_SCHEMA = @schema
   AND TABLE_NAME = @table 
   AND DATA_TYPE IN ('char','nchar','ntext','nvarchar','text','varchar')
 
   SET @sqlCommand = left(@sqlCommand,len(@sqlCommand)-3)
   EXEC (@sqlCommand)
   PRINT @sqlCommand
END TRY
 
BEGIN CATCH 
   PRINT 'There was an error. Check to make sure object exists.'
   PRINT error_message()
END CATCH 

After creating the stored procedure, make sure to mark it as a system stored procedure.

USE master
GO
 
EXEC sys.sp_MS_marksystemobject sp_FindStringInTable_with_flag
GO

We can run as follows:

USE AdventureWorks 
GO 
EXEC sp_FindStringInTable_with_flag 'Irv%', 'Person', 'Address' 

Here are the results.

search 3

Summary

That’s all there is to it. Once created, you can use this against any table and any database on your server.

Next Steps

  • Add this to your scripts toolbox if you need to search through a table to find a certain value.
  • Remember: Using the % in front of the value (such as ‘%land’) forces SQL Server to scan the table and not use the indexes. Also, since you are searching through all columns using an OR clause, it is a good bet that you will do a table scan. Be aware of this on large tables.

3 Comments

  1. Hi to all,

    I use the folling code! Has anyone an idea to search not only for one “stringtoFind” but to use In(Select Searchstring from table)
    I have no idea at the moment!

    Thanks and best regards
    Dieter

    USE master
    GO

    CREATE PROCEDURE 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.’
    IF CURSOR_STATUS(‘variable’, ‘col_cursor’) <> -3
    BEGIN
    CLOSE col_cursor
    DEALLOCATE col_cursor
    END
    END CATCH

  2. • Performance enhancement-
    We can easily search more efficient using the collation.

    SELECT
    FROM table_name
    WHERE upper(column_name) COLLATE Latin1_General_100_BIN2 LIKE upper(@str);

    Source:
    https://www.sqlservergeeks.com/resources/magazine/SSG_Magazine_June_2021.pdf

    for this SP just replace -
    ‘[‘ + upper(column_name) + ‘] COLLATE Latin1_General_100_BIN2 LIKE ”’ + upper(@stringToFind) + ”’ OR ‘

    • For the comment about receiving several tables using XML;
    Why exactly do we keep 2005 compatibility ?
    cross apply string_split(@tables, ‘,’) would work on any server you have there… wouldn’t it?

Leave a Reply

Your email address will not be published. Required fields are marked *