Related
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
}
}
}
}
}
I have 50 tables and I want to get list of all column names containing specific value in the column.
Something like: if 'Test' in column give me the column name. And I want to apply it for all 50 tables
Tables names are: abc1,abc2,abc3, … abcn.
There must be a better way to do this, but I did come across a query like this from a colleague a couple of years ago and I found the below (or a script similar to below that i may have tweaked before sending onto him) somewhere online (I imagine here on SO, but can't see to find where - I will update with link to original source if i can find it).
I would also just point out that this is a very heavy proc so I would take a backup of your db and restore it to a dev or test environment before executing it. Or even execute it against a test db that you might have in your localdb. The script searches all columns of all tables in a selected db for a string.
Personally I would probably export the data from each table to txt or csv and do a Find within them.
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
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I'm getting the error
The multi-part identifier sys.schemas could not be bound. The
multi-part identifier sys.tables could not be bound.
With the following code:
SET NOCOUNT ON
DECLARE #Statement NVARCHAR(MAX) = ''
DECLARE #Statement2 NVARCHAR(MAX) = ''
DECLARE #FinalStatement NVARCHAR(MAX) = ''
DECLARE #TABLE_SCHEMA SYSNAME = sys.schemas
DECLARE #TABLE_NAME SYSNAME = sys.tables
SELECT
#Statement = #Statement + 'SUM(CASE WHEN ' + COLUMN_NAME + ' IS NULL THEN 1 ELSE 0 END) AS ' + COLUMN_NAME + ',' + CHAR(13) ,
#Statement2 = #Statement2 + COLUMN_NAME + '*100 / OverallCount AS ' + COLUMN_NAME + ',' + CHAR(13) FROM
INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TABLE_NAME AND TABLE_SCHEMA = #TABLE_SCHEMA
IF ##ROWCOUNT = 0
RAISERROR('TABLE OR VIEW with schema "%s" and name "%s" does not exists or you do not have appropriate permissions.',16,1, #TABLE_SCHEMA, #TABLE_NAME)
ELSE
BEGIN
SELECT #FinalStatement =
'SELECT ' + LEFT(#Statement2, LEN(#Statement2) -2) + ' FROM (SELECT ' + LEFT(#Statement, LEN(#Statement) -2) +
', COUNT(*) AS OverallCount FROM ' + #TABLE_SCHEMA + '.' + #TABLE_NAME + ') SubQuery'
EXEC(#FinalStatement)
END
Where is my code wrong?
sysname is some character variant. You can't do
DECLARE #TABLE_SCHEMA SYSNAME = sys.schemas
DECLARE #TABLE_NAME SYSNAME = sys.tables
you need to use quotes as with char, varchar, etc.. Use
DECLARE #TABLE_SCHEMA SYSNAME = 'sys.schemas'
DECLARE #TABLE_NAME SYSNAME = 'sys.tables'
to fix the error.
Edit:
And since you apparently want to use these values to filter on the schema name and table name separately (
WHERE TABLE_NAME = #TABLE_NAME AND TABLE_SCHEMA = #TABLE_SCHEMA
) you should only assign the right part to each variable:
DECLARE #TABLE_SCHEMA SYSNAME = 'some_schema_name'
DECLARE #TABLE_NAME SYSNAME = 'some_table_name'
A schema with the name sys.schemas is unlikely to exist. As well as a table named sys.tables (I'm talking about just the name. Of course this is otherwise a qualified identifier including the schema.).
E.g.:
DECLARE #TABLE_SCHEMA SYSNAME = 'sys'
DECLARE #TABLE_NAME SYSNAME = 'tables'
(But sys.tables doesn't seem to bee listed in information_schema.columns. But I guess your not after system but "regular" tables anyway.)
I think what you are looking for has similarities to what this person achieved here:
recursively go through each table and find the total number of NULL values.
But do bear in mind that this can take a while depending on the size of your database.
<!-- language: lang-SQL -->
DECLARE #schemaName AS sysname;
DECLARE #tableName AS sysname;
DECLARE #columnName AS sysname;
DECLARE #schema_id AS int;
DECLARE #object_id AS int;
DECLARE #column_id AS int;
DECLARE #isNullable AS bit;
DECLARE #lastSchema_id AS int;
DECLARE #lastTable_id AS int;
DECLARE #recordCount AS bigint;
DECLARE #nullCnt AS bigint;
DECLARE #SQL as nvarchar(max);
DECLARE #paramDefinition NVARCHAR(max);
if exists(select name from tempdb..sysobjects where name LIKE'#Columns%')
DROP TABLE #Columns;
CREATE TABLE #Columns (
schema_id int,
object_id int,
column_id int,
schemaName sysname,
tableName sysname,
columnName sysname,
recordCnt bigint,
nullCnt bigint,
nullPct numeric(38,35) );
-- Set to the #lastSchema_id and #lastTable_id to NULL so that the first
-- loop through the cursor the record count is generated.
SET #lastSchema_id = NULL;
SET #lastTable_id = NULL;
-- List of all the user schemas.tables.columns
-- in the database
DECLARE c_Cursor CURSOR FOR
SELECT schemas.schema_id
, all_objects.object_id
, all_columns.column_id
, schemas.name AS schemaName
, all_objects.name AS tableName
, all_columns.name AS columnName
, all_columns.is_nullable
FROM sys.schemas
INNER JOIN sys.all_objects
ON schemas.schema_id = all_objects.schema_id
AND all_objects.type = 'U'
INNER JOIN sys.all_columns
ON all_objects.object_id = all_columns.object_id
ORDER BY schemas.schema_id
, all_objects.object_id
, all_columns.column_id;
OPEN c_Cursor;
FETCH NEXT FROM c_Cursor
INTO #schema_id
, #object_id
, #column_id
, #schemaName
, #tableName
, #columnName
, #isNullable;
-- Loop through the cursor
WHILE ##FETCH_STATUS = 0
BEGIN
-- Get the record count for the table we are currently working on if this is
-- the first time we are encountering the table.
IF ( ( #schema_id <> #lastSchema_id ) OR ( #object_id <> #lastTable_id )
OR ( #lastSchema_id IS NULL ) OR ( #lastTable_id IS NULL ) )
BEGIN
SET #SQL = N'SELECT #recordCount = COUNT(1) FROM ' + QUOTENAME(#schemaName) + N'.' + QUOTENAME(#tableName) + ';';
SET #paramDefinition = N'#recordCount bigint OUTPUT';
exec sp_executesql #SQL,
#paramDefinition,
#recordCount = #recordCount OUTPUT;
END
-- If the column is NOT NULL, there is no reason to do a count
-- Set the nullCnt and nullPct to 0
IF ( #isNullable = 0 )
BEGIN
INSERT INTO #Columns
( [schema_id]
, [object_id]
, column_id
, schemaName
, tableName
, columnName
, recordCnt
, nullCnt
, nullPct )
VALUES
( #schema_id
, #object_id
, #column_id
, #schemaName
, #tableName
, #columnName
, #recordCount
, 0
, 0.0 );
END
-- If the column is NULL, count the number of NULL fields in the table.
ELSE
BEGIN
SET #SQL = N'SELECT #nullCnt = COUNT(1) FROM ' + QUOTENAME(#schemaName) + N'.' + QUOTENAME(#tableName) +
N' WHERE ' + QUOTENAME(#columnName) + N' IS NULL;';
SET #paramDefinition = N'#nullCnt bigint OUTPUT';
PRINT #SQL;
exec sp_executesql #SQL,
#paramDefinition,
#nullCnt = #nullCnt OUTPUT;
INSERT INTO #Columns
( [schema_id]
, [object_id]
, column_id
, schemaName
, tableName
, columnName
, recordCnt
, nullCnt
, nullPct )
VALUES
( #schema_id
, #object_id
, #column_id
, #schemaName
, #tableName
, #columnName
, #recordCount
, #nullCnt
-- USE NULLIF in case there are no recods in the table
, ISNULL( #nullCnt * 1.0 / NULLIF( #recordCount, 0) * 100.0, 0 ) );
END
-- Set the #lastSchema_id and #lastTable_id so that on
-- the next loop, if it's the same table there is no
-- need to recount the columns for the table.
SET #lastSchema_id = #schema_id;
SET #lastTable_id = #object_id;
FETCH NEXT FROM c_Cursor
INTO #schema_id
, #object_id
, #column_id
, #schemaName
, #tableName
, #columnName
, #isNullable;
END;
CLOSE c_Cursor;
DEALLOCATE c_Cursor;
SELECT *
FROM #Columns;
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/3e097cad-f355-4f7e-b265-f7e6137670bf/challenge-how-to-count-nulls-for-each-column-in-every-table-in-vldb-etc?forum=transactsql
I'm using SQL Server 2008 and working off of the follow example HERE.
Below is the code I have so far. (The only difference is a turned it into a query from a stored procedure and removed schema name which wasn't necessary.)
What I'd like to do is add another column to the result and give the first column's data value. In other words if each table starts off with a Primary Key, my result will include the Primary Key of the row where the data was found. Is this even possible? Thanks.
DECLARE #DataToFind nvarchar(MAX) = ''
DECLARE #ExactMatch BIT = 0
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 TableName, ColumnName
FROM #Temp
WHERE DataFound = 1
Returning the primary key is absolutely possible. You will need to:
Determine the name if the primary key column or columns in each table. If you can stick to a single column, it will make your life much easier.
Add a variable for the content of the Primary Key value, say #PrimaryKeyValue
Modify #SQLTemplate to return the value of this column into #PrimaryKeyValue, something like this:
SELECT #SQLTemplate = CASE WHEN #ExactMatch = 1
THEN 'select #PrimaryKeyValue = min(' + #PrimaryKeyColumnName + ')
from ReplaceTableName
where Convert(nVarChar(4000), [ReplaceColumnName]) = ''' + #DataToFind + ''')'
ELSE 'select #PrimaryKeyValue = min(' + #PrimaryKeyColumnName + ')
from ReplaceTableName
where Convert(nVarChar(4000), [ReplaceColumnName]) Like ''%' + #DataToFind + '%'')'
END,
#PARAMETERS = '#PrimaryKeyValue nvarchar(4000) OUTPUT',
#i = 1
I was wondering if someone could help me with creating a while loop to iterate through several databases to obtain data from one table from two columns. this is was I have done so far. nothing works because i do not know how to make the select statement work through each database with regards to the table that I am querying from each database (dbo.tbldoc)
DECLARE #Loop int
DECLARE #DBName varchar(300)
DECLARE #SQL varchar(max)
DECLARE #tableName VARCHAR(255)
SET #Loop = 1
SET #DBName = ''
WHILE #Loop = 1
BEGIN
SELECT [name] FROM sys.databases
WHERE [name] like 'z%' and create_date between '2010-10-17' and '2011-01-15'
ORDER BY [name]
SET #Loop = ##ROWCOUNT
IF #Loop = 0
BREAK
SET #SQL = ('USE ['+ #DBNAME +']')
IF EXISTS(SELECT [name] FROM sys.tables WHERE name != 'dbo.tbldoc' )
BEGIN
SELECT SUM(PGCOUNT), CREATED FROM **dbo.tbldoc**
END
ELSE
--BEGIN
PRINT 'ErrorLog'
END
I would consider sp_MSForEachDB which is a lot easier...
Edit:
EXEC sp_MSForEachDB 'USE [?]; IF DB_NAME() LIKE ''Z%%''
BEGIN
END
'
CREATE TABLE #T
(dbname sysname NOT NULL PRIMARY KEY,
SumPGCOUNT INT,
CREATED DATETIME)
DECLARE #Script NVARCHAR(MAX) = ''
SELECT #Script = #Script + '
USE ' + QUOTENAME(name) + '
IF EXISTS(SELECT * FROM sys.tables WHERE OBJECT_ID=OBJECT_ID(''dbo.tbldoc''))
INSERT INTO #T
SELECT db_name() AS dbname, SUM(PGCOUNT) AS SumPGCOUNT, CREATED
FROM dbo.tbldoc
GROUP BY CREATED;
'
FROM sys.databases
WHERE state=0 AND user_access=0 and has_dbaccess(name) = 1
AND [name] like 'z%' and create_date between '2010-10-17' and '2011-01-15'
ORDER BY [name]
IF (##ROWCOUNT > 0)
BEGIN
--PRINT #Script
EXEC (#Script)
SELECT * FROM #T
END
DROP TABLE #T
My code to search for data from more than one database would be:
use [master]
go
if object_id('tempdb.dbo.#database') is not null
drop TABLE #database
go
create TABLE #database(id INT identity primary key, name sysname)
go
set nocount on
insert into #database(name)
select name
from sys.databases
where name like '%tgsdb%' --CHANGE HERE THE FILTERING RULE FOR YOUR DATABASES!
and source_database_id is null
order by name
Select *
from #database
declare #id INT, #cnt INT, #sql NVARCHAR(max), #currentDb sysname;
select #id = 1, #cnt = max(id)
from #database
while #id <= #cnt
BEGIN
select #currentDb = name
from #database
where id = #id
set #sql = 'select Column1, Column2 from ' + #currentDb + '.dbo.Table1'
print #sql
exec (#sql);
print '--------------------------------------------------------------------------'
set #id = #id + 1;
END
go
DECLARE #Loop int
DECLARE #MaxLoop int
DECLARE #DBName varchar(300)
DECLARE #SQL varchar(max)
SET #Loop = 1
SET #DBName = ''
set nocount on
SET #MaxLoop = (select count([name]) FROM sys.databases where [name] like 'Z%')
WHILE #Loop <= #MaxLoop
BEGIN
SET #DBName = (select TableWithRowsNumbers.name from (select ROW_NUMBER() OVER (ORDER by [name]) as Row,[name] FROM sys.databases where [name] like 'Z%' ) TableWithRowsNumbers where Row = #Loop)
SET #SQL = 'USE [' + #DBName + ']'
exec (#SQL)
...
...
set #Loop = #Loop + 1
END
***Note: I didn't add the check if exists here.
I ended up writing one last week on the fly for some stuff I was doing.
Blog post here:
http://tsells.wordpress.com/2012/02/14/sql-server-database-iterator/
Here is the code.
SET NOCOUNT ON
GO
use master
go
Declare
#dbname nvarchar(500),
#variable1 int,
#variable2 int,
#variable3 int,
#totaldb int = 0,
#totaldbonserver int = 0,
#totaldbwithmatches int = 0
-- Get non system databases
Declare mycursor CURSOR for select name, database_id from SYS.databases where database_id > 4 order by name desc
open mycursor
fetch next from mycursor into #dbname, #variable1
while (##FETCH_STATUS <> -1)
BEGIN
DECLARE #ParmDefinition NVARCHAR(500)
Declare #mysql nvarchar(500) = 'select #variable2OUT = COUNT(*) from [' + #dbname + '].INFORMATION_SCHEMA.TABLES where Upper(TABLE_NAME) like ''MyTable''';
SET #ParmDefinition = N'#variable2OUT int OUTPUT'
set #totaldbonserver = #totaldbonserver + 1
Execute sp_executesql #mysql, #ParmDefinition, #variable2 OUTPUT
if #variable2 = 1
BEGIN
DECLARE #ParmDefinition2 NVARCHAR(500)
Declare #mysql2 nvarchar(500) = 'select #variable2OUT = COUNT(*) from [' + #dbname + '].dbo.MyTable';
SET #ParmDefinition2 = N'#variable2OUT int OUTPUT'
Execute sp_executesql #mysql2, #ParmDefinition2, #variable3 OUTPUT
set #totaldb = #totaldb + 1
if #variable3 > 1
BEGIN
Print #dbname + ' matched the criteria'
set #totaldbwithmatches = #totaldbwithmatches + 1
END
ELSE
Select 1
END
fetch next from mycursor into #dbname, #variable1
END
PRINT 'Total databases on server: '
Print #totaldbonserver
PRINT 'Total databases tested () : '
Print #totaldb
PRINT 'Total databases with matches: '
Print #totaldbwithmatches
CLOSE mycursor
DEALLOCATE mycursor
This doesn't use a loop. Hope this helps!
Note that "TABLE_OWNER" is that same as "SCHEMA Owner" and "TABLE_TYPE" will identify if the item is a table OR view.
--This will return all tables, table owners and table types for all database(s) that are NOT 'Offline'
--Offline database information will not appear
Declare #temp_table table(
DB_NAME varchar(max),
TABLE_OWNER varchar(max),
TABLE_NAME varchar(max),
TABLE_TYPE varchar(max),
REMARKS varchar(max)
)
INSERT INTO #temp_table (DB_NAME, TABLE_OWNER, TABLE_NAME, TABLE_TYPE,REMARKS)
EXECUTE master.sys.sp_MSforeachdb 'USE [?]; EXEC sp_tables'
SELECT DB_NAME, TABLE_OWNER, TABLE_NAME, TABLE_TYPE
FROM #temp_table
--Uncomment below if you are seaching for 1 database
--WHERE DB_NAME = '<Enter specific DB Name>'
--For all databases other than 'System Databases'
WHERE DB_NAME not in ('master','model','msdn','tempdb')
order by 1,2,3
You don't have to use a "USE DATABASE" statement. You can select from the particular database table by using a 3 part identifier as in:
select * from MyDatabase.dbo.MyTable