Execute queries stored in columns of a SQL Server table - sql-server

I have a table with 2 columns QueryName and Query. I am trying the execute the queries stored in the Query column of the table.
I want to display anything that has more than record count of zero, we need to print to output with queryname and count. I am using the following cursor, I was able to display rowcount but anyone please suggest me how to display the QueryName:
DECLARE #Sql NVARCHAR(MAX);
DECLARE Cur CURSOR LOCAL FAST_FORWARD FOR
(SELECT Query
FROM VWLetterTYB )
OPEN Cur
FETCH NEXT FROM Cur INTO #Sql
WHILE (##FETCH_STATUS = 0)
BEGIN
--Exec sp_executesql #Sql
EXEC ('SELECT COUNT(*) AS Rowcounts FROM (' + #sql + ') AS t HAVING COUNT(*) > 0 ')
FETCH NEXT FROM Cur INTO #Sql
END
CLOSE Cur
DEALLOCATE Cur;

DECLARE #Sql NVARCHAR(MAX);
DECLARE Cur CURSOR LOCAL FAST_FORWARD FOR
(SELECT Query
FROM VWLetterTYB )
OPEN Cur
FETCH NEXT FROM Cur INTO #Sql
WHILE (##FETCH_STATUS = 0)
BEGIN
--Exec sp_executesql #Sql
EXEC ('SELECT [sql] = ''' + #Sql + ''', COUNT(*) AS Rowcounts FROM (' + #sql + ') AS t HAVING COUNT(*) > 0 ')
FETCH NEXT FROM Cur INTO #Sql
END
CLOSE Cur
DEALLOCATE Cur;

If I understand you correctly, you have two fields in the table but you only managed to get one using the cursor. In that case, the solution is easy, you can fetch more than one value with a cursor (each one to a different variable), then just put that value in the dynamic SELECT:
DECLARE #Sql NVARCHAR(MAX), #QueryName NVARCHAR(MAX);
DECLARE Cur CURSOR LOCAL FAST_FORWARD FOR
(SELECT Query, QueryName
FROM VWLetterTYB )
OPEN Cur
-- variables are filled in the order used in the SELECT (first column to first variable,
-- second column to second variable, etc.)
FETCH NEXT FROM Cur INTO #Sql, #QueryName
WHILE (##FETCH_STATUS = 0)
BEGIN
-- when using single quotes inside a string you have to double it
EXEC ('SELECT '''+ #QueryName+''' AS QueryName, COUNT(*) AS Rowcounts FROM (' + #sql + ') AS t HAVING COUNT(*) > 0 ')
FETCH NEXT FROM Cur INTO #Sql, #QueryName
END
CLOSE Cur
DEALLOCATE Cur;

I don't have an answer, but a further refinement of the question. The examples here are using the stored SQL as a standalone command. Can a snippet of SQL stored in a table's column be used as an item in a SELECT list?
I have a situation where I have a table of code/name lookup pairs. Most codes lead to a single name. But something logic needs to be applied to determine the name. I would like to have this seamless in my master SQL statement. I have to apply this logic from the same lookup table in many scripts. I don't want to have to embed complex logic in each script or maintain a convoluted stored procedure.

Related

Stored procedure - Only return if rows > 0

I have this stored procedure which executes a lot of SQL queries and returns the results. How do i go about returning only the results from the queries that returns rows?
BEGIN
DECLARE #Query varchar(4000)
DECLARE cur CURSOR FOR SELECT SQLSyntax FROM tblChecks
OPEN cur
FETCH NEXT FROM cur INTO #Query
WHILE ##FETCH_STATUS = 0 BEGIN
EXEC (#Query)
FETCH NEXT FROM cur INTO #Query
END
CLOSE cur
DEALLOCATE cur
END
Please help me...
To avoid returning empty result sets, you have to conditionally run each query. For instance:
declare #query varchar(4000)
declare cur cursor local for
select 'if exists (' + SQLSyntax + ') ' + SQLSyntax from tblChecks
open cur
fetch next from cur into #query
while ##fetch_status = 0
begin
exec(#query)
fetch next from cur into #query
end
close cur
deallocate cur
Or, if every result set is identical, you could create a temp table first, and then insert all values into the temp table, and then do a single select statement at the end from the temp table:
create #temp (field1 type null, field2 type null, field3 type null)
declare #query varchar(4000)
declare cur cursor local for
select 'insert into #temp ' + SQLSyntax from tblChecks
open cur
fetch next from cur into #query
while ##fetch_status = 0
begin
exec(#query)
fetch next from cur into #query
end
close cur
deallocate cur
select * from #temp
drop table #temp
If possible, the 2nd option is better, because it will only run each query one time. The 1st option will run each query twice (that returns data), once to test if there are any results and once to return the data. This is not efficient! Hopefully all queries return the same fields in the results and you can use the 2nd option.
You can also execute and evaluate the results using the If exists strategy.
IF EXISTS(exec(#query))
BEGIN
exec(#query)
END

Facilitating shorthand referencing for linked server with synonyms

I'm using a linked server and finding it very painful to write queries like this:
select * from [10.150.10.109].lhf.[dbo].[TABLE_NAME]
Is it possible to use a synonym for something like this:
CREATE SYNONYM [DataRelay] FOR [10.150.10.109].[lhf].[dbo]
in order to be able to query like this:
select * from DataRelay.TABLE_NAME
Without the capabilities of Intellisense, this is just painful...
No, there is no short hand for linked servers, however, you can alias tables in your queries to make it a bit easier.
select * from [10.150.10.109].lhf.[dbo].[TABLE_NAME] T
WHERE
T.FieldName=1
OR
T.FieldName=2
Now that I had a minute what I was saying in my comment is that you cannot create a synonym for just part of an object path as you desire. But you can dynamically script the drop and creation of synonyms for any object in your remote database pretty easily. here is an example of how to do if for user tables. For other objects you can use the sys.objects instead of sys.table system view.
Technique key words for more learning. Dynamic SQL, cursor, schema views.
DECLARE #ServerAndDB SYSNAME = '[10.150.10.109].[lhf]'
DECLARE #SynonymSchema SYSNAME = '[syn]'
DECLARE #ObjectPath NVARCHAR(1000)
DECLARE #SynonymName NVARCHAR(1000)
DECLARE CursorName CURSOR FOR
SELECT
#ServerAndDB + QUOTENAME(SCHEMA_NAME(t.schema_id)) + '.' + QUOTENAME(t.name) as ObjectPath
,#SynonymSchema + '.' + QUOTENAME(t.name) as SynonymName
FROM
[10.150.10.109].[lhf].sys.tables t
WHERE
t.type = 'U'
OPEN CursorName
FETCH NEXT FROM CursorName
INTO #ObjectPath, #SynonymName
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN TRY
DECLARE #SQL NVARCHAR(MAX)
IF EXISTS (SELECT * FROM sys.synonyms WHERE object_id = OBJECT_ID(#SynonymName))
BEGIN
SET #SQL = 'DROP SYNONYM ' + #SynonymName
EXECUTE sp_executesql #SQLString
SET #SQL = ''
END
SET #SQL = 'CREATE SYNONYM ' + #SynonymName + ' FOR ' + #ObjectPath
EXECUTE sp_executesql #SQLString
SET #SQL = ''
END TRY
BEGIN CATCH
--Can do error handling here
END CATCH
FETCH NEXT FROM CursorName
INTO #ObjectPath, #SynonymName
END
CLOSE CursorName
DEALLOCATE CursorName

SQL Server stored procedure to empty specific tables

I have a stored procedure which is supposed to truncate specific tables based on their names.
Tables which do not have "A" as the first character in their names must be truncated.
The problem is that this code doesn't work! Please help
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[return_data]
#DBName varchar(100)
AS
declare #i int, #tc int
set #i = 1
DECLARE #sql NVARCHAR(100)
SET #sql = 'USE ' + QUOTENAME(#DBName) + '
DECLARE cur CURSOR FOR SELECT TABLE_NAME FROM information_schema.tables
'
EXECUTE(#sql)
OPEN cur
declare #tbl_name nvarchar(100)
declare #first_char char(1)
FETCH NEXT FROM cur INTO #tbl_name
WHILE ##FETCH_STATUS = 0 BEGIN
set #first_char = SUBSTRING(#tbl_name, 0, 1)
set #sql = 'DELETE FROM ' + QUOTENAME(#tbl_name)
if (#first_char != 'A')
begin
EXECUTE(#sql)
end
FETCH NEXT FROM cur INTO #tbl_name
END
CLOSE cur
DEALLOCATE cur
return
The problem is with the SUBSTRING function. The starting position is a 1-based ordinal, not 0-based. Try
SUBSTRING(#tbl_name, 1, 1)
or
LEFT(#tbl_name, 1)
Also, I suggest you make it a habit of schema-qualifying table names to avoid ambiguity.

SQL Server cursor - loop through multiple servers and execute query

I have the following code (cursor):
DECLARE #SN VARCHAR(20);
DECLARE #sql NVARCHAR(MAX);
DECLARE C CURSOR LOCAL FAST_FORWARD
FOR SELECT DISTINCT(SERVERNAME) FROM INSTALLATION
where DATABASETYPE = 'MsSql' AND SERVERNAME IN ('x');
OPEN C;
FETCH NEXT FROM C INTO #SN;
WHILE (##FETCH_STATUS = 0)
BEGIN
PRINT #SN;
-- you could loop here for each database, if you'd define what that is
SELECT name
FROM master.dbo.sysdatabases
WHERE name not in ('master','model','msdb','tempdb');
SET #sql = N'SELECT TOP 1 NAME FROM TABLE ';
EXEC sp_executesql #sql;
FETCH NEXT FROM C INTO #SN;
END
CLOSE C;
DEALLOCATE C;
I would like to be able to loop through every server and execute a select statement on some (not all) of their databases.
The query is something like:
SELECT TOP 1 NAME FROM TABLE
The server from where I am running the cursor has all the others as linked servers.
DECLARE #SN VARCHAR(20);
DECLARE C CURSOR LOCAL FAST_FORWARD
FOR SELECT DISTINCT(SERVERNAME) FROM TABLE
where SERVERNAME NOT IN ('SRV1','SRV2','SRV3');
OPEN C;
FETCH NEXT FROM C INTO #SN;
WHILE (##FETCH_STATUS = 0)
BEGIN
PRINT #SN;
-- you could loop here for each database, if you'd define what that is
SET #sql = N'SELECT * FROM ' + #SN + '.master.dbo.TABLE;';
EXEC sys.sp_executesql #sql;
FETCH NEXT FROM C INTO #SN;
END
CLOSE C;
DEALLOCATE C;
Changes:
There is no reason to use the default cursor options here - global, updatable, dynamic, scrollable, etc. Background.
As a habit / best practice, use sp_executesql and not EXEC(). While it doesn't really matter in this case, it can matter in others, so I'd prefer to always code the same way. Background.
Also, please get in the habit of terminating your statements with semi-colons. You'll have to, eventually. Background.
EDIT
Now that we have a little more information about your actual requirements, I suggest this bit of code. Oh, and look, no cursors (well, no explicit cursor declarations and all the scaffolding that comes with them)!
SET NOCOUNT ON;
DECLARE #dbs TABLE(SERVERNAME SYSNAME, DBNAME SYSNAME);
DECLARE #sql NVARCHAR(MAX) = N'';
-- first, let's get the databases on each server:
SELECT #sql += N'SELECT ''' + SERVERNAME + ''', name FROM '
+ QUOTENAME(SERVERNAME) + '.master.sys.databases
WHERE database_id > 4
AND name NOT IN (N''somedb'',N''someotherdb'');'
FROM dbo.INSTALLATION
WHERE DATABASETYPE = 'MsSql'
AND SERVERNAME IN ('x');
INSERT #dbs EXEC sys.sp_executesql #sql;
SELECT #sql = N'';
-- now, build a command to run in each database context:
SELECT #sql += N'
EXEC ' + QUOTENAME(SERVERNAME) + '.'
+ QUOTENAME(DBNAME) + '.sys.sp_executesql #sql;'
FROM #dbs;
-- feel free to change the 3rd parameter here:
EXEC sys.sp_executesql #sql, N'#sql NVARCHAR(MAX)',
N'SELECT ##SERVERNAME, DB_NAME(), actual_columns FROM dbo.table_name;';
This will fail if table_name doesn't exist, so you may still have some work to do if you want to facilitate error handling. But this should get you started.
Also, please be conscious of, and consistently use, the schema prefix. Background.

Run operations on all the tables in all the databases

I'm trying to create a SQL Server script that applies some operations to all the tables in all the databases. I need to rename some tables if some conditions are respected, truncate the tables otherwise.
This is my script
EXEC sp_MSforeachdb
#command1 = '
IF not exists(select 1 where ''?'' in (''master'',''model'',''msdb'',''tempdb''))
EXEC [?].dbo.sp_MSforeachtable
#command1 = ''
IF(substring(&, 1, 3)=pv_ and right(&, 5) != _data and right(&, 4) != _BCK)
exec sp_RENAME & , &_BCK''
ELSE IF (right(&, 4) != _BCK)
TRUNCATE TABLE &
#replacechar = ''&'''
I got some errors but I'm new to SQL Server and I have not idea how to fix this script.
Any suggestions?
Many thanks
Here is a solution for start. It won't be quick, but it loops all tables of all databases on the server. Inside in the second cursor you can deceide what to do with the table.
(The query is not optimalized, just a quick solution)
DECLARE #DBName NVARCHAR(50)
DECLARE #TableName NVARCHAR(100)
DECLARE #DynamicSQL NVARCHAR(300)
DECLARE #DBCursor CURSOR
SET #DBCursor = CURSOR FOR
SELECT NAME FROM SYS.DATABASES
WHERE NAME NOT IN ('master','tempdb','model','msdb')
OPEN #DBCursor
FETCH NEXT FROM #DBCursor INTO #DBName
WHILE ##FETCH_STATUS = 0
BEGIN
CREATE TABLE #TempTableDatas
(
name varchar(100),
objectID int
)
SET #DynamicSQL = 'INSERT INTO #TempTableDatas
SELECT name, object_id FROM [' + #DBName + ']' + '.sys.Tables '
EXEC SP_EXECUTESQL #DynamicSQL
DECLARE #TableCursor CURSOR
SET #TableCursor = CURSOR FOR
SELECT name FROM #TempTableDatas
OPEN #TableCursor
FETCH NEXT FROM #TableCursor INTO #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #TableName, #DBName
FETCH NEXT FROM #TableCursor INTO #TableName
END
CLOSE #TableCursor
DEALLOCATE #TableCursor
DROP TABLE #TempTableDatas
FETCH NEXT FROM #DBCursor INTO #DBName
END
CLOSE #DBCursor
DEALLOCATE #DBCursor

Resources