iterative executing stored procedure with a set based approach - sql

I have an issue where I am trying to replace the following code with a different solution. Currently I am using a cursor but it is running to slowly. I am under the understanding that iterative solutions can only be completed with cursors or while loops but I am trying to find a set based approach and running out of ideas. I was hoping that I could find some inspiration here. Thanks all.
--used to find a unique list of Some_ID
#Id1, #Id2, #Id3
DECLARE SomeCursor CURSOR FOR
SELECT SOME_ID FROM SomeTable
WHERE ID1=#Id1 AND ID2=#Id2 and ID3=#Id3
OPEN SomeCursor
FETCH NEXT FROM SomeCursor INTO #SomeID
WHILE ##Fetch_Status = 0
BEGIN
Print #SomeID
--simply populates a single table with values pulled from
--other tables in the database based on the give parameters.
EXEC SP_PART1 #SomeID, #parameters...
print 'part 2 starting'
EXEC SP_PART2 #SomeID, #parameters...
FETCH NEXT FROM SomeCursor INTO #SomeID
print getdate()
END
CLOSE SomeCursor;
DEALLOCATE SomeCursor;

Your only option to make this set-based is to rewrite the sps to make them set-based (using table-valed parameters intead of individual ones) or to write set based code in this proc instead of re-using procs designed for single record use. This is a case where code re-use is usually not appropriate.

