SQL Cursor to use Table and Field Names from Temp Table - sql

I'll preface this by letting you all know that I promised myself a few years ago never to use a cursor in SQL where it's not needed. Unfortunately I think I may have to use one in my current situation but it's been so long that I'm struggling to remember the correct syntax.
Basically, I've got a problem with CONVERT_IMPLICIT happening in queries because I have data types that are different for the same field in different tables so I'd like to eventually convert these to int. But to do this I need to check whether all data can be converted to int or not to see how big the job is.
I've got the query below which gives me a list of all tables in the database that contain the relevant field in a list;
IF OBJECT_ID('tempdb..#BaseData') IS NOT NULL DROP TABLE #BaseData
GO
CREATE TABLE #BaseData (Table_Name varchar(100), Field_Name varchar(100), Data_Type_Desc varchar(20), Data_Max_Length int, Convertible bit)
DECLARE #FieldName varchar(20); SET #FieldName = 'TestFieldName'
INSERT INTO #BaseData (Table_Name, Field_Name, Data_Type_Desc, Data_Max_Length)
SELECT
o.name ,c.name ,t.name ,t.max_length
FROM sys.columns c
JOIN sys.types t
ON c.user_type_id = t.user_type_id
JOIN sys.objects o
ON c.object_id = o.object_id
WHERE c.name LIKE '%' + #FieldName + '%'
AND o.type_desc = 'USER_TABLE'
Which gives results like this;
Table_Name Field_Name Data_Type_Desc Data_Max_Length Convertible
Table1 TestFieldName varchar 8000 NULL
Table2 TestFieldName nvarchar 8000 NULL
Table3 TestFieldName int 4 NULL
Table4 TestFieldName varchar 8000 NULL
Table5 TestFieldName varchar 8000 NULL
What I'd like to do is to check if all data in the relevant table & field can be converted to an int and update the 'convertible' field (1 if there's data that can't be converted, 0 if the data is fine). I've got the following calculation which works perfectly fine;
'SELECT
CASE
WHEN COUNT(' + #FieldName + ') - SUM(ISNUMERIC(' + #FieldName + ')) > 0
THEN 1
ELSE 0
END
FROM ' + #TableName
And gives the result that I'm after. But I'm struggling to get to the correct syntax to create the cursor which will look at each row in my temp table and run this SQL accordingly. It then needs to update the final column of the temp table with the result of the query (1 or 0).
This will have to be run on a couple of hundred databases which is why I need this list to be dynamic, there may well be custom tables in some databases (in fact, it's pretty likely).
If anybody could give any guidance it would be greatly appreciated.
Thanks

I made a couple of changes to your original query but here is something that should work. I have done similar things in the past :-)
Changes:
Added schema to the source table - my test database had matches in multiple schemas
Changed datatypes to sysname, smallint to match table definitions or names could get truncated
IF OBJECT_ID('tempdb..#BaseData') IS NOT NULL DROP TABLE #BaseData;
GO
CREATE TABLE #BaseData (Schema_Name sysname, Table_Name sysname, Field_Name sysname, Data_Type_Desc sysname, Data_Max_Length smallint, Convertible bit);
DECLARE #FieldName varchar(20); SET #FieldName = 'TestFieldName';
INSERT INTO #BaseData (Schema_Name, Table_Name, Field_Name, Data_Type_Desc, Data_Max_Length)
SELECT
s.name, o.name ,c.name ,t.name ,t.max_length
FROM sys.columns c
JOIN sys.types t
ON c.user_type_id = t.user_type_id
JOIN sys.objects o
ON c.object_id = o.object_id
JOIN sys.schemas s ON o.schema_id=s.schema_id
WHERE c.name LIKE '%' + #FieldName + '%'
AND o.type_desc = 'USER_TABLE';
--select * from #BaseData;
DECLARE #sName sysname,
#tName sysname,
#fName sysname,
#sql VARCHAR(MAX);
DECLARE c CURSOR LOCAL FAST_FORWARD FOR
SELECT Schema_Name,
Table_Name,
Field_Name
FROM #BaseData;
OPEN c;
FETCH NEXT FROM c INTO #sName, #tName, #fName;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = 'UPDATE #BaseData SET Convertible =
(SELECT
CASE
WHEN COUNT(' + #fName + ') - SUM(ISNUMERIC(' + #fName + ')) > 0
THEN 1
ELSE 0
END Convertible
FROM ' + #sName + '.' + #tName + ')
FROM #BaseData WHERE Schema_Name = ''' + #sName + ''' AND Table_Name = ''' + #tName + ''' AND Field_Name = ''' + #fName + '''';
--select #sql;
EXEC(#sql);
FETCH NEXT FROM c INTO #sName, #tName, #fName;
END
CLOSE c;
DEALLOCATE c;
select *
from #BaseData;

If I understand your question, I would do something like this to identify those records that do not cast as an [int].
You didn't state which version of SQL Server you're using; TRY_CAST and TRY_CONVERT are 2012 or later.
DECLARE #test AS TABLE ( [field] [sysname] );
INSERT INTO #test
( [field] )
VALUES ( N'1' ),
( N'a' );
SELECT [field]
FROM #test
WHERE TRY_CAST([field] AS [INT]) IS NULL;
-- this is the basic sql syntax for a cursor
CURSOR (https://msdn.microsoft.com/en-us/library/ms180169.aspx)
DECLARE #parameter [sysname];
BEGIN
DECLARE [field_cursor] CURSOR
FOR
SELECT [value]
FROM [<schema>].[<table>];
OPEN [field_cursor];
FETCH NEXT FROM [field_cursor] INTO #parameter;
WHILE ##FETCH_STATUS = 0
BEGIN
-- do something really interesting here
FETCH NEXT FROM [field_cursor] INTO #parameter;
END;
CLOSE [field_cursor];
DEALLOCATE [field_cursor];
END;

I wasn't able to test this but it should do what you're looking for. Just plop this in after you create your temp table:
DECLARE #tName VARCHAR(20),
#fName VARCHAR(20),
#dType VARCHAR(20),
#dLength INT,
#sql VARCHAR(MAX);
DECLARE c CURSOR LOCAL FAST_FORWARD FOR
SELECT Table_Name,
Field_Name,
Data_Type_Desc,
Data_Max_Length
FROM #BaseData;
OPEN c;
FETCH NEXT FROM c INTO #tName, #fName, #dType, #dLength;
WHILE ##FETCH_STATUS = 0
BEGIN
IF((COUNT(#FieldName) - SUM(ISNUMERIC(#FieldName))) > 0)
BEGIN
SET #sql = 'UPDATE ' + #tName + ' SET Convertible = 1 WHERE Table_Name = ''' + #tName + '''';
END
ELSE
BEGIN
SET #sql = 'UPDATE ' + #tName + ' SET Convertible = 0 WHERE Table_Name = ''' + #tName + '''';
END
EXEC(#sql);
FETCH NEXT FROM c INTO #TableName, #FieldName, #DataType, #DataLength;
END
CLOSE c;
DEALLOCATE c;

Related

Search for a particular string in an entire database [duplicate]

I know it's possible, but I don't know how.
I need to search an SQL Server database for all mentions of a specific string.
For example: I would like to search all tables, views, functions, stored procedures, ... for string "tblEmployes" (not data within the tables).
One of the reasons I need this is I would like to remove some extra data tables that are created, but I am afraid that they are maybe used somewhere in procedures or functions.
This will search every column of every table in a specific database. Create the stored procedure on the database that you want to search in.
The Ten Most Asked SQL Server Questions And Their Answers:
CREATE PROCEDURE FindMyData_String
#DataToFind NVARCHAR(4000),
#ExactMatch BIT = 0
AS
SET NOCOUNT ON
DECLARE #Temp TABLE(RowId INT IDENTITY(1,1), SchemaName sysname, TableName sysname, ColumnName SysName, DataType VARCHAR(100), DataFound BIT)
INSERT INTO #Temp(TableName,SchemaName, ColumnName, DataType)
SELECT C.Table_Name,C.TABLE_SCHEMA, C.Column_Name, C.Data_Type
FROM Information_Schema.Columns AS C
INNER Join Information_Schema.Tables AS T
ON C.Table_Name = T.Table_Name
AND C.TABLE_SCHEMA = T.TABLE_SCHEMA
WHERE Table_Type = 'Base Table'
And Data_Type In ('ntext','text','nvarchar','nchar','varchar','char')
DECLARE #i INT
DECLARE #MAX INT
DECLARE #TableName sysname
DECLARE #ColumnName sysname
DECLARE #SchemaName sysname
DECLARE #SQL NVARCHAR(4000)
DECLARE #PARAMETERS NVARCHAR(4000)
DECLARE #DataExists BIT
DECLARE #SQLTemplate NVARCHAR(4000)
SELECT #SQLTemplate = CASE WHEN #ExactMatch = 1
THEN 'If Exists(Select *
From ReplaceTableName
Where Convert(nVarChar(4000), [ReplaceColumnName])
= ''' + #DataToFind + '''
)
Set #DataExists = 1
Else
Set #DataExists = 0'
ELSE 'If Exists(Select *
From ReplaceTableName
Where Convert(nVarChar(4000), [ReplaceColumnName])
Like ''%' + #DataToFind + '%''
)
Set #DataExists = 1
Else
Set #DataExists = 0'
END,
#PARAMETERS = '#DataExists Bit OUTPUT',
#i = 1
SELECT #i = 1, #MAX = MAX(RowId)
FROM #Temp
WHILE #i <= #MAX
BEGIN
SELECT #SQL = REPLACE(REPLACE(#SQLTemplate, 'ReplaceTableName', QUOTENAME(SchemaName) + '.' + QUOTENAME(TableName)), 'ReplaceColumnName', ColumnName)
FROM #Temp
WHERE RowId = #i
PRINT #SQL
EXEC SP_EXECUTESQL #SQL, #PARAMETERS, #DataExists = #DataExists OUTPUT
IF #DataExists =1
UPDATE #Temp SET DataFound = 1 WHERE RowId = #i
SET #i = #i + 1
END
SELECT SchemaName,TableName, ColumnName
FROM #Temp
WHERE DataFound = 1
GO
To run it, just do this:
exec FindMyData_string 'google', 0
It works amazingly well!!!
If you need to find database objects (e.g. tables, columns, and triggers) by name - have a look at the free Redgate Software tool called SQL Search which does this - it searches your entire database for any kind of string(s).
It's a great must-have tool for any DBA or database developer - did I already mention it's absolutely free to use for any kind of use??
You can also try ApexSQL Search – it’s a free SSMS add-in similar to SQL Search.
If you really want to use only SQL you might want to try this script:
select
S.name as [Schema],
o.name as [Object],
o.type_desc as [Object_Type],
C.text as [Object_Definition]
from sys.all_objects O inner join sys.schemas S on O.schema_id = S.schema_id
inner join sys.syscomments C on O.object_id = C.id
where S.schema_id not in (3,4) -- avoid searching in sys and INFORMATION_SCHEMA schemas
and C.text like '%ICE_%'
order by [Schema]
You can export your database (if small) to your hard drive / desktop, and then just do a string search via a text search program or text editor.
For getting a table by name in SQL Server:
SELECT *
FROM sys.Tables
WHERE name LIKE '%Employees%'
For finding a stored procedure by name:
SELECT name
FROM sys.objects
WHERE name = 'spName'
To get all stored procedures related to a table:
----Option 1
SELECT DISTINCT so.name
FROM syscomments sc
INNER JOIN sysobjects so ON sc.id=so.id
WHERE sc.TEXT LIKE '%tablename%'
----Option 2
SELECT DISTINCT o.name, o.xtype
FROM syscomments c
INNER JOIN sysobjects o ON c.id=o.id
WHERE c.TEXT LIKE '%tablename%'
This code searching procedure and function but not search in table :)
SELECT name
FROM sys.all_objects
WHERE Object_definition(object_id)
LIKE '%text%'
ORDER BY name
You could;
Script the database to a single file and search the file for tblEmployees using a text editor. In SQL Server Management Studio (SSMS), right click over the database and choose Generate Scripts.
Use SSMS 'View Dependencies' by right clicking over tblEmployees to see which other objects are dependent on it
Use a free third-party tool such as Redgate Software's SQL Search to search all database objects by name and content by keyword.
My version...
I named it "Needle in the haystack" for obvious reasons.
It searches for a specific value in each row and each column, not for column names, etc.
Execute search (replace values for the first two variables of course):
DECLARE #SEARCH_DB VARCHAR(100)='REPLACE_WITH_YOUR_DB_NAME'
DECLARE #SEARCH_VALUE_LIKE NVARCHAR(100)=N'%REPLACE_WITH_SEARCH_STRING%'
SET NOCOUNT ON;
DECLARE col_cur CURSOR FOR
SELECT TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE
FROM information_schema.columns WHERE TABLE_CATALOG=#SEARCH_DB AND DATA_TYPE NOT IN ('timestamp', 'datetime');
DECLARE #TOTAL int = (SELECT COUNT(*)
FROM information_schema.columns WHERE TABLE_CATALOG=#SEARCH_DB AND DATA_TYPE NOT IN ('timestamp', 'datetime'));
DECLARE #TABLE_CATALOG nvarchar(500), #TABLE_SCHEMA nvarchar(500), #TABLE_NAME nvarchar(500), #COLUMN_NAME nvarchar(500), #DATA_TYPE nvarchar(500);
DECLARE #SQL nvarchar(4000)='';
PRINT '-------- BEGIN SEARCH --------';
OPEN col_cur;
FETCH NEXT FROM col_cur INTO #TABLE_CATALOG, #TABLE_SCHEMA, #TABLE_NAME, #COLUMN_NAME, #DATA_TYPE;
BEGIN TRY DROP TABLE ##RESULTS; END TRY BEGIN CATCH END CATCH
CREATE TABLE ##RESULTS( TABLE_CATALOG nvarchar(500), TABLE_SCHEMA nvarchar(500), TABLE_NAME nvarchar(500), COLUMN_NAME nvarchar(500), DATA_TYPE nvarchar(500), RECORDS int)
DECLARE #SHOULD_CAST bit=0
DECLARE #i int =0
DECLARE #progress_sum bigint=0
WHILE ##FETCH_STATUS = 0
BEGIN
-- PRINT '' + CAST(#i as varchar(100)) +' of ' + CAST(#TOTAL as varchar(100)) + ' ' + #TABLE_CATALOG+'.'+#TABLE_SCHEMA+'.'+#TABLE_NAME+': '+#COLUMN_NAME+' ('+#DATA_TYPE+')';
SET #SHOULD_CAST = (SELECT CASE #DATA_TYPE
WHEN 'varchar' THEN 0
WHEN 'nvarchar' THEN 0
WHEN 'char' THEN 0
ELSE 1 END)
SET #SQL='SELECT '''+#TABLE_CATALOG+''' catalog_name, '''+#TABLE_SCHEMA+''' schema_name, '''+#TABLE_NAME+''' table_name, '''+#COLUMN_NAME+''' column_name, '''+#DATA_TYPE+''' data_type, ' +
+' COUNT(['+#COLUMN_NAME+']) records '+
+' FROM '+#TABLE_CATALOG+'.'+#TABLE_SCHEMA+'.'+#TABLE_NAME +
+' WHERE ' + CASE WHEN #SHOULD_CAST=1 THEN 'CAST(['+#COLUMN_NAME + '] as NVARCHAR(max)) ' ELSE ' ['+#COLUMN_NAME + '] ' END
+' LIKE '''+ #SEARCH_VALUE_LIKE + ''' '
-- PRINT #SQL;
IF #i % 100 = 0
BEGIN
SET #progress_sum = (SELECT SUM(RECORDS) FROM ##RESULTS)
PRINT CAST (#i as varchar(100)) +' of ' + CAST(#TOTAL as varchar(100)) +': '+ CAST (#progress_sum as varchar(100))
END
INSERT INTO ##RESULTS (TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE, RECORDS)
EXEC(#SQL)
FETCH NEXT FROM col_cur INTO #TABLE_CATALOG, #TABLE_SCHEMA, #TABLE_NAME, #COLUMN_NAME, #DATA_TYPE;
SET #i=#i+1
-- IF #i > 1000
-- BREAK
END
CLOSE col_cur;
DEALLOCATE col_cur;
SELECT * FROM ##RESULTS WHERE RECORDS>0;
Then to view results, even while executing, from another window, execute:
DECLARE #SEARCH_VALUE_LIKE NVARCHAR(100)=N'%#FLEX#%'
SELECT * FROM ##RESULTS WHERE RECORDS>0;
SET NOCOUNT ON;
DECLARE col_cur CURSOR FOR
SELECT TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE
FROM ##RESULTS WHERE RECORDS>0;
DECLARE #TABLE_CATALOG nvarchar(500), #TABLE_SCHEMA nvarchar(500), #TABLE_NAME nvarchar(500), #COLUMN_NAME nvarchar(500), #DATA_TYPE nvarchar(500);
DECLARE #SQL nvarchar(4000)='';
OPEN col_cur;
FETCH NEXT FROM col_cur INTO #TABLE_CATALOG, #TABLE_SCHEMA, #TABLE_NAME, #COLUMN_NAME, #DATA_TYPE;
DECLARE #i int =0
DECLARE #SHOULD_CAST bit=0
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SHOULD_CAST = (SELECT CASE #DATA_TYPE
WHEN 'varchar' THEN 0
WHEN 'nvarchar' THEN 0
WHEN 'char' THEN 0
ELSE 1 END)
SET #SQL='SELECT '''+#TABLE_CATALOG+''' catalog_name, '''+#TABLE_SCHEMA+''' schema_name, '''+#TABLE_NAME+''' table_name, '''+#COLUMN_NAME+''' column_name, '''+#DATA_TYPE+''' data_type, ' +
+' ['+#COLUMN_NAME+']'+
+', * '
+' FROM '+#TABLE_CATALOG+'.'+#TABLE_SCHEMA+'.'+#TABLE_NAME +
+' WHERE ' + CASE WHEN #SHOULD_CAST=1 THEN 'CAST(['+#COLUMN_NAME + '] as NVARCHAR(max)) ' ELSE ' ['+#COLUMN_NAME + '] ' END
+' LIKE '''+ #SEARCH_VALUE_LIKE + ''' '
PRINT #SQL;
EXEC(#SQL)
FETCH NEXT FROM col_cur INTO #TABLE_CATALOG, #TABLE_SCHEMA, #TABLE_NAME, #COLUMN_NAME, #DATA_TYPE;
SET #i=#i+1
-- IF #i > 10
-- BREAK
END
CLOSE col_cur;
DEALLOCATE col_cur;
Few mentions about it:
it uses cursors instead of a blocking while loop
it can print progress (uncomment if needed)
it can exit after a few attempts (uncomment the IF at the end)
it displays all records
you can fine tune it as needed
DISCLAIMERS:
DO NOT run it in production environments!
It is slow. If the DB is accessed by other services/users, please add " WITH (NOLOCK) " after every table name in all the selects, especially the dynamic select ones.
It does not validate/protect against all sorts of SQL injection options.
If your DB is huge, prepare yourself for some sleep, make sure the query will not be killed after a few minutes.
It casts some values to string, including ints/bigints/smallints/tinyints. If you don't need those, put them at the same exclusion lists with the timestamps at the top of the script.
The content of all stored procedures, views and functions are stored in field text of table sysComments. The name of all objects are stored in table sysObjects and the columns are in sysColumns.
Having this information, you can use this code to search in content of views, stored procedures, and functions for the specified word:
Select b.name from syscomments a
inner join sysobjects b on a.id = b.id
where text like '%tblEmployes%'
This query will give you the objects which contains the word "tblEmployes" .
To search by the name of Objects you can use this code:
Select name from sysobjects
where name like '%tblEmployes%'
And finally to find the objects having at least one column containing the word "tblEmployes", you can use this code:
Select b.name from syscolumns a inner join sysobjects b on a.id = b.id
where a.name like '%tblEmployes%'
You can combine these three queries with union:
Select distinct b.name from syscomments a
inner join sysobjects b on a.id = b.id
where text like '%tblEmployes%'
union
Select distinct name from sysobjects
where name like '%tblEmployes%'
union
Select distinct b.name from syscolumns a inner join sysobjects b on a.id = b.id
where a.name like '%tblEmployes%'
With this query you have all objects containing the word "tblEmployes" in content or name or as a column.
I was given access to a database, but not the table where my query was being stored in.
Inspired by #marc_s answer, I had a look at HeidiSQL which is a Windows program that can deal with MySQL, SQL Server, and PostgreSQL.
I found that it can also search a database for a string.
It will search each table and give you how many times it found the string per table!
This will search for a string over every database:
declare #search_term varchar(max)
set #search_term = 'something'
select #search_term = 'use ? SET QUOTED_IDENTIFIER ON
select
''[''+db_name()+''].[''+c.name+''].[''+b.name+'']'' as [object],
b.type_desc as [type],
d.obj_def.value(''.'',''varchar(max)'') as [definition]
from (
select distinct
a.id
from sys.syscomments a
where a.[text] like ''%'+#search_term+'%''
) a
inner join sys.all_objects b
on b.[object_id] = a.id
inner join sys.schemas c
on c.[schema_id] = b.[schema_id]
cross apply (
select
[text()] = a1.[text]
from sys.syscomments a1
where a1.id = a.id
order by a1.colid
for xml path(''''), type
) d(obj_def)
where c.schema_id not in (3,4) -- avoid searching in sys and INFORMATION_SCHEMA schemas
and db_id() not in (1,2,3,4) -- avoid sys databases'
if object_id('tempdb..#textsearch') is not null drop table #textsearch
create table #textsearch
(
[object] varchar(300),
[type] varchar(300),
[definition] varchar(max)
)
insert #textsearch
exec sp_MSforeachdb #search_term
select *
from #textsearch
order by [object]
If I want to find where anything I want to search is, I use this:
DECLARE #search_string varchar(200)
SET #search_string = '%myString%'
SELECT DISTINCT
o.name AS Object_Name,
o.type_desc,
m.definition
FROM sys.sql_modules m
INNER JOIN
sys.objects o
ON m.object_id = o.object_id
WHERE m.definition Like #search_string;
It's easy to search a string in your database with phpmyadmin. There you can chose from many search options and you can see where your search phrase is mentioned.
Here is the same script as submitted by user l--''''''---------'''''''''''', but corrected to work on a case-sensitive SQL instance, and with some other minor improvements.
DROP PROCEDURE IF EXISTS dbo.spFind_Text_In_Database
GO
CREATE PROCEDURE dbo.spFind_Text_In_Database
#strText_To_Find NVARCHAR(4000),
#bitExact_Match BIT = 0
AS
SET NOCOUNT ON
DECLARE #Temp TABLE(RowId INT IDENTITY(1,1), SchemaName sysname, TableName sysname, ColumnName SysName, DataType VARCHAR(100), DataFound BIT)
INSERT INTO #Temp(TableName,SchemaName, ColumnName, DataType)
SELECT C.TABLE_NAME, C.TABLE_SCHEMA, C.COLUMN_NAME, C.DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS AS C
INNER Join INFORMATION_SCHEMA.TABLES AS T
ON C.TABLE_NAME = T.TABLE_NAME
AND C.TABLE_SCHEMA = T.TABLE_SCHEMA
WHERE TABLE_TYPE = 'BASE TABLE'
And DATA_TYPE In ('ntext','text','nvarchar','nchar','varchar','char')
DECLARE #i INT
DECLARE #MAX INT
DECLARE #TableName sysname
DECLARE #ColumnName sysname
DECLARE #SchemaName sysname
DECLARE #SQL NVARCHAR(4000)
DECLARE #PARAMETERS NVARCHAR(4000)
DECLARE #DataExists BIT
DECLARE #SQLTemplate NVARCHAR(4000)
SELECT #SQLTemplate = CASE WHEN #bitExact_Match = 1
THEN 'If Exists(Select *
From ReplaceTableName
Where Convert(nVarChar(4000), [ReplaceColumnName])
= ''' + #strText_To_Find + '''
)
Set #DataExists = 1
Else
Set #DataExists = 0'
ELSE 'If Exists(Select *
From ReplaceTableName
Where Convert(nVarChar(4000), [ReplaceColumnName])
Like ''%' + #strText_To_Find + '%''
)
Set #DataExists = 1
Else
Set #DataExists = 0'
END,
#PARAMETERS = '#DataExists Bit OUTPUT',
#i = 1
SELECT #i = 1, #MAX = MAX(RowId)
FROM #Temp
WHILE #i <= #MAX
BEGIN
SELECT #SQL = REPLACE(REPLACE(#SQLTemplate, 'ReplaceTableName', QUOTENAME(SchemaName) + '.' + QUOTENAME(TableName)), 'ReplaceColumnName', ColumnName)
FROM #Temp
WHERE RowId = #i
PRINT #SQL
EXEC sp_executesql #SQL, #PARAMETERS, #DataExists = #DataExists OUTPUT
IF #DataExists =1
UPDATE #Temp SET DataFound = 1 WHERE RowId = #i
SET #i = #i + 1
END
SELECT SchemaName,TableName, ColumnName
FROM #Temp
WHERE DataFound = 1
GO
Searching SQL Database objects is possible with SQL Server Management Studio (SSMS) with the following methods, with SSMS Object Search: object explorer details or T-SQL scripts as explained in following:
Different ways to search for SQL Server database objects
SQL Server Find Anything in Object Explorer in SSMS
Search text with wildcards
Here is how you can search the database in Swift using the FMDB library.
First, go to this link and add this to your project: FMDB. When you have done that, then here is how you do it. For example, you have a table called Person, and you have firstName and secondName and you want to find data by first name, here is a code for that:
func loadDataByfirstName(firstName : String, completion: #escaping CompletionHandler){
if isDatabaseOpened {
let query = "select * from Person where firstName like '\(firstName)'"
do {
let results = try database.executeQuery(query, values: [firstName])
while results.next() {
let firstName = results.string(forColumn: "firstName") ?? ""
let lastName = results.string(forColumn: "lastName") ?? ""
let newPerson = Person(firstName: firstName, lastName: lastName)
self.persons.append(newPerson)
}
completion(true)
}catch let err {
completion(false)
print(err.localizedDescription)
}
database.close()
}
}
Then in your ViewController you will write this to find the person detail you are looking for:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
SQLManager.instance.openDatabase { (success) in
if success {
SQLManager.instance.loadDataByfirstName(firstName: "Hardi") { (success) in
if success {
// You have your data Here
}
}
}
}
}

How to use loop for like or ilike function?

I am currently asked to validate the result of a global search which to find all records containing some keywords cross whole database. To do so, I need to check all the rows in each table that have keywords. The result for this global search is ready and partially of the result looks like the one below:
table_name
column_name
keyword
cnt
Wf_Process
Name_EN
FEC
11
Wf_Process
FTABLENAME
GB
14
ICCClass
Name_EN
GB
4
What I am trying to do is using the 'like' operator to extract all the data in each table where columns containing keywords. That is, I will use query such as:
select distinct Name_EN, FTABLENAME from Wf_Process where Name_EN like '%FEC%' or FTABLENAME like '%GB%'
and
select distinct Name_EN from ICCClass where Name_EN like '%GB%'
to pull out all data I need in whole database. If I only have three records, then it is not a problem. However my result returned over thousands tables have more than 10K rows containing keywords in total. Therefore, I was trying to use a loop to do this mission but I failed.
My question therefore is, does anyone have any idea to write a loop to do the 'like' search for all the tables in one time? Or is there another way rather than loop can do this in SQL Server?
Thank you very much!
You need dynamic SQL for this. You can generate a big UNION ALL query of all the tables together.
DECLARE #sql nvarchar(max) = (
SELECT STRING_AGG(CAST('
SELECT
' + QUOTENAME(con.table_name, '''') + ' table_name,
' + QUOTENAME(con.column_name, '''') + ' column_name,
' + QUOTENAME(con.keyword, '''') + ' keyword,
COUNT(*) cnt
FROM ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name) + ' t
WHERE t.' + QUOTENAME(c.name) + ' LIKE ' + QUOTENAME('%' + con.keyword + '%', '''')
AS nvarchar(max)), '
UNION ALL')
FROM YourConditions con
JOIN sys.tables t ON t.table_name
JOIN sys.schemas s ON s.schema_id = t.schema_id
JOIN sys.columns c ON c.object_id = t.object_id
WHERE t.name = con.table_name
AND c.name = con.column_name
);
PRINT #sql; -- your friend
EXEC sp_executesql #sql;
There are more efficient ways to do this if there are many columns or keywords per table, but this should get you started.
The question looks odd, thou can be done via dynamic sql, but not as a regular solution more like a one off or POC/test.
use tempdb
GO
drop table if exists search_result
go
create table search_result (
table_name sysname
,column_name sysname
,keyword varchar(100))
go
insert into search_result values
('Wf_Process', 'Name_EN', 'FEC')
,('Wf_Process' , 'FTABLENAME', 'GB')
,('ICCClass', 'Name_EN', 'GB')
GO
drop table if exists result
create table result (val varchar(500))
go
declare #col sysname
declare #tab sysname
declare #kw varchar(100)
declare #sql varchar(1000)
while exists (select * from search_result)
begin
select top 1
#tab = table_name
, #col = column_name
, #kw = keyword
from search_result
set #sql = concat('insert into result select ', #col, ' from ', #tab, ' where ', #col, ' like ''%', #kw, '%''' )
print(#sql)
exec (#sql)
delete from search_result
where table_name = #tab and column_name = #col and keyword = #kw
end
GO
select * from result
--initially trying to get all the column name with its respective table name, finding out if there is that particular keyword in all table,
--if yes, insert into temp table, if there aint keyword, it will insert the record too but the count of record will be zero.
--trying to make it dynamic and running it inside the loop by assigning row number to each table with different column.
IF OBJECT_ID('tempdb..#TEMP') IS NOT NULL
DROP TABLE #TEMP
CREATE TABLE #TEMP (TABLE_NAME NVARCHAR(200), COLUMN_NAME NVARCHAR(200), KEYWORD NVARCHAR(200), CNT INT)
DECLARE #TABLE NVARCHAR(200)
DECLARE #TABLE_ID INT
SET #TABLE_ID = 1
DECLARE #TABLE_NAME NVARCHAR (200)
DECLARE #FOR_LOOP INT
DECLARE #COLUMN NVARCHAR(200)
DECLARE #COLUMN_NAME NVARCHAR(200)
--TOTAL NO OF RECORDS FOR LOOP
SET #FOR_LOOP = (SELECT COUNT(*)
FROM SYS.tables T
JOIN SYS.all_columns C ON T.object_id = C.object_id
JOIN INFORMATION_SCHEMA.TABLES S ON S.TABLE_NAME = T.name)
DECLARE #STRINGS NVARCHAR(200)
SET #STRINGS = '%FEC%' --------->ENTER YOUR KEYWORD HERE, TRY ONE AT A TIME
DECLARE #COUNT INT
WHILE #TABLE_ID <= #FOR_LOOP
BEGIN
SET #TABLE = (SELECT CAST(TABLE_NAME AS NVARCHAR(200))
FROM
(SELECT ROW_NUMBER()OVER(ORDER BY T.NAME) TABLE_ID
,S.TABLE_SCHEMA SCHEMA_NAME ,T.NAME TABLE_NAME, C.name COLUMN_NAME
FROM SYS.tables T
JOIN SYS.all_columns C ON T.object_id = C.object_id
JOIN INFORMATION_SCHEMA.TABLES S ON S.TABLE_NAME = T.name)A
WHERE TABLE_ID = #TABLE_ID)
SET #TABLE_NAME = '['+#TABLE+']'
SET #COLUMN = (SELECT CAST(COLUMN_NAME AS NVARCHAR(200))
FROM
(SELECT ROW_NUMBER()OVER(ORDER BY T.NAME) TABLE_ID
,S.TABLE_SCHEMA SCHEMA_NAME ,T.NAME TABLE_NAME, C.name COLUMN_NAME
FROM SYS.tables T
JOIN SYS.all_columns C ON T.object_id = C.object_id
JOIN INFORMATION_SCHEMA.TABLES S ON S.TABLE_NAME = T.name)A
WHERE TABLE_ID = #TABLE_ID)
SET #COLUMN_NAME = '['+#COLUMN+']'
DECLARE #EXEC NVARCHAR(200) = 'SELECT ' + #COLUMN_NAME +' FROM ' + #TABLE_NAME + ' WHERE ' + #COLUMN_NAME + ' LIKE ' + ''''+#STRINGS+''''
--THERE MUST BE ANOTHER WAY TO REPLACE SP_EXECUTESQL FOR BETTER PERFORMANCE, AS IT WILL BE EXECUTED AS MANY TIMES AS THERE ARE RECORDS IN #FOR_LOOP
EXEC SP_EXECUTESQL #EXEC
SET #COUNT = (SELECT ##ROWCOUNT)
IF (SELECT ##ROWCOUNT) >=1
BEGIN
INSERT INTO #TEMP VALUES
(#TABLE_NAME, #COLUMN_NAME, #STRINGS, #COUNT)
SET #TABLE_ID = #TABLE_ID + 1
END
ELSE
--DONT KNOW WHY THIS ELSE PART IS NOT TOUCHED, WHEREAS I THINK IT SHOULD HAVE
BEGIN
PRINT 'RECORD NOT FOUND'
END
END
GO
--AFTER THE ABOVE BLOCK IS EXECUTED THEN TRY RUNNING BELOW ONE
SELECT *FROM #TEMP WHERE CNT>0
--THERE MAY BE MULTIPLE ERROS, MUST BE EDITED AND CAN MAKE IT AS A PROCEDURE TOO.

ERROR IN DYNAMIC SQL: Conversion failed when converting the varchar value ', ' to data type int

My purpose is to produce a table containing the table_name, column_name, number of row each column and number of null value in each column. But I get an error:
Conversion failed when converting the varchar value ', ' to data type int
These are my queries:
DECLARE #BANG TABLE
(
TABLE_NAME NVARCHAR(MAX),
COLUMN_NAME NVARCHAR(MAX),
ID INT IDENTITY(1, 1)
)
INSERT INTO #BANG (TABLE_NAME, COLUMN_NAME)
SELECT A.NAME AS TABLE_NAME, B.NAME AS COLUMN_NAME
FROM SYS.TABLES AS A
LEFT JOIN SYS.COLUMNS AS B ON A.OBJECT_ID = B.OBJECT_ID
WHERE 1=1
AND A.NAME IN ('CTHD', 'HOADON', 'SANPHAM', 'KHACHHANG', 'NHANVIEN')
DECLARE #RESULT TABLE
(
TABLE_NAME NVARCHAR(MAX),
COLUMN_NAME NVARCHAR(MAX),
TOTAL_ROW INT,
TOTAL_NULL INT
)
DECLARE #ID INT = 0
WHILE #ID <= (SELECT COUNT(*) FROM #BANG)
BEGIN
DECLARE #TABLE_NAME NVARCHAR(MAX)
SET #TABLE_NAME = (SELECT TABLE_NAME FROM #BANG WHERE #ID = ID)
DECLARE #COLUMN_NAME NVARCHAR(MAX)
SET #COLUMN_NAME = (SELECT COLUMN_NAME FROM #BANG WHERE ID = #ID)
DECLARE #TOTAL_ROW INT
DECLARE #TOTAL_NULL INT
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = 'SET #TOTAL_ROW = (SELECT COUNT(*) FROM '+#TABLE_NAME+')
SET #TOTAL_NULL = (SELECT COUNT(*) FROM '+#TABLE_NAME+' WHERE '+#COLUMN_NAME+' IS NULL)
INSERT INTO #RESULT
VALUES ('+#TABLE_NAME+', '+#COLUMN_NAME+', '+#TOTAL_ROW+', '+#TOTAL_NULL+')
'
SET #ID += 1
EXEC (#SQL)
END
I need your help. Thanks in advance
You should be using parameterized SQL. But honestly, the code is such a mess that I'm not going to attempt that fix.
The problem is that parameters such as #TOTAL_ROW are integers, not strings. So, the + is treated as addition rather than string concatenation.
The simplest immediate fix is to use CONCAT():
SET #SQL = CONCAT('
INSERT INTO #RESULT
VALUES (''', #TABLE_NAME, ''', ''', #COLUMN_NAME, ''', ''', #TOTAL_ROW, ', ', #TOTAL_NULL, ')';
You may have the same error elsewhere in the code. You need to fix all places where you have a number and string combined with + and you intend string concatenation rather than addition.
However, the real fix is to not munge query strings with such values. Instead use sp_executesql passing the values in as parameters.
The conversion error is during the generation of the dynamic SQL query, not during execution of the statement.
There are a number of issues with the script in your question. Below is a script that uses QUOTENAME to more security build the SQL statement and uses a parameterized query to execute it. The WHILE pseudo cursor doesn't provide any value in this case so this version uses a real cursor.
DECLARE #RESULT TABLE (SCHEMA_NAME sysname, TABLE_NAME sysname, COLUMN_NAME sysname, TOTAL_ROW int, TOTAL_NULL int);
DECLARE #SQL nvarchar(MAX), #SchemaName sysname, #TableName sysname, #ColumnName sysname;
DECLARE BANG CURSOR LOCAL FAST_FORWARD FOR
SELECT s.name AS SCHEMA_NAME, t.name AS TABLE_NAME, c.name AS COLUMN_NAME
FROM sys.tables AS t
JOIN sys.schemas AS s ON s.schema_id = t.schema_id
JOIN sys.columns AS c ON c.object_id = t.object_id
WHERE t.name IN (N'CTHD', N'HOADON', N'SANPHAM', N'KHACHHANG', N'NHANVIEN');
OPEN BANG;
WHILE 1 = 1
BEGIN
FETCH NEXT FROM BANG INTO #SchemaName, #TableName, #ColumnName;
IF ##FETCH_STATUS = -1 BREAK;
SET #SQL = N'SELECT #SchemaName, #TableName, #ColumnName, COUNT(*), COALESCE(SUM(CASE WHEN ' + QUOTENAME(#ColumnName) + N' IS NULL THEN 1 ELSE 0 END),0)
FROM ' + QUOTENAME(#SchemaName) + N'.' + QUOTENAME(#TableName) + N';'
PRINT #SQL
INSERT INTO #RESULT(SCHEMA_NAME, TABLE_NAME, COLUMN_NAME, TOTAL_ROW, TOTAL_NULL)
EXEC sp_executesql #SQL
, N'#SchemaName sysname, #TableName sysname, #ColumnName sysname'
, #SchemaName = #SchemaName
, #TableName = #TableName
, #ColumnName = #ColumnName;
END;
CLOSE BANG;
DEALLOCATE BANG;
SELECT SCHEMA_NAME, TABLE_NAME, COLUMN_NAME, TOTAL_ROW, TOTAL_NULL
FROM #RESULT
ORDER BY SCHEMA_NAME, TABLE_NAME, COLUMN_NAME;
GO
If you don't have many tables/columns, you could use a single UNION ALL query and ditch the (pseudo)cursor entirely:
DECLARE #SQL nvarchar(MAX) = (SELECT STRING_AGG(
N'SELECT ' + QUOTENAME(s.name,'''') + N' AS SCHEMA_NAME,'
+ QUOTENAME(t.name, '''') + N' AS TABLE_NAME,'
+ QUOTENAME(c.name,'''') + N' AS COLUMN_NAME,'
+ 'COUNT(*) AS TOTAL_ROW,'
+ 'COALESCE(SUM(CASE WHEN ' + QUOTENAME(c.name) + ' IS NULL THEN 1 ELSE 0 END),0) AS TOTAL_NULL '
+ 'FROM ' + QUOTENAME(s.name) + N'.' + QUOTENAME(t.name)
, ' UNION ALL ') + N';'
FROM sys.tables AS t
JOIN sys.schemas AS s ON s.schema_id = t.schema_id
JOIN sys.columns AS c ON c.object_id = t.object_id
WHERE t.name IN (N'CTHD', N'HOADON', N'SANPHAM', N'KHACHHANG', N'NHANVIEN');
EXEC sp_executesql #SQL;
As Gordon said, use sp_execute properly to execute dynamics sql. And there's some others issue with this code, not related to the question.
"the more important" when investigating is to use the print statement before the exec statement, to know what's being execute. Then, you'll realize where the errors is and why it won't work....
the first execution of the loop is useless (or maybe this is the one that produce the error....). You initialize #Id with the value of 0 and then compare it with the value of the identity in the table #BANG, starting at 1. This result to #TABLE_NAME and #COLUMN_NAME set to NULL, thus, concatening string without using CONCAT will end up in a NULL value. Nothing is execute on the first loop.
However, using concat with null value will not result in a null value, but an incorrect value (query in your case). As an exemple, this code
SET #SQL = CONCAT('
INSERT INTO #RESULT
VALUES (''', #TABLE_NAME, ''', ''', #COLUMN_NAME, ''', ''', #TOTAL_ROW, ', ', #TOTAL_NULL, ')';
will result in something like "INSERT INTO #RESULT VALUES (''CTHD'',''COL1'',,)" since both #TOTAL_ROW AND #TOTAL_NULL are null values. you need to use parametrized query using sp_executesql.
There's no need to execute two count on the same table, one for total rows, the second for null values. select count(1) return the total number of rows, and select count(#column_name) return the number of non-null value. So, Count(1) - count(#column_name) will gives you the number of null value. Then, use something like this to insert into #result :
INSERT INTO #RESULT (...) SELECT TABLE_NAME, COLUMN_NAME, COUNT(1), COUNT(1) - COUNT(#COLUMN_NAME) FROM ...
when dealing with SQL Server object, you the quotename function. You'll never know when someone will put a space, quote, bracket or whatever in a schema/table/column name that might break you query.
Do not use "WHILE #ID <= (SELECT COUNT(*) FROM #BANG)" to test if there are more rows to process. Use a "WHILE EXISTS (SELECT 1 FROM #BANG)"

SQL Server SELECT where any column contains 'x'

Using SQL Server 2008, say I have a table called testing with 80 columns and I want to find a value called foo.
I can do:
SELECT *
FROM testing
WHERE COLNAME = 'foo'
Is it possible I can query all 80 columns and return all the results where foo is contained in any of the 80 columns?
You can use in:
SELECT *
FROM testing
WHERE 'foo' in (col1, col2, col3, . . . );
First Method(Tested)
First get list of columns in string variable separated by commas and then you can search 'foo' using that variable by use of IN
Check stored procedure below which first gets columns and then searches for string:
DECLARE #TABLE_NAME VARCHAR(128)
DECLARE #SCHEMA_NAME VARCHAR(128)
-----------------------------------------------------------------------
-- Set up the name of the table here :
SET #TABLE_NAME = 'testing'
-- Set up the name of the schema here, or just leave set to 'dbo' :
SET #SCHEMA_NAME = 'dbo'
-----------------------------------------------------------------------
DECLARE #vvc_ColumnName VARCHAR(128)
DECLARE #vvc_ColumnList VARCHAR(MAX)
IF #SCHEMA_NAME =''
BEGIN
PRINT 'Error : No schema defined!'
RETURN
END
IF NOT EXISTS (SELECT * FROM sys.tables T JOIN sys.schemas S
ON T.schema_id=S.schema_id
WHERE T.Name=#TABLE_NAME AND S.name=#SCHEMA_NAME)
BEGIN
PRINT 'Error : The table '''+#TABLE_NAME+''' in schema '''+
#SCHEMA_NAME+''' does not exist in this database!'
RETURN
END
DECLARE TableCursor CURSOR FAST_FORWARD FOR
SELECT CASE WHEN PATINDEX('% %',C.name) > 0
THEN '['+ C.name +']'
ELSE C.name
END
FROM sys.columns C
JOIN sys.tables T
ON C.object_id = T.object_id
JOIN sys.schemas S
ON S.schema_id = T.schema_id
WHERE T.name = #TABLE_NAME
AND S.name = #SCHEMA_NAME
ORDER BY column_id
SET #vvc_ColumnList=''
OPEN TableCursor
FETCH NEXT FROM TableCursor INTO #vvc_ColumnName
WHILE ##FETCH_STATUS=0
BEGIN
SET #vvc_ColumnList = #vvc_ColumnList + #vvc_ColumnName
-- get the details of the next column
FETCH NEXT FROM TableCursor INTO #vvc_ColumnName
-- add a comma if we are not at the end of the row
IF ##FETCH_STATUS=0
SET #vvc_ColumnList = #vvc_ColumnList + ','
END
CLOSE TableCursor
DEALLOCATE TableCursor
-- Now search for `foo`
SELECT *
FROM testing
WHERE 'foo' in (#vvc_ColumnList );
2nd Method
In sql server you can get object id of table then using that object id you can fetch columns. In that case it will be as below:
Step 1: First get Object Id of table
select * from sys.tables order by name
Step 2: Now get columns of your table and search in it:
select * from testing where 'foo' in (select name from sys.columns where object_id =1977058079)
Note: object_id is what you get fetch in first step for you relevant table
You can use in and you can get the column names dynamically and pass them to IN clause by making sql string and executing it using execute sp_executesql.
declare #sql nvarchar(2100)
declare #cols nvarchar(2000)
declare #toSearch nvarchar(200)
declare #tableName nvarchar(200)
set #tableName = 'tbltemp'
set #toSearch = '5'
set #cols =(
SELECT LEFT(column_name, LEN(column_name) - 1)
FROM (
SELECT column_name + ', '
FROM INFORMATION_SCHEMA.COLUMNS where table_name = #tableName
FOR XML PATH ('')
) c (column_name )
)
set #sql = 'select * from tbltemp where '''+ #toSearch + ''' in (' + #cols + ')';
execute sp_executesql #sql
I think this is one of the best ways of doing it
SELECT * FROM sys.columns a
inner join
(
SELECT object_id
FROM sys.tables
where
type='U'--user table
and name like 'testing'
) b on a.object_id=b.object_id
WHERE a.name like '%foo%'
I took the idea from ubaid ashraf's answer, but made it actually work. Just change MyTableName here:
SELECT STUFF((
SELECT ', ' + c.name
FROM sys.columns c
JOIN sys.types AS t ON c.user_type_id=t.user_type_id
WHERE t.name != 'int' AND t.name != 'bit' AND t.name !='date' AND t.name !='datetime'
AND object_id =(SELECT object_id FROM sys.tables WHERE name='MyTableName')
FOR XML PATH('')),1,2,'')
You could tweak it to your needs and add or remove conditions from the where column (the 't.name != 'int' AND t.name != 'bit' etc. part), e.g. add 't.name != 'uniqueidentifier'' to avoid getting Conversion failed when converting the varchar value 'myvalue' to data type int type of errors..
Then copy paste the result into this query (otherwise it didn't work):
SELECT * from MyTableName where 'foo' in (COPY PASTE PREVIOUS QUERY RESULT INTO HERE)
--Obtain object_id
SELECT object_id FROM sys.tables WHERE name = <your_table>
--look for desired value in specified columns using below syntax
SELECT * FROM <your_table> WHERE <VALUE_YOU_SEARCH_FOR> in
(SELECT name FROM sys.tables WHERE object_id = <your_table_object_id>
and name like '<if_you_have_multiple_columns_with_same_name_pattern>')
I've worked with BornToCode's answer and this script generates the queries to find a value in all columns of type varchar for any view (can be table) of the database:
DECLARE #id INT
declare #name nvarchar(30)
DECLARE #getid CURSOR
declare #value nvarchar(30)
set #value = 'x'
SET #getid = CURSOR FOR
SELECT object_id,name
FROM sys.views
OPEN #getid
FETCH NEXT
FROM #getid INTO #id, #name
WHILE ##FETCH_STATUS = 0
BEGIN
---------
SELECT 'SELECT * from ' + #name + ' where ''' + #value + ''' in (' +
STUFF((
SELECT ', ' + c.name
FROM sys.columns c
JOIN sys.types AS t ON c.user_type_id=t.user_type_id
WHERE t.name = 'varchar'-- AND t.name != 'bit' AND t.name !='date' AND t.name !='datetime'
AND object_id =(SELECT object_id FROM sys.views WHERE name=#name)
FOR XML PATH('')),1,2,'')
+ ')' as 'query'
------
FETCH NEXT
FROM #getid INTO #id, #name
END
CLOSE #getid
DEALLOCATE #getid

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