TSQL - Changing databases dynamically - sql

I'm in a new company, and trying to write a proc that loops through the databases and looks up a keyword in all the tables, procs, etc.
It works great in theory, however I've noticed that the database never changes.
I've outputted the SQL, and ran it, no problem. But if I run the piece of code, it doesn't change.
I've also played around with the code and hard-coded the table name and gotten it to work, so could it be a security feature?
Here's the TSQL:
Declare #Dbname varchar(250); --Stores Database Name<br>
Declare #SearchTerm varchar(250); --What we're searching the DB for<br>
Declare #vSQL varchar(550)=''; --Variable that will hold our dynamic queries<br>
set #SearchTerm='LoadID'; <br>
select name
from sys.databases
where owner_sid != 0x01;
print #Dbname;
set #vSQL = 'use [' + #dbname + ']';
exec (#vSQL);
--exec #vSQL;
--execute (#vSQL);
--execute #vSQL);

EXEC runs in its own context. Put entire script into #vSql kind of
set #vSQL = 'use [' + #dbname + '];' +'--do what you need ' ;

Something I have that does this, and a bit more is the following proc. Should loop through all DB's on a given instance, look for a keyword in the definition of objects in those dbs, as well as if any jobs make use of those objects and their last execute duration.
USE [master]
GO
Create Proc dbo.DependencyCheck #SearchKey Varchar(150) As
Begin
--DB Refs
Drop Table If Exists #Databases
Create Table #Databases
(
DatabaseName Varchar(50) Primary Key
,Checked Int Default 0 Not Null
,SearchKey Varchar(150)
,DependencyText As 'Insert Into #Dependencies
Select Distinct '
+ '''' + DatabaseName + '''' +
',
D.id As ObjectID,
O.name As ObjectName,
xtype As ObjectType,
S.name As SchemaName
From '
+ DatabaseName + '.dbo.syscomments D
Inner Join ' + DatabaseName + '.dbo.sysobjects O
On O.ID = D.id
Inner Join ' + DatabaseName + '.sys.schemas As S
On s.schema_id = O.uid
Where D.text Like ''%' + SearchKey + '%'''
)
Insert Into #Databases (DatabaseName, SearchKey)
Select
Name, #SearchKey
From sys.sysdatabases As S
Where S.filename Not Like '%.SS' --Not DB Snapshots
Declare #TargetDatabase Varchar(25) = (Select Top 1 DatabaseName From #Databases As D Where D.Checked = 0)
--Dependency Table
Drop Table If Exists #Dependencies
Create Table #Dependencies
(
DatabaseName Varchar(50) Not Null,
ObjectID Int Not Null,
ObjectName Varchar(150) Not Null,
ObjectType Varchar(15) Not Null,
SchamaName Varchar(150) Not null
)
--Dynamic SQL for Insert
Declare #DynamicDependency Varchar(8000) = (Select D.DependencyText From #Databases As D Where D.DatabaseName = #TargetDatabase)
While #TargetDatabase Is Not Null
Begin
Exec(#DynamicDependency)
Update D Set Checked = 1 From #Databases D Where DatabaseName = #TargetDatabase
Set #TargetDatabase = (Select Top 1 DatabaseName From #Databases As D Where D.Checked = 0)
Set #DynamicDependency = (Select D.DependencyText From #Databases As D Where D.DatabaseName = #TargetDatabase)
End
;With JobStats As
(
Select Distinct
J.name,
JH.step_id,
First_Value(JH.run_duration) Over (Partition By J.name, JH.step_id Order By CA.StartTime Desc) As Last_Duration,
Avg(JH.run_duration) Over (Partition By J.name, JH.step_id) As Avg_Duration
From msdb.dbo.sysjobhistory As JH
Cross Apply (Select Convert(Datetime, Convert(varchar(8),run_date))
+ Convert(datetime, Stuff(STUFF(RIGHT(REPLICATE('0', 6) + CAST(run_time as varchar(6)), 6), 3, 0, ':'), 6, 0, ':')) As StartTime) CA
Inner Join msdb.dbo.sysjobs As J
On J.job_id = JH.job_id
Where JH.step_id <> 0
And JH.run_status = 1 -- Success
)
Select
D.ObjectID
,D.DatabaseName
,D.SchamaName
,D.ObjectName
,D.ObjectType
,J.name As JobName
,JS.step_id As JoBStepID
,JS.step_name As JobStepName
,JStat.Last_Duration
,JStat.Avg_Duration
From #Dependencies As D
Left Join msdb.dbo.sysjobsteps JS
On JS.command Like '%' + D.ObjectName + '%'
And JS.subsystem = 'TSQL'
Left Join msdb.dbo.sysjobs J
On J.job_id = JS.job_id
Left Join JobStats JStat
On JStat.name = J.name
And JStat.step_id = JS.step_id
Order By 1, 3
End

Related

Select all databases that have a certain table and a certain column

I need to create a query that is executed on all databases of my SQL server instance. An additional constraint is, that the query should only be executed on databases that contain a special table with a special column. Background is that in some databases the special table does (not) have the special column.
Based on this solution, what I have until now is a query that executes only on databases that contain a certain table.
SELECT *
FROM sys.databases
WHERE DATABASEPROPERTY(name, 'IsSingleUser') = 0
AND HAS_DBACCESS(name) = 1
AND state_desc = 'ONLINE'
AND CASE WHEN state_desc = 'ONLINE'
THEN OBJECT_ID(QUOTENAME(name) + '.[dbo].[CERTAIN_TABLE]', 'U')
END IS NOT NULL
However, what is still missing is a constraint that the query should only select databases where the table CERTAIN_TABLE has a specific column. How can this be achieved?
When i want to loop through all databases, i do a loop like the following. Its easy to follow:
DECLARE #dbs TABLE ( dbName NVARCHAR(100) )
DECLARE #results TABLE ( resultName NVARCHAR(100) )
INSERT INTO #dbs
SELECT name FROM sys.databases
DECLARE #current NVARCHAR(100)
WHILE (SELECT COUNT(*) FROM #dbs) > 0
BEGIN
SET #current = (SELECT TOP 1 dbName FROM #dbs)
INSERT INTO #results
EXEC
(
'IF EXISTS(SELECT 1 FROM "' + #current + '".INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = ''Target_Table_Name'' AND COLUMN_NAME = ''Target_Column_Name'')
BEGIN
--table and column exists, execute query here
SELECT ''' + #current + '''
END'
)
DELETE FROM #dbs
WHERE dbName = #current
END
SELECT * FROM #results
You are going to need either some looping or dynamic sql for this. I really dislike loops so here is how you could do this with dynamic sql.
declare #TableName sysname = 'CERTAIN_TABLE'
, #ColumnName sysname = 'CERTAIN_COLUMN'
declare #SQL nvarchar(max) = ''
select #SQL = #SQL + 'select DatabaseName = ''' + db.name + ''' from ' + QUOTENAME(db.name) + '.sys.tables t join ' + QUOTENAME(db.name) + '.sys.columns c on c.object_id = t.object_id where t.name = ''' + QUOTENAME(#TableName) + ''' and c.name = ''' + QUOTENAME(#ColumnName) + '''' + char(10) + 'UNION ALL '
from sys.databases db
where db.state_desc = 'ONLINE'
order by db.name
select #SQL = substring(#SQL, 0, len(#SQL) - 9)
select #SQL
--uncomment the line below when you are comfortable the query generated is correct
--exec sp_executesql #SQL

How can I return a distinct count of a variable for all tables in my database?

I have a SQL database with 60+ tables, almost all of which are populated with a CLIENTID field. I want to count the number of unique client IDs in each table.
The results I'm looking for are:
TABLE_NAME; CLIENTID_COUNT
dbo.HISTORY; 650
dbo.VISITS; 596
dbo.SALES; 1053
...; ...
This seems like it should be so simple but I've been playing around with cursors for hours and can't figure this one out. Please help!
IF OBJECT_ID('tempdb..#temp_RESULTS') IS NOT NULL DROP TABLE #temp_RESULTS
CREATE TABLE #TEMP_RESULTS
(
TABLENAME VARCHAR(MAX),
CLIENTCNT BIGINT
)
DECLARE #TABLENAME VARCHAR(MAX)
DECLARE #command VARCHAR(MAX)
IF OBJECT_ID('tempdb..#temp_PROCESS') IS NOT NULL DROP TABLE #temp_PROCESS
SELECT * INTO #TEMP_PROCESS FROM sys.tables
WHILE EXISTS(SELECT * FROM [#TEMP_PROCESS])
BEGIN
SET #TABLENAME = (SELECT TOP 1 [NAME] FROM [#TEMP_PROCESS])
SET #command = ('SELECT ''' + #TABLENAME + ''', COUNT(DISTINCT CLIENTID) AS CLIENTCNT FROM ' + #TABLENAME)
SELECT #command
INSERT INTO #TEMP_RESULTS
EXEC(#command)
DELETE FROM [#TEMP_PROCESS] WHERE [NAME] = #TABLENAME
END
SELECT * FROM [#TEMP_RESULTS]
Assuming the column is exactly ClientId in every table, you should be able to use this as is:
DROP TABLE IF EXISTS #clientId
CREATE TABLE #clientId
(
TableName nvarchar(1000),
ClientIdCount bigint
)
DECLARE #TableName nvarchar(1000);
DECLARE #CurrentQuery nvarchar(2000);
DECLARE result_cursor CURSOR local fast_forward FOR
SELECT DISTINCT
'['+TABLE_SCHEMA + '].[' + TABLE_NAME + ']'
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
COLUMN_NAME = 'ClientId'
OPEN result_cursor
FETCH NEXT FROM result_cursor into #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #CurrentQuery = 'SELECT ''' + #TableName + ''', COUNT(DISTINCT ClientId) FROM ' + #TableName
--print #CurrentQuery
INSERT INTO
#clientId
(
TableName,
ClientIdCount
)
EXEC(#CurrentQuery)
FETCH NEXT FROM result_cursor into #TableName
END
--end loop
--clean up
CLOSE result_cursor
DEALLOCATE result_cursor
GO
SELECT
*
FROM
#clientId
You could use dynamic sql.
This will read through your system tables, find those that have a ClientID column, and build the text of a query that's in the general shape of 'Select Count(DISTINCT ClientID)' from each table.
DECLARE #SQLQuery as nvarchar(max) = ''
------------------------------------
-- GET THE TABLES THAT HAVE A CLIENTID FROM SCHEMA
SELECT #SQLQuery = #SQLQuery + qryTxt FROM (
SELECT DISTINCT 'SELECT ''' + tables.name + ''', COUNT(DISTINCT CLIENTID) FROM ' + tables.name + ' UNION ' AS qryTxt
FROM sys.columns left join sys.tables on columns.object_id = tables.object_id where columns.name = CLIENTID AND isnull(tables.name, '') <> '') subquery
------------------------------------
-- REMOVE THE LAST 'UNION' KEYWORD FROM SQLQUERY
SET #SQLQuery = left(#sqlQuery, len(#sqlQuery) - 5)
------------------------------------
-- EXECUTE
execute sp_executesql #SQLQuery
I really dislike cursors and loops. Even though this is not going to be much difference for a performance perspective I like to share how you can leverage the system tables and dynamic sql to avoid using a cursor, while loop or temp tables for something like this.
This code is literally all you need to to do this.
declare #SQL nvarchar(max) = ''
select #SQL = #SQL + 'select TableName = ''' + t.name + ''', ClientID_Count = count(distinct clientID)
from ' + QUOTENAME(t.name) + ' UNION ALL '
from sys.tables t
join sys.columns c on c.object_id = t.object_id
where c.name = 'clientID'
select #SQL = left(#SQL, len(#SQL) - 10) --removes the last UNION ALL
select #SQL
--once your comfortable the dynamic sql is correct just uncomment the line below.
--exec sp_executesql #SQL
A similar pattern to other answers here, but this is how I would tackle it:
IF OBJECT_ID('#Tables', 'U') IS NOT NULL
DROP TABLE #Tables;
SELECT ID = IDENTITY(INT, 1, 1),
SchemaName = OBJECT_SCHEMA_NAME([object_id]),
TableName = OBJECT_NAME([object_id]),
ColumnName = name,
DistinctCount = 0
INTO #Tables
FROM sys.columns
WHERE name = 'CLIENTID';
DECLARE #ID INT = 1,
#MaxID INT = (SELECT MAX(ID) FROM #Tables);
WHILE #ID < #MaxID
BEGIN;
DECLARE #SQLCommand VARCHAR(MAX);
SELECT #SQLCommand = FORMATMESSAGE('
UPDATE #Tables SET DistinctCount = (
SELECT COUNT(DISTINCT %s) FROM %s.%s
)
WHERE ID = %i;',
QUOTENAME(ColumnName), QUOTENAME(SchemaName), QUOTENAME(TableName), ID)
FROM #Tables
WHERE ID = #ID;
EXEC (#SQLCommand);
SET #ID += 1;
END;
SELECT *
FROM #Tables;

Get all UNIQUEIDENTIFIER values based on variables in query

Goal is to get all UNIQUEIDENTIFIER values from all columns in database.
Code which is supposed to load all those values:
DECLARE #TableNames TABLE
(
ID INT NOT NULL IDENTITY(0, 1),
TableName NVARCHAR(50) NOT NULL,
ColName NVARCHAR(50) NOT NULL
);
DECLARE #Guids TABLE
(
ID INT NOT NULL IDENTITY(0, 1),
FoundGuid UNIQUEIDENTIFIER NOT NULL
);
DECLARE #Local NVARCHAR(50);
WHILE #Counter < 500
BEGIN
SELECT #Local = TableName FROM #TableNames WHERE Id = #Counter;
INSERT INTO #Guids EXEC('SELECT Id FROM [' + #Local + ']');
SET #Counter = #Counter + 1;
END;
Is this safe thing to do so? Eventually, what is the way to get those values?
I would use the system views to generate dynamic sql. This is 100% accurate and not limited to only those columns named Id. It won't matter what schema or column name is used. This will get you all those values with no looping at all.
declare #SQL nvarchar(max) = ''
select #SQL = #SQL + 'select ' + QUOTENAME(c.name) + ' = ' + QUOTENAME(c.name)
+ ' FROM ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name)
+ ' UNION ALL '
from sys.tables t
join sys.columns c on c.object_id = t.object_id
join sys.types ty on ty.user_type_id = c.user_type_id
join sys.schemas s on s.schema_id = t.schema_id
where ty.name = 'uniqueidentifier'
--removes the last UNION ALL
select #SQL = left(#SQL, len(#SQL) - 10)
select #SQL
--uncomment below to execute the dynamic sql when you are comfortable it is correct
--exec sp_executesql #SQL

How can I retrieve all the stored procedures and their input and output parameters in all the databases from SQL Server?

Apologies, for a convoluted question - I'm not a dba. Is there a simple script I can run that can list all the stored procedures I have on SQL Server, grouped by database and list them with the input and output parameters that go with the stored procedures.
I'm writing a similar script outside of SQL, to do the same for a language calling the stored procedures, so I find if there are conflicts in a legacy application.
Use the following script:
DECLARE #CurrentRowID INT
,#CurrentDatabase SYSNAME;
DECLARE #DynamicSQL NVARCHAR(MAX);
IF OBJECT_ID('tempdb..##DataSource') IS NOT NULL
BEGIN
DROP TABLE ##DataSource;
END;
CREATE TABLE ##DataSource
(
[database] SYSNAME
,[procedure] SYSNAME
,[parameter] SYSNAME
,[is_output] BIT
);
DECLARE #DataBases TABLE
(
[RowID] INT IDENTITY(1,1)
,[database] SYSNAME
);
INSERT INTO #DataBases ([database])
SELECT [name]
FROM [sys].[databases];
WHILE EXISTS(SELECT 1 FROM #DataBases)
BEGIN
SELECT TOP 1 #CurrentRowID = [RowID]
,#CurrentDatabase = [database]
FROM #DataBases;
SET #DynamicSQL = N'INSERT INTO ##DataSource
SELECT ''' + #CurrentDatabase + ''' AS [database]
,PR.[name]
,P.[name]
,P.[is_output]
FROM [' + #CurrentDatabase + '].[sys].[procedures] PR
INNER JOIN [' + #CurrentDatabase + '].[sys].[parameters] P
ON PR.[object_id] = P.[object_id]'
EXEC sp_executesql #DynamicSQL;
DELETE FROM #DataBases
WHERE [RowID] = #CurrentRowID;
END;
SELECT *
FROM ##DataSource
Of course, you can filter some of the databases, or add more columns from the sys.procedures dmv like system type for example.
Run a loop/cursor on sys.databases and inside the loop, USE and run the following query and keep taking union:
SELECT pr.name [Procedure], par.name Parameter, CASE WHEN is_output = 1 THEN 'Output Parameter' ELSE 'Input Parameter' END [ParameterType]
FROM sys.parameters par
INNER JOIN sys.procedures pr ON pr.object_id = par.object_id
ORDER BY [Procedure], [ParameterType]
This will give you all procedures and their dependent parameters.
declare #sql nvarchar(max) = ''
set #sql = #sql + N'union all
select '''+ quotename(d.name) + N''' as db, s.name as sname, p.name as p.name, r. name as paramname, r.is_output as paramoutput
from '''+ quotename(d.name) + N'''.sys.procedures p
join sys.parameters r on p.object_id = r.object_id
join sys.schemas s on p.schema_id = s.schema_id'
from sys.databases d
-- where d.name like ....
order by d.name
set #sql = stuff#sql, 1, 10, N'')
exec sp_executesql #sql
-- not tested

Script that pull the names of all the stored procedures that has functions in the where clause?

Can any one get me the Script that pull the names of all the stored procedures that has functions in the where clause?
For example ISNULL(fieldname,0)
Really hard to find
My first attempt is to run something like this
SELECT * FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_TYPE = 'PROCEDURE'
AND ROUTINE_DEFINITION LIKE '%WHERE%ISNULL%'
But you need to play with all functions. Better to join this query with a table contacting all built-in functions.
If am not wrong we cannot take the list of system functions.
refer this Extracting system functions in SQL Server 2008 R2
if the list is of user defined functions, you can try this way
SELECT ROUTINE_NAME
FROM INFORMATION_SCHEMA.ROUTINES A
JOIN SYS.OBJECTS B
ON A.ROUTINE_DEFINITION LIKE '%' + B.NAME + '%'
AND B.TYPE IN ( 'FN', 'IF', 'TF' )
AND A.ROUTINE_TYPE = 'PROCEDURE'
Below is one of the way to check function that utilized in stored procedures
DECLARE #nStart INT = 1
DECLARE #nEnd INT = (SELECT COUNT(1) FROM SYS.SYSOBJECTS WHERE XTYPE='TF'OR XTYPE='FN')
DECLARE #Query NVARCHAR(MAX) = ''
DECLARE #sUnion NVARCHAR(30) = ' UNION ALL '
DECLARE #FncName NVARCHAR(100)
WHILE #nStart<=#nEnd
BEGIN
SET #FncName = (SELECT NAME FROM (SELECT SO.NAME,ROW_NUMBER() OVER (ORDER BY SO.NAME) RN FROM SYS.SYSCOMMENTS SC INNER JOIN SYS.SYSOBJECTS SO
ON SO.ID = SC.ID WHERE SO.XTYPE = 'TF' OR SO.XTYPE = 'FN')T1 WHERE RN = #nStart)
SET #Query = #Query + ' SELECT ' + '''' + #FncName + ''''
IF(#nStart!=#nEnd)
BEGIN
SET #Query = #Query + #sUnion
END
SET #nStart = #nStart + 1
END
DECLARE #TempTable TABLE
(
ID INT IDENTITY(1,1),
TITLE NVARCHAR(MAX)
)
INSERT INTO #TempTable
EXEC SP_EXECUTESQL #Query
DECLARE #nTempTableStart INT = 1
DECLARE #nTempTableEnd INT = (SELECT COUNT(1) FROM #TempTable)
DECLARE #Condition NVARCHAR(MAX) = ' '
DECLARE #FunctionName NVARCHAR(100)
WHILE #nTempTableStart <= #nTempTableEnd
BEGIN
SET #FunctionName = (SELECT NAME FROM (SELECT TITLE 'NAME',ROW_NUMBER() OVER (ORDER BY TITLE) RN FROM #TempTable)T1 WHERE RN = #nTempTableStart)
SET #Condition = #Condition + ' LIKE ' + '''' + '%'+#FunctionName+'%' + ''''
IF #nTempTableStart!=#nTempTableEnd
SET #Condition = #Condition + ' OR SC.TEXT'
IF #nTempTableStart = #nTempTableEnd
SET #Condition = #Condition + ' ) '
SET #nTempTableStart = #nTempTableStart + 1
END
DECLARE #FinalQuery NVARCHAR(MAX)
SET #FinalQuery = 'SELECT SO.NAME,SC.TEXT FROM
SYS.SYSCOMMENTS SC INNER JOIN SYS.SYSOBJECTS SO ON SC.ID = SO.ID WHERE (SC.TEXT ' + #Condition
+ ' AND (SO.XTYPE !=''TF'' AND SO.XTYPE !=''FN'')'
EXECUTE SP_EXECUTESQL #FinalQuery