I'm not too sure what you want, but why not use your select statement to create your sql scripts and execute them all at once with something like this.
DECLARE #sql VARCHAR(MAX);
SELECT #sql = COALESCE(#sql,'') + 'EXEC SP_Part1 ' + SOME_ID + '; EXEC SP_Part2 ' + SomeID + '; GO '
FROM SomeTable
WHERE ID1=#Id1 AND ID2=#Id2 and ID3=#Id3
EXEC (#sql)

Related

SQL Server 2008 r2: How to check all views for runtime errors?

I have a LOT of views in the database.
Each view ofc refers to one or more tables.
There was some work done with those tables (alter, delete columns) and now i need to check all views for any runtime errors.
I went straithforward: got list of all views, iterated over it and launch SELECT TOP 0 * FROM view_name dynamically so any errors should appear in the Messages pane.
This is my code
DECLARE #view_name_template varchar(max) = '%'
DECLARE #columnList varchar(75) = '*'
--------------------------
DECLARE #tmp_views AS TABLE (view_name varchar(max))
DECLARE #view_name varchar(max)
DECLARE #sqlCommand nvarchar(max)
DECLARE #num int = 1
DECLARE #total_count int
SET NOCOUNT ON
INSERT INTO #tmp_views
SELECT name FROM sys.views
WHERE name LIKE #view_name_template
SELECT #total_count = COUNT(*) FROM sys.views WHERE name LIKE #view_name_template
DECLARE db_cursor CURSOR FOR
SELECT view_name FROM #tmp_views ORDER BY LOWER(view_name)
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #view_name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sqlCommand = 'SELECT TOP 0 ' + #columnList + ' FROM ' + #view_name
PRINT CAST(#num as varchar(31)) + '/' + CAST(#total_count as varchar(31)) + ' ' + #sqlCommand
EXECUTE sp_executesql #sqlCommand
FETCH NEXT FROM db_cursor INTO #view_name
SET #num = #num + 1
END
CLOSE db_cursor
DEALLOCATE db_cursor
It works fine except it completely freezes on some views (select from those views in other window works extremely fast and fine). I think it is server a memory overflow issue or something similar.
Tell me please: what is the lightweighiest way to check view has errors or not? Maybe SQL Server has a special function or stored procedure?
The code is not "hanging". It is waiting for the view to run, despite the top 0.
SQL Server offers several ways of testing queries. In addition to the top 0, you also have:
`set parseonly1
set noexec on
And then the more recent sp_describe_first_result_set.
Each of these do different things. parseonly checks for syntax errors but doesn't look at table layouts. I believe noexec completely compiles the query, creating the execution plan. top 0 will compile the query and also run it.
In some cases, the optimizer may not recognize that a query that returns no rows might need to do no work. For instance, there might be subqueries that are run despite the top 0, and this is causing the delay.
Two approaches. The first is to use noexec on (documented here). The second, if feasible, would be to create another database with the same structure and no data. You can then test the queries on that database.

Use SELECT results as a variable in a loop

I've searched here and elsewhere, and haven't found an answer yet. Hope I didn't miss it.
Using SQL Server Management Studio 2008 R2.
I have n specific databases on my server (there are other DBs as well, but I'm only interested in some of them)
Each of these databases has a table within it, which all have the same name. The only difference is the DB name. I want to aggregate these tables together to make one big table on a different database (different to the other DBs).
I can get the db names from the results of a query.
N is unknown.
Is a loop the way to go about this?
I was thinking something along the lines of the following pseudocode:
Set #dbnames = SELECT DISTINCT dbname FROM MyServer.dbo.MyTable
For each #name in #dbnames
INSERT INTO ADifferentDB.dbo.MyOtherTable
SELECT * FROM #name.dbo.table
Next name
(Clearly I'm new to using SQL variable as well, as you can see)
Your first problem is about iterating the databases: you cand do that with a cursor
Then you have another problem, executing a query where part of it is variable (database's name). You can do that with execute function.
All that is something similar to this:
DECLARE #query VARCHAR(max)
DECLARE #dbname VARCHAR(100)
DECLARE my_db_cursor CURSOR
FOR SELECT DISTINCT dbname FROM MyServer.dbo.MyTable
OPEN my_db_cursor
FETCH NEXT FROM my_db_cursor
INTO #dbname
WHILE ##FETCH_STATUS = 0
BEGIN
SET #query = 'INSERT INTO ADifferentDB.dbo.MyOtherTable
SELECT * FROM ' + #dbname + '.dbo.table'
EXECUTE(#query)
FETCH NEXT FROM my_db_cursor
INTO #dbname
END
CLOSE my_db_cursor
DEALLOCATE my_db_cursor
what you want to do is define a CURSOR for row-level operation. here is some doc
I would suggest using sp_MSForEachDB:
EXEC sp_MSForEachDB '
-- Include only the databases you care about.
IF NOT EXISTS (
SELECT *
FROM MySever.dbo.MyTable
WHERE dbname = ''?''
)
-- Exit if the database is not in your table.
RETURN
-- Otherwise, perform your insert.
INSERT INTO ADifferentDB.dbo.MyOtherTable
SELECT * FROM ?.dbo.table
'
In this case, ? is a token that is replaced with each database on the server.

Is there a better way to execute dynamic SQL that doesn't use a cursor?

I have dynamic SQL stored in a SQL table that I have to execute under certain conditions. Currently, we use cursors to handle that for us, but I was always told to avoid cursors when possible as they aren't the most efficient way of doing things. So, my question is: how do I execute dynamic SQL without them (if there's a way)? The entire system is built around this dynamic SQL mess, so there is no changing it.
For this, just assume the table has Id AS IDENTITY and SQL AS VARCHAR fields, where the SQL field contains the SQL to be executed (obviously).
EDIT:
Basically, I want to loop through the table and execute the SQL in the SQL column.
So, a row in the table will basically look like this:
ID SQL
-- ----------------------
1 SELECT * FROM RECORD
2 SELECT * FROM PERSON
3 SELECT * FROM LOCATION
I haven't written any code because what I'd write is a cursor to traverse through the table and execute it. I just don't know of any other ways of looping a table and executing that string as a SQL query other than something like:
DECLARE #sql VARCHAR(MAX)
DECLARE _cursor CURSOR
FOR
SELECT [SQL]
FROM #tmp2
OPEN _cursor
FETCH NEXT FROM _cursor INTO #sql
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT ( #sql )
END
CLOSE _cursor
DEALLOCATE _cursor
You can use any number of concatenation tricks to make one big batch without using a cursor, I personally use the FOR XML trick a lot.
Here's an overview:
http://www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql/
However, the cursor (while generally a code smell) isn't going to contribute a terrible amount to the non-performance of this. And you will have an opportunity to handle errors etc a lot easier than with a single batch.
In addition, if you have DDL in some of those statements which has to be the first statement in a batch, then you would need to submit them in separate batches. EXEC or sp_executesql doesn't implement any batch splitting like SSMS has the GO batch separator.
Ignoring the fundamental flaws in this whole schema....
declare #sql nvarchar(max)
select #sql = ''
select #sql = #sql + SQL + ';' from #tmp2
exec sp_executesql #sql
At least we've got rid of your cursor now :)
EDIT: Code that is working for me...
create table #tmp2 (sql nvarchar(100))
insert #tmp2 values ('select * from sysobjects')
insert #tmp2 values ('Select * from sysColumns')
declare #sql nvarchar(max)
select #sql = ''
select #sql = #sql + SQL + ';' from #tmp2
exec sp_executesql #sql
drop table #tmp2

execute stored procedures returned from database table

I am working with sql server 2008
I have a database table that has a column containing a stored procedure name.
I want to query the database table which returns a list of the stored procedure names, and execute them.
The stored procedures are similar all having a select statment. The data returned in this select statement I want to insert in to a data base table.
Pseudo code looks like this:
INSERT INTO MyTable
EXECUTE sp_executesql SELECT StoredProcedureName FROM Table
Anyone able to assist me with correct sql for achieveing the above?
sp_executesql accepts a unicode string not a tsql statement. So you would need to execute your procedure(s) like this:
execute sp_executesql 'execute ' + #storedprocedurename
which will execute a single procedure.
You will need to write some iterative process to populate the #storedprocedurename variable from your source table.
This is pretty much same as #Coltech answer just with cursor.
DECLARE #spname VARCHAR(200)
DECLARE #sql VARCHAR(1000)
DECLARE your_cursor CURSOR FOR
SELECT spname
FROM yourTable;
OPEN your_cursor;
FETCH NEXT FROM your_cursor
INTO #spname;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = 'EXEC ' + #spname
execute sp_executesql #sql
FETCH NEXT FROM your_cursor
INTO #spname;
END
CLOSE your_cursor;

How do I go through a cursor to perform logic on multiple tables? (The table names are in the cursor)

I get the feeling this is pretty basic database work, but it isn't for me. I'm trying to get a list of all of my tombstone tables from system tables and store the results in a cursor. I'm then trying to perform some logic on each of those tables I'm having trouble doing so.
Any help would be greatly appreciated.
Here is the error I get:
Must declare the table variable "#tablename"
Here is the code:
declare tombstonetables cursor for
(select name from sys.objects
where
name like'%tombstone%'
and type = 'U'--for user_table
)
Print 'Begin purging tombstone tables'
declare #tablename varchar(250)
open tombstonetables
fetch next from tombstonetables into #tablename
WHILE ##FETCH_STATUS = 0
begin
select * from #tablename--real logic goes here later
fetch next from tombstonetables into #tablename
end
close tombstonetables
deallocate tombstonetables
Looks like you need to use Dynamic SQL
Here is a reference to a simple walk through http://www.mssqltips.com/tip.asp?tip=1160
You will probably need to make use of sp_executesql
Here is a simple example of using Dynamic SQL with your example
DECLARE #DynamicSQL nvarchar(100)
WHILE ##FETCH_STATUS = 0
begin
SET #DynamicSQL = 'select * from ' + #tablename --real logic goes here later
EXEC #DynamicSQL
fetch next from tombstonetables into #tablename
end