I would like to search through an MS SQL Server DB for a specific value in the SQL Views.
This SO post is helpful to search all tables for a specific value (Find a value anywhere in a database) but does not cover Views.
Does anyone have any SQL Script they can share? Google has only turned up how to search through all the tables for the value.
This procedure (based on this tip from 2015) will build a separate search command for every string column in any view, table, or both, in any database, or all user databases. Note that I use sys.objects instead of INFORMATION_SCHEMA for reasons I outline here.
CREATE OR ALTER PROCEDURE dbo.SearchViewsAndOrTables
#SearchTerm nvarchar(255) = NULL,
#SingleDatabase nvarchar(128) = NULL,
#ViewsOnly bit = 0,
#TablesOnly bit = 0
AS
BEGIN
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
IF #SearchTerm IS NULL OR #SearchTerm NOT LIKE N'%[^%^_]%'
BEGIN
RAISERROR(N'Please enter a valid search term.', 11, 1);
RETURN;
END
CREATE TABLE #results
(
[database] sysname,
[schema] sysname,
[object] sysname,
[column] sysname,
ExampleValue nvarchar(4000)
);
DECLARE #DatabaseCommands nvarchar(max) = N'',
#ColumnCommands nvarchar(max) = N'';
SELECT #DatabaseCommands = #DatabaseCommands + N'
EXEC ' + QUOTENAME(name) + '.sys.sp_executesql
#ColumnCommands, N''#SearchTerm nvarchar(255)'', #SearchTerm;'
FROM sys.databases
WHERE database_id > 4 -- non-system databases
AND [state] = 0 -- online
AND user_access = 0 -- multi-user
AND LOWER(name) = LOWER(COALESCE(#SingleDatabase, name));
SET #ColumnCommands = N'DECLARE #q nchar(1),
#SearchCommands nvarchar(max);
SELECT #q = nchar(39),
#SearchCommands = N''DECLARE #VSearchTerm varchar(255) = #SearchTerm;'';
SELECT #SearchCommands = #SearchCommands + char(13) + char(10) + N''
SELECT TOP (1)
[db] = DB_NAME(),
[schema] = N'' + #q + s.name + #q + '',
[table] = N'' + #q + t.name + #q + '',
[column] = N'' + #q + c.name + #q + '',
ExampleValue = LEFT('' + QUOTENAME(c.name) + '', 4000)
FROM '' + QUOTENAME(s.name) + ''.'' + QUOTENAME(t.name) + ''
WHERE '' + QUOTENAME(c.name) + N'' LIKE #'' + CASE
WHEN c.system_type_id IN (35, 167, 175) THEN ''V''
ELSE '''' END + ''SearchTerm;''
FROM sys.schemas AS s
INNER JOIN sys.objects AS t
ON s.[schema_id] = t.[schema_id]
INNER JOIN sys.columns AS c
ON t.[object_id] = c.[object_id]
WHERE c.system_type_id IN (35, 99, 167, 175, 231, 239)
AND c.max_length >= LEN(#SearchTerm)
AND t.type IN ('
+ CASE #TablesOnly WHEN 1 THEN '''''' ELSE '''V''' END
+ ','
+ CASE #ViewsOnly WHEN 1 THEN '''''' ELSE '''U''' END
+ N');
PRINT #SearchCommands;
EXEC sys.sp_executesql #SearchCommands,
N''#SearchTerm nvarchar(255)'', #SearchTerm;';
INSERT #Results
(
[database],
[schema],
[object],
[column],
ExampleValue
)
EXEC [master].sys.sp_executesql #DatabaseCommands,
N'#ColumnCommands nvarchar(max), #SearchTerm nvarchar(255)',
#ColumnCommands, #SearchTerm;
SELECT [Searched for] = #SearchTerm;
SELECT [database],[schema],[object],[column],ExampleValue
FROM #Results
ORDER BY [database],[schema],[object],[column];
END
GO
Related
We're migrating a SQL Server and as part of preparing for this we need to know about any file access it will be doing to paths that may not exist any more.
There is a large amount of T-SQL we would need to go through to search for any reads / writes to file paths. I'm thinking I could search all of our T-SQL for keywords that do those operations to find potential problems.
Two example keywords I can think of are
OPENROWSET
QUERYOUT
Is there any others?
You want a list of keywords that might be in code somewhere that hard-codes a defunct path? Well, the list is very long... bulk insert, xp_cmdshell, sys.dm_os_file_exists, sp_OACreate, xp_fileexist, create database, alter database, restore database, backup database, ... enumerating this and somehow being sure you got them all will be difficult. It will certainly be easier to find all of the hard-coded paths (assuming you know the list of paths that have been removed) than to find all the keywords that might be involved in referencing them.
If you have removed a mapped drive called X:\, for example, it is much easier to search all procedures / job steps / etc. for %X:\% than it is to find all the places where you use BULK INSERT and then check each one of those to see if they reference X:\.
In either case, I put together this handy search procedure that searches code for a specific string. You can certainly use it to find all instances of BULK INSERT, and all instances of xp_cmdshell, etc. But again it will be a lot easier to search for the explicit paths / drive letters / network shares, and even use that as an opportunity to change the way your code references paths that can change (e.g. put them in config or in a table, instead of hard-coding them anywhere).
CREATE PROCEDURE dbo.FindStringInModules
#search_string nvarchar(4000),
#database_list nvarchar(max) = NULL,
#case_sensitive bit = 0,
#search_jobs bit = 0,
#search_job_and_step_names bit = 0,
#search_schema_names bit = 0,
#search_object_names bit = 0,
#search_column_names bit = 0,
#search_parameter_names bit = 0,
#search_system_objects bit = 0,
#search_system_databases bit = 0,
#search_everything bit = 0,
#debug bit = 0
AS
BEGIN
SET NOCOUNT ON;
IF #search_everything = 1
BEGIN
SELECT
#search_jobs = 1,
#search_job_and_step_names = 1,
#search_object_names = 1,
#search_schema_names = 1,
#search_column_names = 1,
#search_parameter_names = 1,
#search_system_objects = 1,
#search_system_databases = 1;
END
DECLARE #sql nvarchar(max),
#template nvarchar(max),
#exec nvarchar(1024),
#all_text nvarchar(128),
#coll_text nvarchar(128);
SELECT #sql = N'',
#template = N'',
#all_text = CASE #search_system_objects
WHEN 1 THEN N'all_' ELSE N'' END,
#coll_text = CASE #case_sensitive
WHEN 1 THEN N'Latin1_General_100_CS_AS_SC'
WHEN 0 THEN N'Latin1_General_100_CI_AS_SC'
END;
CREATE TABLE #o
(
[database] nvarchar(130),
[schema] nvarchar(130),
[object] nvarchar(130),
[type] nvarchar(130),
create_date datetime,
modify_date datetime,
column_name nvarchar(130),
param_name nvarchar(130),
definition xml
);
SET #search_string = N'%' + #search_string + N'%';
SET #template = N'
SELECT [database] = DB_NAME(),
[schema] = s.name,
[object] = o.name,
[type] = o.type_desc,
o.create_date,
o.modify_date,
[column_name] = $col$,
[param_name] = $param$,
definition = CONVERT(xml, ''<?query -- '' + QUOTENAME(DB_NAME())
+ CHAR(13) + CHAR(10) + OBJECT_DEFINITION(o.object_id)
+ CHAR(13) + CHAR(10) + ''--'' + CHAR(63) + ''>'')
FROM sys.$all$objects AS o
INNER JOIN sys.schemas AS s
ON o.[schema_id] = s.[schema_id]';
SET #sql = #sql + REPLACE(REPLACE(#template, N'$col$', N'NULL'), N'$param$', N'NULL')
+ N'
' + N' WHERE OBJECT_DEFINITION(o.[object_id]) COLLATE $coll$
' + N' LIKE #s COLLATE $coll$';
SET #sql = #sql + CASE #search_schema_names WHEN 1 THEN N'
OR s.name COLLATE $coll$
LIKE #s COLLATE $coll$' ELSE N'' END;
SET #sql = #sql + CASE #search_object_names WHEN 1 THEN N'
OR o.name COLLATE $coll$
LIKE #s COLLATE $coll$' ELSE N'' END;
SET #sql = #sql + CASE #search_column_names WHEN 1 THEN N';
' + REPLACE(REPLACE(#template, N'$col$', N'c.name'),N'$param$',N'NULL')
+ N'
INNER JOIN sys.$all$columns AS c ON o.[object_id] = c.[object_id]
AND c.name COLLATE $coll$
LIKE #s COLLATE $coll$;' ELSE N'' END;
SET #sql = #sql + CASE #search_parameter_names WHEN 1 THEN N';
' + REPLACE(REPLACE(#template, N'$col$', N'NULL'),N'$param$',N'p.name')
+ N'
INNER JOIN sys.$all$parameters AS p ON o.[object_id] = p.[object_id]
AND p.name COLLATE $coll$
LIKE #s COLLATE $coll$;' ELSE N'' END;
SET #sql = REPLACE(REPLACE(#sql, N'$coll$', #coll_text), N'$all$', #all_text);
DECLARE #db sysname, #c cursor;
SET #c = cursor FORWARD_ONLY STATIC READ_ONLY FOR
SELECT QUOTENAME(name) FROM sys.databases AS d
LEFT OUTER JOIN dbo.fn_split(#database_list, N',') AS s ON 1 = 1
WHERE
(
LOWER(d.name) = LOWER(LTRIM(RTRIM(s.value)))
OR NULLIF(RTRIM(#database_list), N'') IS NULL
)
AND d.database_id >= CASE #search_system_databases
WHEN 1 THEN 1 ELSE 5 END
AND d.database_id < 32767
AND d.state = 0;
OPEN #c;
FETCH NEXT FROM #c INTO #db;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #exec = #db + N'.sys.sp_executesql';
IF #debug = 1
BEGIN
RAISERROR(N'Running dynamic SQL on %s:', 1, 0, #db);
PRINT #sql;
END
ELSE
BEGIN
INSERT #o
(
[database],
[schema],
[object],
[type],
create_date,
modify_date,
column_name,
param_name,
definition
)
EXEC #exec #sql, N'#s nvarchar(4000)', #s = #search_string;
END
FETCH NEXT FROM #c INTO #db;
END
IF #debug = 0
BEGIN
SELECT [database],
[schema],
[object],
[type],
create_date,
modify_date,
column_name,
param_name,
definition
FROM #o
ORDER BY [database], [schema], [object], [column_name], [param_name];
END
/* jobs */
IF #search_jobs = 1
BEGIN
SET #template = N'SELECT
job_name = j.name,
s.step_id,
s.step_name,
j.date_created,
j.date_modified,
[command_with_use] = CONVERT(xml, N''<?query -- ''
+ QUOTENAME(s.database_name)
+ CHAR(13) + CHAR(10) + s.[command]
+ CHAR(13) + CHAR(10) + ''--'' + CHAR(63) + ''>'')
FROM msdb.dbo.sysjobs AS j
INNER JOIN msdb.dbo.sysjobsteps AS s
ON j.job_id = s.job_id
WHERE s.command COLLATE $coll$
LIKE #s COLLATE $coll$'
+ CASE #search_job_and_step_names WHEN 1 THEN
N' OR j.name COLLATE $coll$
LIKE #s COLLATE $coll$
OR s.step_name COLLATE $coll$
LIKE #s COLLATE $coll$'
ELSE N'' END
+ N' ORDER BY j.name, s.step_id;';
SET #sql = REPLACE(#template, N'$coll$', #coll_text);
IF #debug = 1
BEGIN
PRINT N'Running this for jobs:';
PRINT #sql;
END
ELSE
BEGIN
EXEC sys.sp_executesql #sql, N'#s nvarchar(4000)', #s = #search_string;
END
END
END
GO
I currently have a trigger on each table that handles a history log. The trigger is the exact same on every table. See below.
If I move this to a stored procedure, will it be faster?
Also if I use a stored procedure will the trigger release for the user to continue?
create trigger ' + #TABLE_NAME + '_ChangeTracking on ' + #TABLE_NAME + ' for
insert, update, delete
as
declare #bit int ,
#field int ,
#maxfield int ,
#char int ,
#fieldname varchar(128) ,
#TableName varchar(128) ,
#PKCols varchar(1000) ,
#sql nvarchar(max),
#Type nvarchar(1) ,
#PKValueSelect varchar(1000),
#MasterId nvarchar(max) = ''0''
select #TableName = ''' + #TABLE_NAME + '''
if exists(select * from CNF_HIL_Tables where referencetable = #TableName and Active = 1)
begin
if exists (select * from inserted)
if exists (select * from deleted)
select #Type = ''2''
else
select #Type = ''3''
else
select #Type = ''1''
select * into #ins from inserted
select * into #del from deleted
select #PKCols = coalesce(#PKCols + '' and'', '' on'') + '' i.'' + c.COLUMN_NAME + '' = d.'' + c.COLUMN_NAME
from INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk
inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE c on c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME and c.TABLE_NAME = pk.TABLE_NAME
where pk.TABLE_NAME = #TableName
and CONSTRAINT_TYPE = ''PRIMARY KEY''
select #PKValueSelect = coalesce(#PKValueSelect+''+'','''') + ''convert(varchar(100), coalesce(i.'' + COLUMN_NAME + '',d.'' + COLUMN_NAME + ''))''
from INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk
inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE c on c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME and c.TABLE_NAME = pk.TABLE_NAME
where pk.TABLE_NAME = #TableName
and CONSTRAINT_TYPE = ''PRIMARY KEY''
select #field = 0,
#maxfield = max(ORDINAL_POSITION)
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = #TableName
while #field < #maxfield
begin
select #field = min(ORDINAL_POSITION)
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = #TableName
and ORDINAL_POSITION > #field
select #bit = (#field - 1 )% 8 + 1
select #bit = power(2,#bit - 1)
select #char = ((#field - 1) / 8) + 1
if substring(COLUMNS_UPDATED(),#char, 1) & #bit > 0 or #Type in (''1'',''3'')
begin
select #fieldname = COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = #TableName and ORDINAL_POSITION = #field
if exists(select * from CNF_Hil_Columns INNER JOIN CNF_HIL_Tables ON CNF_HIL_Tables.TablesId = CNF_Hil_Columns.TablesId
where CNF_HIL_Tables.referencetable = #TableName and CNF_Hil_Columns.ColumnName = #fieldname
and CNF_Hil_Columns.Active = 1
)
begin
if #MasterId = 0
begin
select #sql = ''insert DATA_HIL_Master (OperationType, ReferenceTable, ReferenceId, UserId, WorkstationId, InsDateTime)''
select #sql = #sql + '' select '''''' + #Type + ''''''''
select #sql = #sql + '', '''''' + #TableName + ''''''''
select #sql = #sql + '','' + #PKValueSelect
select #sql = #sql + '',convert(varchar(1000),i.Last_UserId_Log)''
select #sql = #sql + '',convert(varchar(1000),i.Last_WorkstationId_Log)''
select #sql = #sql + '',convert(varchar(1000),i.Last_DateTime_Log)''
select #sql = #sql + '' from #ins i full outer join #del d''
select #sql = #sql + #PKCols
select #sql = #sql + '' SELECT #MasterId = SCOPE_IDENTITY() ''
EXECUTE sp_executesql #sql, N''#MasterId nvarchar(max) OUTPUT'', #MasterId OUTPUT
end
select #sql = ''insert data_HIL_Detail (MasterId, ColumnName, OriginalValue, ModifiedValue)''
select #sql = #sql + '' select convert(varchar(1000),'' + #MasterId + '')''
select #sql = #sql + '','''''' + #fieldname + ''''''''
select #sql = #sql + '', convert(varchar(1000),d.'' + #fieldname + '')''
select #sql = #sql + '', convert(varchar(1000),i.'' + #fieldname + '')''
select #sql = #sql + '' from #ins i full outer join #del d''
select #sql = #sql + #PKCols
EXECUTE sp_executesql #sql
END
END
END
END
I was actually searching for an answer to this question and stumbled in here.
I've found a lot of different answers, but as a student, I am currently being told that "stored procedures run quicker than individual SQL statements; this improves performance." So, it seems the answer is "yes".
However, it seems "performance" may be interpreted differently by different people. I'm not very experienced yet, so I don't really understand all the nuances yet. I've seen some comments attributing the difference to "cache", and others that suggest using stored procedure only because of better "control" for security and maintenance rather than anything performance related.
While reading my course material, I also came across something that might be relevant. This is from Beginning Databases with PostgreSQL: From Novice to Professional (Stones and Matthew, 2005):
Stored Procedures reside on the server side, not on the client side, adding to
access control. Invoked from the client side, only the results are passed on to the caller, this reduces network traffic. Several applications can use a single stored procedure, standardizing processing rules.
So, maybe that's what is meant by "performance".
Stored procedures also seem more similar to functions themselves, which are objects stored in a database and used by all other database objects. Whereas triggers are object associated with a table that runs a function.
Generally, Irrespective of trigger or stored procedure, you have got the same code. In trigger, you can not call it directly and in stored procedure, you are calling directly. So, whether you use trigger or stored procedure, execution wise it is same. The first time it is called, the execution plan is cached.
In your case, as you are using specifically using inserted, deleted tables, you should have different stored procedure code to implement auditing. Or you can consider using SQL Server temporal tables or Change Data Capture or SQL Server auditing
But, there are few disadvantages of using trigger.
It can make the transaction longer
Difficult to debug
This stored procedure creates a table in the database when I make the select * into, and because of that, when there is more than one user, an error appears and says SearchTMP table already exists, this happens even if I make a drop of that table. So I decided to make a temporary table, as the following code shows:
INSERT INTO #SQLTbl(Tablename, WHEREClause)
SELECT QUOTENAME(SCh.name) + '.' + QUOTENAME(ST.NAME),
(
SELECT '[' + SC.Name + ']' + ' LIKE ''' + REPLACE(SearchSTR.SearchString,'''','''''') + ''' 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
CROSS JOIN #SearchStringTbl SearchSTR
WHERE STY.name in ('varchar','char','nvarchar','nchar','text')
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
AND Sch.name = chktbls.Schemaname
WHERE ST.name <> '#MyTempTable' -- it was SearchTMP
GROUP BY ST.object_id, QUOTENAME(SCh.name) + '.' + QUOTENAME(ST.NAME);
UPDATE #SQLTbl SET SQLStatement = 'SELECT * INTO #MyTempTable FROM ' + Tablename + ' WHERE ' + substring(WHEREClause,1,len(WHEREClause)-5)
Since I didn't want to declare or create the table previously, the temptable will be created in the select * into. But when I try to run the procedure by filling the parameters it says:
>Msg 208, Level 16, State 0, Procedure SP_SearchTables_TEST, Line 265, Invalid object name #MyTempTable
The entire stored procedure:
ALTER PROCEDURE [dbo].[SearchTables_TEST] #SearchStr NVARCHAR(60) ,#GenerateSQLOnly Bit = 0 ,#SchemaNames VARCHAR(500) ='%' AS
SET NOCOUNT ON
DECLARE #MatchFound BIT
SELECT #MatchFound = 0
DECLARE #CheckTableNames Table
(
Schemaname sysname
,Tablename sysname
)
DECLARE #SearchStringTbl TABLE
(
SearchString VARCHAR(500)
)
DECLARE #SQLTbl TABLE
(
Tablename SYSNAME
,WHEREClause VARCHAR(MAX)
,SQLStatement VARCHAR(MAX)
,Execstatus BIT
)
DECLARE #SQL VARCHAR(MAX)
DECLARE #TableParamSQL VARCHAR(MAX)
DECLARE #SchemaParamSQL VARCHAR(MAX)
DECLARE #TblSQL VARCHAR(MAX)
DECLARE #tmpTblname sysname
DECLARE #ErrMsg VARCHAR(100)
IF LTRIM(RTRIM(#SchemaNames)) =''
BEGIN
SELECT #SchemaNames = '%'
END
IF CHARINDEX(',',#SchemaNames) > 0
SELECT #SchemaParamSQL = 'SELECT ''' + REPLACE(#SchemaNames,',','''as SchemaName UNION SELECT ''') + ''''
ELSE
SELECT #SchemaParamSQL = 'SELECT ''' + #SchemaNames + ''' as SchemaName '
SELECT #TblSQL = 'SELECT SCh.NAME,T.NAME
FROM SYS.TABLES T
JOIN SYS.SCHEMAS SCh
ON SCh.SCHEMA_ID = T.SCHEMA_ID
INNER JOIN [DynaForms].[dbo].[Enums_Tables] et on
(et.Id = T.NAME COLLATE Latin1_General_CI_AS) '
INSERT INTO #CheckTableNames
(Schemaname,Tablename)
EXEC(#TblSQL)
IF NOT EXISTS(SELECT 1 FROM #CheckTableNames)
BEGIN
SELECT #ErrMsg = 'No tables are found in this database ' + DB_NAME() + ' for the specified filter'
PRINT #ErrMsg
RETURN
END
IF LTRIM(RTRIM(#SearchStr)) =''
BEGIN
SELECT #ErrMsg = 'Please specify the search string in #SearchStr Parameter'
PRINT #ErrMsg
RETURN
END
ELSE
BEGIN
SELECT #SearchStr = REPLACE(#SearchStr,',,,',',#DOUBLECOMMA#')
SELECT #SearchStr = REPLACE(#SearchStr,',,','#DOUBLECOMMA#')
SELECT #SearchStr = REPLACE(#SearchStr,'''','''''')
SELECT #SQL = 'SELECT ''' + REPLACE(#SearchStr,',','''as SearchString UNION SELECT ''') + ''''
INSERT INTO #SearchStringTbl
(SearchString)
EXEC(#SQL)
UPDATE #SearchStringTbl
SET SearchString = REPLACE(SearchString ,'#DOUBLECOMMA#',',')
END
INSERT INTO #SQLTbl(Tablename,WHEREClause)
SELECT QUOTENAME(SCh.name) + '.' + QUOTENAME(ST.NAME),
(
SELECT '[' + SC.Name + ']' + ' LIKE ''' + REPLACE(SearchSTR.SearchString,'''','''''') + ''' 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
CROSS JOIN #SearchStringTbl SearchSTR
WHERE STY.name in ('varchar','char','nvarchar','nchar','text')
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
AND Sch.name = chktbls.Schemaname
WHERE ST.name <> '#MyTempTable' -- it was SearchTMP
GROUP BY ST.object_id, QUOTENAME(SCh.name) + '.' + QUOTENAME(ST.NAME);
UPDATE #SQLTbl SET SQLStatement = 'SELECT * INTO #MyTempTable FROM ' + Tablename + ' WHERE ' + substring(WHEREClause,1,len(WHEREClause)-5)
DELETE FROM #SQLTbl
WHERE WHEREClause IS NULL
DECLARE #output TABLE (Id VARCHAR(50), Name VARCHAR(100))
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('#MyTempTable','U') IS NOT NULL -- this line was uncomment
DROP TABLE #MyTempTable -- this line was uncomment
EXEC (#SQL)
IF EXISTS(SELECT 1 FROM #MyTempTable) -- It was like this: SearchTMP
BEGIN
SELECT #MatchFound = 1
INSERT INTO #output (Id, Name)
Select * from [DynaForms].[dbo].[Enums_Tables] where id in (SELECT parsename(#tmpTblname,1) FROM #MyTempTable) -- It was like this: SearchTMP
END
END
ELSE
BEGIN
PRINT REPLICATE('-',100)
PRINT #tmpTblname
PRINT REPLICATE('-',100)
PRINT replace(#SQL,'INTO #MyTempTable','')
END
UPDATE #SQLTbl
SET Execstatus = 1
WHERE Tablename = #tmpTblname
END
SELECT * FROM #output
IF #MatchFound = 0
BEGIN
SELECT #ErrMsg = 'No Matches are found in this database ' + DB_NAME() + ' for the specified filter'
PRINT #ErrMsg
RETURN
END
SET NOCOUNT OFF
OK, so a quick test actually answered this, and i can't believe I missed this (/facepalm). you can't use a INTO clause with dynamic SQL and a Temporary table and reference it afterwards; you need to CREATE the table. Consider the following:
DECLARE #SQL varchar(max);
SET #SQL = 'SELECT 1 AS A, 2 AS B INTO #test;';
EXEC (#SQL);
SELECT *
FROM #test;
DROP TABLE #test;
This returns the error:
Msg 208, Level 16, State 0, Line 5
Invalid object name '#test'.
And now, for the correct way:
DECLARE #SQL varchar(max);
CREATE TABLE #test (A int, B int);
SET #SQL = 'INSERT INTO #test SELECT 1 AS A, 2 AS B';
EXEC (#SQL);
SELECT *
FROM #test;
DROP TABLE #test;
This works fine. Thus, you need to CREATE your temporary table, not use an INTO clause.
Instead of
Object_Id('#MyTempTable','U')
You need to use
Object_Id('tempdb..#MyTempTable','U')
To get the object_id of the temporary table to check if it exists or not.
Temp tables are created in TempDB system database
I would like to create the stored procedure and generate insert statement for the table dynamically. The input parameters for the stored procedure are supposed to be schema, table name, #col1, #col2, ..., #colN. This stored procedure is supposed to take 1 random record from another server and based on this record is supposed to generate INSERT statement. #col1, #col2, ..., #colN parameters are optional in case you would like to overwrite original value with the one you need.
The insert record is supposed to look like that:
INSERT INTO schema_name.table_name VALUES (
col1,
col2,
...,
colN)
VALUES (
COALESCE(#col1, 'col1_value'),
COALESCE(#col2, 'col2_value'),
...,
COALESCE(#colN, 'colN_value')
);
Currently I can not realize how to take the real data and put it to the statement. What I already did is:
CREATE PROCEDURE dbo.GenerateSampleDataInsertSP
#SchemaName VARCHAR(255),
#TableName VARCHAR(255)
AS
SET NOCOUNT ON;
DECLARE #sql VARCHAR(MAX) = '',
#columns VARCHAR(MAX) = '',
#columnsWithCoalesce VARCHAR(MAX) = '';
SELECT c.name
INTO #column
FROM sys.tables t
JOIN sys.schemas s ON s.schema_id = t.schema_id
JOIN sys.columns c ON c.object_id = t.object_id
JOIN sys.types tt ON c.system_type_id = tt.system_type_id
WHERE t.name = #TableName
AND s.name = #SchemaName
AND tt.name NOT IN ( 'timestamp' );
SET #columns = NULL;
SELECT #columns = ISNULL(#columns + ', ', '') + name
FROM #column;
SET #sql = 'SELECT TOP 1 ' + #columns + ' FROM AnotherDatabase.' + #SchemaName + '.' + #TableName + ' ORDER BY NEWID();';
SET #sql = 'INSERT INTO [' + #SchemaName + '].[' + #TableName + '] (' + #columns + ') VALUES ();';
SELECT #sql;
I do not care about ideal code or solution. I need result and that's it.
UPDATED:
-- Example #1
USE tempdb
GO
/*CREATE PROCEDURE dbo.GenerateSampleDataInsertSP ...*/
CREATE TABLE dbo.Employee (ID INT, EmployeeName VARCHAR(255));
INSERT INTO dbo.Employee VALUES (1, 'John Smith');
EXEC dbo.GenerateSampleDataInsertSP #SchemaName = 'dbo', #TableName = 'Employees';
------------------------ EXPECTED OUTPUT OF THE PROCEDURE (NOT THE ACTION, BUT PLAIN TEXT) ------------------
INSERT INTO dbo.Employee
(
ID,
EmployeeName
)
VALUES
(
COALESCE(#ID, '1'),
COALESCE(#EmployeeName, 'John Smith')
);
-- Example #2
USE tempdb
GO
/*CREATE PROCEDURE dbo.GenerateSampleDataInsertSP ...*/
CREATE TABLE dbo.Orders (ID INT, OrderNbr VARCHAR(10), OrderDate DATE, CustomerID ID);
INSERT INTO dbo.Orders VALUES (7, '12345678', GETDATE(), 1024);
EXEC dbo.GenerateSampleDataInsertSP #SchemaName = 'dbo', #TableName = 'Orders';
------------------------ EXPECTED OUTPUT OF THE PROCEDURE (NOT THE ACTION, BUT PLAIN TEXT) ------------------
INSERT INTO dbo.Orders
(
ID,
OrderNbr,
OrderDate,
CustomerId
)
VALUES
(
COALESCE(#ID, '7'),
COALESCE(#OrderNbr,'12345678'),
COALESCE(#OrderDate, '2015-07-05'),
COALESCE(#CustomerId, '1024')
);
Ok, I'll answer my own question. As I said that I do not care about the code beauty and performance, I just need the result so anyone who would provide more elegant solution would be accepted as solved solution. Here is the code:
CREATE PROCEDURE dbo.GenerateSampleDataInsertSP
#SchemaName VARCHAR(255),
#TableName VARCHAR(255)
AS
SET NOCOUNT ON;
IF EXISTS ( SELECT name
FROM tempdb.sys.tables
WHERE name LIKE '%##record%' )
BEGIN
DROP TABLE ##record;
END;
DECLARE #sql VARCHAR(MAX) = '',
#columns VARCHAR(MAX) = '',
#columnsWithCoalesce VARCHAR(MAX) = '';
SELECT c.name
INTO #column
FROM sys.tables t
JOIN sys.schemas s ON s.schema_id = t.schema_id
JOIN sys.columns c ON c.object_id = t.object_id
JOIN sys.types tt ON c.system_type_id = tt.system_type_id
WHERE t.name = #TableName
AND s.name = #SchemaName
AND tt.name NOT IN ( 'timestamp' );
SET #columns = NULL;
SELECT #columns = ISNULL(#columns + ', ', '') + name
FROM #column;
SET #sql = 'SELECT TOP 1 ' + #columns + ' INTO ##record FROM AnotherDataBase.' + #SchemaName + '.' + #TableName + ' ORDER BY NEWID();';
EXEC (#sql);
SET #sql = 'INSERT INTO [' + #SchemaName + '].[' + #TableName + '] (' + #columns + ') VALUES (';
DECLARE #columnsCur CURSOR, #ColumnName VARCHAR(255), #tmpValue VARCHAR(MAX), #sqlCommand nvarchar(1000);
SET #columnsCur = CURSOR FOR
SELECT name
FROM #column;
OPEN #columnsCur;
FETCH NEXT
FROM #columnsCur INTO #ColumnName;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sqlCommand = 'SELECT #value=CAST(' + #ColumnName + ' AS VARCHAR(MAX)) FROM ##record;'
EXECUTE sp_executesql #sqlCommand, N'#value VARCHAR(MAX) OUTPUT', #value=#tmpValue OUTPUT
SET #sql = #sql + 'COALESCE(#'+ #ColumnName +', ''' + #tmpValue + '''),';
FETCH NEXT
FROM #columnsCur INTO #ColumnName;
END;
CLOSE #columnsCur;
DEALLOCATE #columnsCur;
SET #sql = #sql + ');'
SET #sql = REPLACE(#sql, ',);', ');');
SELECT #sql;
GO
For a given sql 2000 - 2008 server I want to find any table named dbo.[MyTable] on that server. In addition, how do I find all databases that have a table named [dbo].[MyTable] and [AnySchemaName].[MyTable]. Is there a simple sp_ command like spTables MyTable? Or 'sp_AllDatabaseTable [MyTable]'?
I want to print it out like:
ServerName Database SchemaName MyTable Date Created
----------- --------- ----------- --------- -------------
Thx
I really would prefer a solution that does not use either CURSORS nor sp_msforeachdb.
The solution below is gives you an idea, and you can adapt it to your own needs.
USE [master]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
BEGIN TRY
SET NOCOUNT ON
SET DATEFORMAT DMY
SET DEADLOCK_PRIORITY NORMAL;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
DECLARE #log NVARCHAR(MAX)
,#vCrlf CHAR(2);
SELECT #log = ''
,#vCrlf = CHAR(13)+CHAR(10);
DECLARE #SQL NVARCHAR(MAX)
BEGIN TRY DROP TABLE #OBJECTS END TRY BEGIN CATCH END CATCH
CREATE TABLE #OBJECTS(
DB_ID INT,
OBJECT_ID INT,
S_NAME SYSNAME,
NAME SYSNAME,
ROW_COUNT INT,
STATISTICS_UPDATED DATETIME)
SELECT #SQL = '
SELECT db_id=db_id(),
o.object_id,
s_name=s.name,
o.name,
ddps.row_count
,[Statistics_Updated]=STATS_DATE(I.OBJECT_ID,I.INDEX_ID)
FROM sys.indexes AS i
INNER JOIN sys.objects AS o ON i.OBJECT_ID = o.OBJECT_ID
INNER JOIN sys.schemas s ON s.schema_id = o.schema_id
INNER JOIN sys.dm_db_partition_stats AS ddps ON i.OBJECT_ID = ddps.OBJECT_ID
AND i.index_id = ddps.index_id
WHERE i.index_id < 2
AND o.is_ms_shipped = 0
'
set #SQL = (
SELECT STUFF(
(SELECT N' ' + ' USE ' + QUOTENAME(name) +';' + #vCrlf + #SQL + #vCrlf
FROM SYS.DATABASES SD
WHERE SD.STATE_DESC = 'ONLINE' -->Skips the database if it is not online
AND SD.COMPATIBILITY_LEVEL > 80
AND SD.database_id > 3 -- NO MASTER NOR TEMPDB NOR MODEL
FOR XML PATH(''),TYPE)
.value('text()[1]','nvarchar(max)'),1,2,N'')
)
INSERT INTO #OBJECTS
( [db_id],
[object_id],
[s_name],
[name],
[row_count],
[Statistics_Updated]
)
EXECUTE MASTER.DBO.sp_executesql #SQL
SELECT * FROM #OBJECTS
--WHERE NAME = 'THE NAME THAT I AM LOOKING FOR'
END TRY
BEGIN CATCH
PRINT '--EXCEPTION WAS CAUGHT--' + CHAR(13) +
'THE ERROR NUMBER:' + COALESCE(CAST ( ERROR_NUMBER() AS VARCHAR), 'NO INFO') + CHAR(13)
PRINT 'SEVERITY: ' + COALESCE(CAST ( ERROR_SEVERITY() AS VARCHAR), 'NO INFO') + CHAR(13) +
'STATE: ' + COALESCE(CAST ( ERROR_STATE() AS VARCHAR), 'NO INFO') + CHAR(13)
PRINT 'PROCEDURE: ' + COALESCE(CAST ( COALESCE(ERROR_PROCEDURE(),'NO INFO') AS VARCHAR), 'NO INFO') + CHAR(13) +
'LINE NUMBER: ' + COALESCE(CAST ( ERROR_LINE() AS VARCHAR), 'NO INFO') + CHAR(13)
PRINT 'ERROR MESSAGE: '
PRINT CAST ( COALESCE(ERROR_MESSAGE(),'NO INFO') AS NTEXT)
END CATCH;
I prefer a set based approach:
Declare #TableName as Varchar(255)
Set #TableName = '<MyTableName>'
Declare #SQL as Varchar(max)
Select #SQL = Coalesce(#SQL + '
', '') +
CASE
WHEN Row_Number() Over (Order by Database_ID) = 1
THEN ''
ELSE
'UNION ALL '
END +
'SELECT
''' + d.Name + ''' as DatabaseName
, s.Name as SchemaName
, o.Name as TableName
FROM ' + d.Name +'.Sys.Objects o
INNER JOIN ' + d.Name + '.Sys.Schemas s
ON o.Schema_ID = s.Schema_ID
WHERE o.Name like ''' + #TableName + ''''
FROM sys.databases d
where d.Name not like 'ReportServer%'
and d.Name not like 'SSISPackageRegistry'
Print #SQL
EXEC(#SQL)
Of course, you can use sp_msforeachdb for this purpose but you need to remember that fhis function is neither documented nor officially supported. Also, sometimes it breaks. More about it here Making a more reliable and flexible sp_MSforeachdb
You can use this script to search table by name in all databases.
I took it from Find table in every database of SQL server
DECLARE #TableName VARCHAR(256)
SET #TableName='YOUR_TABLE_NAME'
DECLARE #DBName VARCHAR(256)
DECLARE #varSQL VARCHAR(512)
DECLARE #getDBName CURSOR
SET #getDBName = CURSOR FOR
SELECT name
FROM sys.databases
CREATE TABLE #TmpTable (DBName VARCHAR(256),
SchemaName VARCHAR(256),
TableName VARCHAR(256),
create_date date, modify_date date)
OPEN #getDBName
FETCH NEXT
FROM #getDBName INTO #DBName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #varSQL = 'USE ' + #DBName + ';
INSERT INTO #TmpTable
SELECT '''+ #DBName + ''' AS DBName,
SCHEMA_NAME(schema_id) AS SchemaName,
name AS TableName,
create_date, modify_date
FROM sys.tables
WHERE name LIKE ''%' + #TableName + '%''' --WHERE name = '' + #TableName + ''' /* if you want to search by exact table name*/
EXEC (#varSQL)
FETCH NEXT
FROM #getDBName INTO #DBName
END
CLOSE #getDBName
DEALLOCATE #getDBName
SELECT *
FROM #TmpTable
DROP TABLE #TmpTable
Also, you may want to read this Find table name in all objects of all databases
I'd have said
sp_msforeachdb 'Select * from Sysobjects where name=''MyTable'''
But you don't need to, sysobjects is in the master table and does it for all databases anyway.
You should be able find the other columns easily enough.