Search all columns of a table for a value? - sql

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

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.

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.

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.

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

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;
More dynamic SQL resources

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.

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

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

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.

Related

how to dynamically identify duplicate records in hundreds of tables

I have spent so long trying to solve my problem to absolutely no avail. I need to create a procedure or loop through whatever you want to call it that scans all tables in all databases in my warehouse and identifies duplicate records. Clearly I want to avoid writing hundreds of queries and manually entering each column name from each table so it needs to be done dynamically. I have 12 or so databases but hundreds of tables and perhaps thousands of columns.
how does one do this?
Something like this might get you started. You can write a cursor to go through the tables in INFORMATION_SCHEMA.TABLES then set the variables from there. Once you run this in a database you'll then need to copy and paste the code that gets generated and run that code to see duplicates.
DECLARE
#column_list NVARCHAR(MAX),
#sql NVARCHAR(MAX),
#table_name SYSNAME,
#table_schema SYSNAME
SELECT #table_schema = 'MySchema', #table_name = 'MyTable'
SELECT #column_list = ''
SELECT #column_list = #column_list + '[' + C.COLUMN_NAME + '], '
FROM INFORMATION_SCHEMA.COLUMNS C
WHERE C.TABLE_NAME = #table_name AND C.TABLE_SCHEMA = #table_schema
SELECT #column_list = SUBSTRING(#column_list, 1, LEN(#column_list) - 1)
SELECT #sql = 'SELECT ''' + #table_schema + ''' AS TABLE_SCHEMA, ''' + #table_name + ''' AS TABLE_NAME, ' + #column_list + ' FROM [' + #table_schema + '].[' + #table_name + '] GROUP BY ' + #column_list + ' HAVING COUNT(*) > 1'
SELECT #sql

How can I easily convert all ntext fields to nvarchar(max) in SQL query?

I would love to be able to write a SQL query, without enumerating the columns, which will return me all the columns, and any ntext column converted to varchar(max). I was wondering if there is a clever way to do this.
This would be great because then I could do the comparison based operators such as UNION, EXCEPT etc. on such queries. The netxt column is not comparable so it fails when using those operators.
My current idea:
Create a function to build the query as dynamic sql. Something similar to this: http://lotsacode.wordpress.com/2010/03/23/sql-server-ntext-cannot-be-selected-as-distinct/
Is there a better way?
Thanks for your input!
NTEXT will be removed from future versions of SQl-Server anyway (along with Image and text), so why not just bite the bullet and change your columns to NVARCHAR(MAX)? It may be costly once, but it will probably be worth it:
ALTER TABLE dbo.T ALTER COLUMN NTextColumn NVARCHAR(MAX) NULL; -- OR NOT NULL
You can generate and execute the script for an entire database using this:
DECLARE #SQL NVARCHAR(MAX) =
( SELECT 'ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(object_id)) + '.' +
QUOTENAME(OBJECT_NAME(object_id)) +
' ALTER COLUMN ' + QUOTENAME(Name) +
' NVARCHAR(MAX) ' +
CASE WHEN is_nullable = 0 THEN 'NOT' ELSE '' END +
' NULL;' + CHAR(13) + 'GO' + CHAR(13)
FROM sys.columns
WHERE system_type_id = 99 --NTEXT
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)');
EXECUTE sp_executesql #SQL;
I used this cursor (and here, there's no set-based alternative, I'm afraid) to do just that:
DECLARE TableCursor CURSOR FAST_FORWARD
FOR
SELECT
t.Name,
c.name,
c.is_nullable,
typ.user_type_id
FROM
sys.columns c
INNER JOIN
sys.tables t ON c.object_id = t.object_id
INNER JOIN
sys.types typ ON c.system_type_id = typ.system_type_id
WHERE
typ.name IN ('text', 'ntext') -- user_type_id: text = 35, ntext = 99
DECLARE #TableName sysname, #ColumnName sysname, #IsNullable BIT, #TypeID INT
OPEN TableCursor
FETCH NEXT FROM TableCursor INTO #TableName, #ColumnName, #IsNullable, #TypeID
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #Stmt NVARCHAR(999)
SET #Stmt = 'ALTER TABLE dbo.[' + #TableName + '] ALTER COLUMN [' + #ColumnName + '] ' +
CASE #TypeID
WHEN 35 THEN ' VARCHAR(MAX) '
WHEN 99 THEN ' NVARCHAR(MAX) '
END +
CASE WHEN #IsNullable = 1 THEN 'NULL' ELSE 'NOT NULL' END
PRINT #Stmt
EXEC (#Stmt)
FETCH NEXT FROM TableCursor INTO #TableName, #ColumnName, #IsNullable, #TypeID
END
CLOSE TableCursor
DEALLOCATE TableCursor
I simplified my code a bit by assuming that all my tables are in the dbo schema - if that's not the case for you, you'd have to include the schema from the sys.schema catalog view, too.
Running this code will turn all text into varchar(max) and all ntext into nvarchar(max) once and for all, and all your issues with text/ntext are gone forever! :-)
Here is my modified version of GarethD's answer above. Had issues with SQL not finding some tables, so I used sys.tables joined with sys.columns. Also, the is_nullable line was incorrect (if the field is not nullable, then you set it to NOT NULL).
DECLARE #SQL NVARCHAR(MAX) = ' ';
SELECT #SQL = #SQL + ' ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(sys.columns.object_id)) + '.' +
QUOTENAME(OBJECT_NAME(sys.columns.object_id)) +
' ALTER COLUMN ' + QUOTENAME(sys.columns.Name) +
' NVARCHAR(MAX) ' +
CASE WHEN is_nullable = 0 THEN 'NOT NULL' ELSE '' END
FROM sys.Tables
inner join sys.columns on sys.tables.object_id = sys.columns.object_id
WHERE sys.columns.system_type_id = 99 ; --NTEXT
EXECUTE sp_executesql #SQL;
GO

Update all SQL Server columns after SQL injection attack

I have been notified that a company website we have has had a problem, and quickly I can see it had a SQL injection attack. I do not manage the website and have no access to the files (and know how I can prevent this in the future) but my current task is to clean the database. It seems there is HTML appended to almost all varchar columns in a Microsoft SQL Server database.
Is there any way in an easy query or function I can run to check all columns for the offending HTML and update the columns in all tables?
For example a column that was:
---------------------------
|Title
---------------------------
|product1
is now
---------------------------
|Title
---------------------------
|product1</title><style>.atpd{position:absolute;clip:rect(400px,auto,auto,400px);}</style><div class=atpd>Apply here <a href=http://abbypaydayloansonline.com >online payday loans</a></div>
Thanks in advance.
Here's one option, assuming the offending text always starts with </title> and that </title> wouldn't naturally appear in the data.
UPDATE dbo.table_name
SET Title = LEFT(Title, CHARINDEX('</title>', Title)-1)
WHERE Title LIKE '%</title>%'
AND Title LIKE '%abbypaydayloansonline.com%';
If you need to do this for multiple columns across multiple tables (assuming, again, that </title> appears first and </title> would never have appeared naturally in the data prior to the incident), you don't need an explicit cursor:
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'';
SELECT #sql = #sql + N'UPDATE ' + QUOTENAME(OBJECT_SCHEMA_NAME([object_id]))
+ '.' + QUOTENAME(OBJECT_NAME([object_id]))
+ ' SET ' + QUOTENAME(name) + ' = LEFT(' + QUOTENAME(name)
+ ', CHARINDEX(''</title>'', ' + QUOTENAME(name) + ')-1)
WHERE ' + QUOTENAME(name) + ' LIKE ''%</title>%''
AND ' + QUOTENAME(name) + ' LIKE ''%abbypaydayloansonline.com%'''
+ ';' + CHAR(13) + CHAR(10)
FROM sys.columns
WHERE OBJECTPROPERTY([object_id], 'IsMsShipped') = 0
AND system_type_id IN (35,99,167,175,231,239,231);
SELECT #sql;
-- EXEC sp_executesql #sql;
Absolutely inspect the output before running it. Note that Management Studio will only show you a small subset of the actual command that will get executed, so you might also want to run this query to see all of the tables and columns that will be checked:
SELECT [table] = QUOTENAME(OBJECT_SCHEMA_NAME([object_id]))
+ '.' + QUOTENAME(OBJECT_NAME([object_id])),
[column] = name
FROM sys.columns
WHERE OBJECTPROPERTY([object_id], 'IsMsShipped') = 0
AND system_type_id IN (35,99,167,175,231,239,231)
ORDER BY [table], [column];
Assuming you don't want to have to type in all your queries manually, I would recommend creating a CURSOR (as much as I hate cursors) and loop through the system tables, something like this:
SELECT t.name AS table_name,
SCHEMA_NAME(schema_id) AS schema_name,
c.name AS column_name
FROM sys.tables AS t
INNER JOIN sys.columns c ON t.OBJECT_ID = c.OBJECT_ID
Then you can run dynamic SQL to do something like #AaronBertrand suggested.
Declare your cursor and fetch your variables into #table_name and #column_name. Then run something like (untested):
DECLARE #updateSQL NVARCHAR(MAX)
SET #updateSQL = 'UPDATE ' + #table_name + ' SET ' + #column_name + ' = LEFT(' + #column_name + ', CHARINDEX(''</title>'', ' + #column_name + ')-1)'
EXECUTE (#updateSQL)
Good luck.

How to find a string inside a entire database?

I have one specific string, such as "123abcd" for example but I don't know the name of the table or even the name of the column inside the table on my SQL Server Database. I want to find it with a select and show all columns of the related string, so I was wondering something like:
select * from Database.dbo.* where * like '%123abcd%'
For obvious reasons it doens't work, but there is a simple way to create a select statement to do something like this?
This will work:
DECLARE #MyValue NVarChar(4000) = 'something';
SELECT S.name SchemaName, T.name TableName
INTO #T
FROM sys.schemas S INNER JOIN
sys.tables T ON S.schema_id = T.schema_id;
WHILE (EXISTS (SELECT * FROM #T)) BEGIN
DECLARE #SQL NVarChar(4000) = 'SELECT * FROM $$TableName WHERE (0 = 1) ';
DECLARE #TableName NVarChar(1000) = (
SELECT TOP 1 SchemaName + '.' + TableName FROM #T
);
SELECT #SQL = REPLACE(#SQL, '$$TableName', #TableName);
DECLARE #Cols NVarChar(4000) = '';
SELECT
#Cols = COALESCE(#Cols + 'OR CONVERT(NVarChar(4000), ', '') + C.name + ') = CONVERT(NVarChar(4000), ''$$MyValue'') '
FROM sys.columns C
WHERE C.object_id = OBJECT_ID(#TableName);
SELECT #Cols = REPLACE(#Cols, '$$MyValue', #MyValue);
SELECT #SQL = #SQL + #Cols;
EXECUTE(#SQL);
DELETE FROM #T
WHERE SchemaName + '.' + TableName = #TableName;
END;
DROP TABLE #T;
A couple caveats, though. First, this is outrageously slow and non-optimized. All values are being converted to nvarchar simply so that they can be compared without error. You may run into problems with values like datetime not converting as expected and therefore not being matched when they should be (false negatives).
The WHERE (0 = 1) is there to make building the OR clause easier. If there are not matches you won't get any rows back.
Here are couple more free tools that can be used for this. Both work as SSMS addins.
ApexSQL Search – 100% free - searches both schema and data in tables. Has couple more useful options such as dependency tracking…
SSMS Tools pack – free for all versions except SQL 2012 – doesn’t look as advanced as previous one but has a lot of other cool features.
I think you have to options:
Build a dynamic SQL using sys.tables and sys.columns to perform the search (example here).
Use any program that have this function. An example of this is SQL Workbench (free).
create procedure usp_find_string(#string as varchar(1000))
as
begin
declare #mincounter as int
declare #maxcounter as int
declare #stmtquery as varchar(1000)
set #stmtquery=''
create table #tmp(tablename varchar(128),columnname varchar(128),rowid int identity)
create table #tablelist(tablename varchar(128),columnname varchar(128))
declare #tmp table(name varchar(128))
declare #tablename as varchar(128)
declare #columnname as varchar(128)
insert into #tmp(tablename,columnname)
select a.name,b.name as columnname from sysobjects a
inner join syscolumns b on a.name=object_name(b.id)
where a.type='u'
and b.xtype in(select xtype from systypes
where name='text' or name='ntext' or name='varchar' or name='nvarchar' or name='char' or name='nchar')
order by a.name
select #maxcounter=max(rowid),#mincounter=min(rowid) from #tmp
while(#mincounter <= #maxcounter )
begin
select #tablename=tablename, #columnname=columnname from #tmp where rowid=#mincounter
set #stmtquery ='select top 1 ' + '[' +#columnname+']' + ' from ' + '['+#tablename+']' + ' where ' + '['+#columnname+']' + ' like ' + '''%' + #string + '%'''
insert into #tmp(name) exec(#stmtquery)
if ##rowcount >0
insert into #tablelist values(#tablename,#columnname)
set #mincounter=#mincounter +1
end
select * from #tablelist
end
In oracle you can use the following sql command to generate the sql commands you need:
select
"select * "
" from "||table_name||
" where "||column_name||" like '%123abcd%' ;" as sql_command
from user_tab_columns
where data_type='VARCHAR2';
Common Resource Grep (crgrep) will search for string matches in tables/columns by name or content and supports a number of DBs, including SQLServer, Oracle and others. Full wild-carding and other useful options.
It's opensource (I'm the author).
http://sourceforge.net/projects/crgrep/
I usually use information_Schema.columns and information_schema.tables, although like #yuck said, sys.tables and sys.columns are shorter to type.
In a loop, concatenate these
#sql = #sql + 'select' + column_name +
' from ' + table_name +
' where ' + column_name ' like ''%''+value+''%' UNION
Then execute the resulting sql.
Here is an easy and convenient cursor based solution
DECLARE
#search_string VARCHAR(100),
#table_name SYSNAME,
#table_id INT,
#column_name SYSNAME,
#sql_string VARCHAR(2000)
SET #search_string = 'StringtoSearch'
DECLARE tables_cur CURSOR FOR SELECT name, object_id FROM sys.objects WHERE type = 'U'
OPEN tables_cur
FETCH NEXT FROM tables_cur INTO #table_name, #table_id
WHILE (##FETCH_STATUS = 0)
BEGIN
DECLARE columns_cur CURSOR FOR SELECT name FROM sys.columns WHERE object_id = #table_id
AND system_type_id IN (167, 175, 231, 239)
OPEN columns_cur
FETCH NEXT FROM columns_cur INTO #column_name
WHILE (##FETCH_STATUS = 0)
BEGIN
SET #sql_string = 'IF EXISTS (SELECT * FROM ' + #table_name + ' WHERE [' + #column_name + ']
LIKE ''%' + #search_string + '%'') PRINT ''' + #table_name + ', ' + #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_name, #table_id
END
CLOSE tables_cur
DEALLOCATE tables_cur

Search for a string in all tables, rows and columns of a DB

I am lost in a big database and I am not able to find where the data I get comes from. I was wondering if it is possible with SQL Server 2005 to search for a string in all tables, rows and columns of a database?
Does anybody has an idea if it is possible and how?
This code should do it in SQL 2005, but a few caveats:
It is RIDICULOUSLY slow. I tested it on a small database that I have with only a handful of tables and it took many minutes to complete. If your database is so big that you can't understand it then this will probably be unusable anyway.
I wrote this off the cuff. I didn't put in any error handling and there might be some other sloppiness especially since I don't use cursors often. For example, I think there's a way to refresh the columns cursor instead of closing/deallocating/recreating it every time.
If you can't understand the database or don't know where stuff is coming from, then you should probably find someone who does. Even if you can find where the data is, it might be duplicated somewhere or there might be other aspects of the database that you don't understand. If no one in your company understands the database then you're in a pretty big mess.
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
I’d suggest you find yourself a 3rd party tool for this such as ApexSQL Search (there are probably others out there too but I use this one because it’s free).
If you really want to go the SQL way you can try using stored procedure created by
Sorna Kumar Muthuraj – copied code is below. Just execute this stored procedure for all tables in your schema (easy with dynamics 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
Although the solutions presented before are valid and work, I humbly offer a code that's cleaner, more elegant, and with better performance, at least as I see it.
Firstly, one may ask: Why would anyone ever need a code snippet to globally and blindly look for a string? Hey, they already invented fulltext, don't you know?
My answer: my mainly work is at systems integration projects, and discovering where the data is written is important whenever I'm learning a new and undocummented database, which seldom happens.
Also, the code I present is a stripped down version of a more powerful and dangerous script that searches and REPLACES text throughout the database.
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
If you are "getting data" from an application, the sensible thing would be to use the profiler and profile the database while running the application. Trace it, then search the results for that string.
The SSMS Tools PACK Add-In (Add-On) for Microsoft SQL Server Management Studio and Microsoft SQL Server Management Studio Express will do exactly what you need. On larger database it takes some time to search, but that is to be expected. It also includes a ton of cool features that should have be included with SQL Server Management Studio in the first place. Give it a try www.ssmstoolspack.com/
You do need to have SP2 for SQL Server Management Studio installed to run the tools.
I adapted a script originally written by Narayana Vyas Kondreddi in 2002. I changed the where clause to check text/ntext fields as well, by using patindex rather than like. I also changed the results table slightly. Unreasonably, I changed variable names, and aligned as I prefer (no disrespect to Mr. Kondretti). The user may want to change the data types searched. I used a global table to allow querying mid-processing, but a permanent table might be a smarter way to go.
/* 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
;
Other answers posted already may work equally well or better, but I haven't used them. However, the following SQL I have used, and it really helped me out when I was trying to reverse-engineer a big system with a huge (and very unorganzied) SQL Server database.
This isn't my code. I wish I could credit the original author, but I can't find the link to the article anymore :(
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
Actually Im agree with MikeW (+1) it's better to use profiler for this case.
Anyway, if you really need to grab all (n)varchar columns in db and make a search. See below.
I suppose to use INFORMATION_SCHEMA.Tables + dynamic SQL.
The plain search:
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
and one with LIKE clause:
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
#NLwino, yery good query with a few errors for keyword usage. I had to modify it a little to wrap the keywords with [ ] and also look char and ntext columns.
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)
I ran it on 2.5 GB database and it came back in 51 seconds
I think this can be an easiest way to find a string in all rows of your database -without using cursors and FOR XML-.
CREATE PROCEDURE SPFindAll (#find VARCHAR(max) = '')
AS
BEGIN
SET NOCOUNT ON;
--
DECLARE #query VARCHAR(max) = ''
SELECT #query = #query +
CASE
WHEN #query = '' THEN ''
ELSE ' UNION ALL '
END +
'SELECT ''' + s.name + ''' As schemaName, ''' + t.name + ''' As tableName, ''' + c.name + ''' As ColumnName, [' + c.name + '] COLLATE DATABASE_DEFAULT As [Data] FROM [' + s.name + '].[' + t.name + '] WHERE [' + c.name + '] Like ''%' + #find + '%'''
FROM
sys.schemas s
INNER JOIN
sys.tables t ON s.[schema_id] = t.[schema_id]
INNER JOIN
sys.columns c ON t.[object_id] = c.[object_id]
INNER JOIN
sys.types ty ON c.user_type_id = ty.user_type_id
WHERE
ty.name LIKE '%char'
EXEC(#query)
END
By creating this stored procedure you can run it for any string you want to find like this:
EXEC SPFindAll 'Hello World'
The result will be like this:
schemaName | tableName | columnName | Data
-----------+-----------+------------+-----------------------
schema1 | Table1 | Column1 | Hello World
schema1 | Table1 | Column1 | Hello World!
schema1 | Table2 | Column1 | I say "Hello World".
schema1 | Table2 | Column2 | Hello World
This uses no cursors or anything like that, just one dynamic query.
Also note that this uses LIKE. Since that happened to be what I needed. It works for all schemas, all tables and only query's those columns that are NVARCHAR or VARCHAR even if they have 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)
The output gives you the table, column and value. Time to execute on a small database was ~3 seconds, had about 3000 results.
/*
This procedure is for finding any string or date in all tables
if search string is date, its format should be yyyy-MM-dd
eg. 2011-07-05
*/
-- ================================================
-- Exec SearchInTables 'f6f56934-a5d4-4967-80a1-1a2223b9c7b1'
-- ================================================
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <Joshy,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
ALTER PROCEDURE SearchInTables
#myValue nvarchar(1000)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
DECLARE #searchsql nvarchar(max)
DECLARE #table_name nvarchar(1000)
DECLARE #Schema_name nvarchar(1000)
DECLARE #ParmDefinition nvarchar(500)
DECLARE #XMLIn nvarchar(max)
SET #ParmDefinition = N'#XMLOut varchar(max) OUTPUT'
SELECT A.name,b.name
FROM sys.tables A
INNER JOIN sys.schemas B ON A.schema_id=B.schema_id
WHERE A.name like 'tbl_Tax_Sections'
DECLARE tables_cur CURSOR FOR
SELECT A.name,b.name FOM sys.tables A
INNER JOIN sys.schemas B ON A.schema_id=B.schema_id
WHERE A.type = 'U'
OPEN tables_cur
FETCH NEXT FROM tables_cur INTO #table_name , #Schema_name
WHILE (##FETCH_STATUS = 0)
BEGIN
SET #searchsql ='SELECT #XMLOut=(SELECT PATINDEX(''%'+ #myValue+ '%'''
SET #searchsql =#searchsql + ', (SELECT * FROM '+#Schema_name+'.'+#table_name+' FOR XML AUTO) ))'
--print #searchsql
EXEC sp_executesql #searchsql, #ParmDefinition, #XMLOut=#XMLIn OUTPUT
--print #XMLIn
IF #XMLIn <> 0 PRINT #Schema_name+'.'+#table_name
FETCH NEXT FROM tables_cur INTO #table_name , #Schema_name
END
CLOSE tables_cur
DEALLOCATE tables_cur
RETURN
END
GO
To "find where the data I get comes from", you can start SQL Profiler, start your report or application, and you will see all the queries issued against your database.
Or, you can use my query here, should be simpler then having to create sProcs for each DB you want to search: FullParam SQL Blog
/* Reto Egeter, fullparam.wordpress.com */
DECLARE #SearchStrTableName nvarchar(255), #SearchStrColumnName nvarchar(255), #SearchStrColumnValue nvarchar(255), #SearchStrInXML bit, #FullRowResult bit, #FullRowResultRows int
SET #SearchStrColumnValue = '%searchthis%' /* use LIKE syntax */
SET #FullRowResult = 1
SET #FullRowResultRows = 3
SET #SearchStrTableName = NULL /* NULL for all tables, uses LIKE syntax */
SET #SearchStrColumnName = NULL /* NULL for all columns, uses LIKE syntax */
SET #SearchStrInXML = 0 /* Searching XML data may be slow */
IF OBJECT_ID('tempdb..#Results') IS NOT NULL DROP TABLE #Results
CREATE TABLE #Results (TableName nvarchar(128), ColumnName nvarchar(128), ColumnValue nvarchar(max),ColumnType nvarchar(20))
SET NOCOUNT ON
DECLARE #TableName nvarchar(256) = '',#ColumnName nvarchar(128),#ColumnType nvarchar(20), #QuotedSearchStrColumnValue nvarchar(110), #QuotedSearchStrColumnName nvarchar(110)
SET #QuotedSearchStrColumnValue = QUOTENAME(#SearchStrColumnValue,'''')
DECLARE #ColumnNameTable TABLE (COLUMN_NAME nvarchar(128),DATA_TYPE nvarchar(20))
WHILE #TableName IS NOT NULL
BEGIN
SET #TableName =
(
SELECT MIN(QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME))
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
AND TABLE_NAME LIKE COALESCE(#SearchStrTableName,TABLE_NAME)
AND QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME) > #TableName
AND OBJECTPROPERTY(OBJECT_ID(QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME)), 'IsMSShipped') = 0
)
IF #TableName IS NOT NULL
BEGIN
DECLARE #sql VARCHAR(MAX)
SET #sql = 'SELECT QUOTENAME(COLUMN_NAME),DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = PARSENAME(''' + #TableName + ''', 2)
AND TABLE_NAME = PARSENAME(''' + #TableName + ''', 1)
AND DATA_TYPE IN (' + CASE WHEN ISNUMERIC(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(#SearchStrColumnValue,'%',''),'_',''),'[',''),']',''),'-','')) = 1 THEN '''tinyint'',''int'',''smallint'',''bigint'',''numeric'',''decimal'',''smallmoney'',''money'',' ELSE '' END + '''char'',''varchar'',''nchar'',''nvarchar'',''timestamp'',''uniqueidentifier''' + CASE #SearchStrInXML WHEN 1 THEN ',''xml''' ELSE '' END + ')
AND COLUMN_NAME LIKE COALESCE(' + CASE WHEN #SearchStrColumnName IS NULL THEN 'NULL' ELSE '''' + #SearchStrColumnName + '''' END + ',COLUMN_NAME)'
INSERT INTO #ColumnNameTable
EXEC (#sql)
WHILE EXISTS (SELECT TOP 1 COLUMN_NAME FROM #ColumnNameTable)
BEGIN
PRINT #ColumnName
SELECT TOP 1 #ColumnName = COLUMN_NAME,#ColumnType = DATA_TYPE FROM #ColumnNameTable
SET #sql = 'SELECT ''' + #TableName + ''',''' + #ColumnName + ''',' + CASE #ColumnType WHEN 'xml' THEN 'LEFT(CAST(' + #ColumnName + ' AS nvarchar(MAX)), 4096),'''
WHEN 'timestamp' THEN 'master.dbo.fn_varbintohexstr('+ #ColumnName + '),'''
ELSE 'LEFT(' + #ColumnName + ', 4096),''' END + #ColumnType + '''
FROM ' + #TableName + ' (NOLOCK) ' +
' WHERE ' + CASE #ColumnType WHEN 'xml' THEN 'CAST(' + #ColumnName + ' AS nvarchar(MAX))'
WHEN 'timestamp' THEN 'master.dbo.fn_varbintohexstr('+ #ColumnName + ')'
ELSE #ColumnName END + ' LIKE ' + #QuotedSearchStrColumnValue
INSERT INTO #Results
EXEC(#sql)
IF ##ROWCOUNT > 0 IF #FullRowResult = 1
BEGIN
SET #sql = 'SELECT TOP ' + CAST(#FullRowResultRows AS VARCHAR(3)) + ' ''' + #TableName + ''' AS [TableFound],''' + #ColumnName + ''' AS [ColumnFound],''FullRow>'' AS [FullRow>],*' +
' FROM ' + #TableName + ' (NOLOCK) ' +
' WHERE ' + CASE #ColumnType WHEN 'xml' THEN 'CAST(' + #ColumnName + ' AS nvarchar(MAX))'
WHEN 'timestamp' THEN 'master.dbo.fn_varbintohexstr('+ #ColumnName + ')'
ELSE #ColumnName END + ' LIKE ' + #QuotedSearchStrColumnValue
EXEC(#sql)
END
DELETE FROM #ColumnNameTable WHERE COLUMN_NAME = #ColumnName
END
END
END
SET NOCOUNT OFF
SELECT TableName, ColumnName, ColumnValue, ColumnType, COUNT(*) AS Count FROM #Results
GROUP BY TableName, ColumnName, ColumnValue, ColumnType
This query can do the thing for you.
DECLARE
#search_string VARCHAR(100),
#table_name SYSNAME,
#table_id INT,
#column_name SYSNAME,
#sql_string VARCHAR(2000)
SET #search_string = 'StringtoSearch'
DECLARE tables_cur CURSOR FOR SELECT ss.name +'.'+ so.name [name], object_id FROM sys.objects so INNER JOIN sys.schemas ss ON so.schema_id = ss.schema_id WHERE type = 'U'
OPEN tables_cur
FETCH NEXT FROM tables_cur INTO #table_name, #table_id
WHILE (##FETCH_STATUS = 0)
BEGIN
DECLARE columns_cur CURSOR FOR SELECT name FROM sys.columns WHERE object_id = #table_id
AND system_type_id IN (167, 175, 231, 239, 99)
OPEN columns_cur
FETCH NEXT FROM columns_cur INTO #column_name
WHILE (##FETCH_STATUS = 0)
BEGIN
SET #sql_string = 'IF EXISTS (SELECT * FROM ' + #table_name + ' WHERE [' + #column_name + ']
LIKE ''%' + #search_string + '%'') PRINT ''' + #table_name + ', ' + #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_name, #table_id
END
CLOSE tables_cur
DEALLOCATE tables_cur