Plainly speaking, this article is about searching for a specific value inside all tables and their columns of a SQL database and return list of tables and columns that contains that value.
That being said, third-party search tools have been around for a long time, as well as custom SQL scripts that you can find online for free, for the purpose of searching for specific values within a database. And even if they don’t meet all your requirements, they at least offer a starting point. However, I realized these solutions weren't sufficient for my specific needs, and I found it easier and quicker to write my own script.
This script uses the dynamic SQL queries to search across all user tables and columns. It does allow you to specify filter criteria to search within a particular schema, table, or column or columns of specific data type groups, for example search inside numeric data only. It also includes commands for exporting table data using PowerShell and BCP utilities, which was one of my requirements.
/* -- CREATE A TEST TABLE FOR DEMO IF OBJECT_ID('my_data_search_table', 'U') IS NOT NULL DROP TABLE my_data_search_table; CREATE TABLE my_data_search_table ( id INT, name VARCHAR(100) ); INSERT INTO my_data_search_table (id, name) VALUES (1996, 'dummy'); */SET NOCOUNT ON; USE <Your DB Name Here>; GO -- Drop temp tables if exists IF OBJECT_ID('tempdb..#t_table_columns') IS NOT NULL DROP TABLE #t_table_columns; IF OBJECT_ID('tempdb..#results') IS NOT NULL DROP TABLE #results; GO -- Search string DECLARE @search_value NVARCHAR(4000) = N'dummy'; -- Example search value /* Limit what column data types to search into Here, you an specify whether to search within numeric data types, other data types, or both. This helps in narrowing down the search to relevant fields, potentially speeding up the search process and reducing the load on the database. */
-- valid values are numeric, other or both
DECLARE @search_datatype VARCHAR(10) -- SET @search_datatype = 'numeric' -- Search mode DECLARE @exact_match BIT = 1; -- EXECUTION OPTIONS DECLARE @execute BIT = 1; -- 1 to execute the search queries DECLARE @debug BIT = 0; -- 1 to print only DECLARE @show_progress BIT = 1; -- 1 to print progress messages DECLARE @progress_interval INT = 100; -- Schema, Table, and Column Filtering (NULL means no filter) DECLARE @search_column_name NVARCHAR(1000); DECLARE @search_table_name NVARCHAR(1000); DECLARE @search_schema_name NVARCHAR(1000); -- Dynamic SQL query DECLARE @SQL NVARCHAR(4000); -- If wild card searfch is requested i.e. @exact_match = 0 IF @exact_match = 0 SET @search_value = N'%' + @search_value + N'%'; -- Temp table to store columns metadata SELECT s.name AS [schema_name], t.name AS [table_name], c.name AS [column_name], TYPE_NAME(c.system_type_id) AS [column_type] INTO #t_table_columns FROM sys.tables t JOIN sys.schemas s ON t.schema_id = s.schema_id JOIN sys.columns c ON t.object_id = c.object_id WHERE t.is_ms_shipped = 0 AND TYPE_NAME(c.system_type_id) NOT IN ('image', 'varbinary'); -- Store list of numeric data types into a table variable DECLARE @numeric_types TABLE (name VARCHAR(100)); INSERT INTO @numeric_types (name) VALUES ('bigint'), ('bit'), ('decimal'), ('int'), ('money'), ('numeric'), ('smallint'), ('smallmoney'), ('tinyint'), ('float'), ('real'); IF (@search_datatype = 'numeric' AND ISNUMERIC(@search_value) = 0) BEGIN RAISERROR('Error: Search value (%s) invalid for numeric search.', 16, 1, @search_value) GOTO QUIT END -- Apply filters IF @search_schema_name IS NOT NULL AND @search_schema_name <> '' DELETE FROM #t_table_columns WHERE [schema_name] <> @search_schema_name; IF @search_table_name IS NOT NULL AND @search_table_name <> '' DELETE FROM #t_table_columns WHERE [table_name] <> @search_table_name; IF @search_column_name IS NOT NULL AND @search_column_name <> '' DELETE FROM #t_table_columns WHERE [column_name] <> @search_column_name; IF @search_datatype IS NOT NULL AND @search_datatype <> '' BEGIN IF @search_datatype NOT IN ('numeric', 'other', 'both') BEGIN RAISERROR('Error: Invalid value %s for @search_datatype.', 16, 1, @search_datatype) WITH NOWAIT RAISERROR('Valid values are 1) numeric 2) other 3) both.', 16, 1) WITH NOWAIT GOTO QUIT END ELSE IF @search_datatype = 'numeric' DELETE #t_table_columns FROM #t_table_columns t WHERE NOT EXISTS (SELECT * FROM @numeric_types v WHERE v.name = t.[column_type]) ELSE IF @search_datatype = 'other' DELETE #t_table_columns FROM #t_table_columns t INNER JOIN @numeric_types v ON v.name = t.[column_type] END -- Placeholder for results SELECT TOP 0 * INTO #results FROM #t_table_columns; -- Progress tracking DECLARE @total_columns INT; DECLARE @counter INT = 0; -- Variables for cursor DECLARE @schema_name SYSNAME, @table_name SYSNAME, @column_name SYSNAME, @column_type NVARCHAR(500); -- Declare cursor DECLARE c1 CURSOR STATIC FOR SELECT * FROM #t_table_columns ORDER BY [schema_name], [table_name], [column_name]; OPEN c1; SELECT @total_columns = @@CURSOR_ROWS; FETCH NEXT FROM c1 INTO @schema_name, @table_name, @column_name, @column_type; WHILE @@FETCH_STATUS = 0 BEGIN SET @counter = @counter + 1; -- Progress message IF @counter % @progress_interval = 0 AND @show_progress = 1 RAISERROR('%i columns of %i processed', 10, 1, @counter, @total_columns) WITH NOWAIT; -- Build and execute the search query SET @SQL = N'SELECT TOP 1 ''' + @schema_name + N''' AS [schema_name], ''' + @table_name + N''' AS [table_name], ''' + @column_name + N''' AS [column_name], ''' + @column_type + N''' AS [column_type] FROM ' + QUOTENAME(@schema_name) + N'.' + QUOTENAME(@table_name) + N' WHERE TRY_CAST(' + QUOTENAME(@column_name) + N' AS VARCHAR(8000)) LIKE ''' + @search_value + N''';'; IF @debug = 1 PRINT @SQL; -- Insert into results if match is found IF @execute = 1 BEGIN INSERT INTO #results EXEC (@SQL); IF @@ROWCOUNT > 0 BEGIN PRINT ''; RAISERROR('*** match found ***', 10, 1) WITH NOWAIT; PRINT @SQL; PRINT ''; END END FETCH NEXT FROM c1 INTO @schema_name, @table_name, @column_name, @column_type; END -- Cleanup CLOSE c1; DEALLOCATE c1; -- Display results with export commands SELECT *, 'SELECT COUNT(*) [' + [schema_name] + '.' + [table_name] + '] FROM ' + QUOTENAME([schema_name]) + '.' + QUOTENAME([table_name]) + ' WHERE TRY_CAST(' + QUOTENAME([column_name]) + ' AS VARCHAR(8000)) LIKE ''' + @search_value + N''';' AS [SQL], 'Send-SQLDataToExcel -Connection "Server=' + @@SERVERNAME + ';Trusted_Connection=True;" -MsSQLserver -DataBase "' + DB_NAME() + '" -SQL "select * from ' + QUOTENAME([schema_name]) + '.' + QUOTENAME([table_name]) + '" -Path "$env:USERPROFILEDocuments' + DB_NAME() + '.' + [schema_name] + '.' + [table_name] + '.xlsx"' AS [PS Export], 'BCP ' + QUOTENAME(DB_NAME()) + '.' + QUOTENAME([schema_name]) + '.' + QUOTENAME([table_name]) + ' out %USERPROFILE%Documents' + DB_NAME() + '_' + [schema_name] + '_' + [table_name] + '.txt -c -t, -T -S' + @@SERVERNAME AS [BCP Export] FROM #results ORDER BY [schema_name], [table_name], [column_name]; QUIT: