View executed sql from dynamic sql - sql-server

I've inherited a complicated application that uses a lot of dynamic SQL. Many of the stored procedures construct and execute significantly different SQL statements depending on input parameters. Is there a way that I can see the executed SQL, without using SQL profiler - ideally from within SQL Server Manager Studio?

I did something more-or-less along the same lines by creating a table called 'WhatHappened' with an AutoInc BigInt as a primary key, and a big varchar(8000) field to hold the dynamically created SQL commands, and then simply wrote the dynamically created SQL into the table, and looked at it with Enterprise Manager later. I don't know if it's a great solution, but it was quick and simple and worked.

You can use PRINT statement
for exp.
IF ##OPTIONS & 512 <> 0
PRINT N'This user has SET NOCOUNT turned ON.';
ELSE
PRINT N'This user has SET NOCOUNT turned OFF.';
GO
use like
PRINT #YourDynamicSQLStatement
ref. https://msdn.microsoft.com/en-IN/library/ms176047.aspx

Related

What is "dummy" in CREATE PROCEDURE statement

I'm doing investigation of code repo and find one thing that make me confused. SQL Server stored procedures are contained in a repo as a set of queries with following structure:
IF OBJECT_ID(N'[dbo].[sp_ProcTitle]', N'P') IS NULL
BEGIN
EXEC dbo.sp_executeSQL N'CREATE PROCEDURE [dbo].[sp_ProcTitle] AS dummy:;';
END
ALTER PROCEDURE dbo.sp_ProcTitle
#ParamOne int,
#ParamTwo date,
#ParamThree int
AS
SET NOCOUNT ON
-- some procedure body
END
Never before I saw AS dummy:; and now I'm a little confused, I can't find any good explanation what is it and how it works. Could anybody tell me what does it mean this statement? How it works? What is the reason to have it? Any thought would be good to hear. Or, please, advise me some link where I can find good explanation.
This is simply a label, such that could be used in a GOTO statement.
The word "dummy" is unimportant. It's simply trying to create the stored procedure if it doesn't exist, with a minimal amount of text. The content is then filled in with the ALTER.
Conceivably, the dummy text could later be searched for to see if any procedures were created and didn't have their content filled in, to check against failed deployments, etc.
Why do this? Well, it preserve the creation time of the stored procedure in metadata (which can be useful in administration or tracking down problems), and is compatible with versions of SQL Server that lack the CREATE OR ALTER... support.
This might make a little more sense if we add a little formatting to the CREATE:
CREATE PROCEDURE [dbo].[sp_ProcTitle]
AS
dummy:
This is, effectively, an empty procedure with a label called dummy. The user appears to be using this to ensure that the procedure exists first, and the ALTERing it. In older versions of SQL Server, such methods were needed because it didn't support CREATE OR ALTER syntax. As such, if you tried to ALTER a procedure that didn't exist the statement failed, and likewise if you try to CREATE a procedure that already exists it fails.
If you are on a recent version of SQL Server, I'd suggest changing to CREATE OR ALTER and getting rid of the call to sys.sp_executesql.

Using variables in TSQL and keep formatting in SQL Server Management Studio

I'm creating some views with a lot of references to tables in another database.
At some point the other database needs to change.
I want to make it easy for the next developer to change the scripts to use another database.
This obviously work like it should:
CREATE VIEW ViewName
AS
SELECT *
FROM AnotherDatabase.SchemaName.TableName;
But when I do:
DECLARE #DB CHAR(100)
SET #DB = 'AnotherDatabase'
GO
CREATE VIEW ViewName
AS
SELECT *
FROM #DB.SchemaName.TableName;
I get the error:
Msg 137, Level 15, State 2, Procedure ViewName, Line 3
Must declare the scalar variable "#DB".
I could do something like:
DECLARE #SQL ...
SET #SQL = ' ... FROM ' + #DB + ' ... '
EXEC (#SQL)
But that goes against the purpose of making it easier for the next developer - because this dynamic SQL approach removed the formatting in SSMS.
So my question is: how do I make it easy for the next developer to maintain T-SQL code where he needs to swap out the database reference?
Notes:
I'm using SQL Server 2008 R2
The other database is on the same server.
Consider using SQLCMD variables. This will allow you to specify the actual database name at deployment time. SQL Server tools (SSMS, SQLCMD, SSDT) will replace the SQLCMD variable names with the assigned string values when the script is run. SQLCMD mode can be turned on for the current query windows from the menu option Query-->SQLCMD mode option.
:SETVAR OtherDatabaseName "AnotherDatabaseName"
CREATE VIEW ViewName AS
SELECT *
FROM $(OtherDatabaseName).SchemaName.TableName;
GO
This approach works best when SQL objects are kept under source control.
When you declare variables, they only live during the execution of the statement. You can not have a variable as part of your DDL. You could create a bunch of synonyms, but I consider that over doing it a bit.
The idea that your database names are going to change over time seems a bit out of the ordinary and conceivably one-time events. However, if you do still require to have the ability to quickly change over to point to a new database, you could consider creating a light utility directly in SQL to automatically generate the views to point to the new database.
An implementation may look something like this.
Assumptions
Assuming we have the below databases.
Assuming that you prefer to have the utility in SQL instead of building an application to manage it.
Code:
create database This;
create database That;
go
Configuration
Here I'm setting up some configuration tables. They will do two simple things:
Allow you to indicate the target database name for a particular configuration.
Allow you to define the DDL of the view. The idea is similar to Dan Guzman's idea, where the DDL is dynamically resolved using variables. However, this approach does not use the native SQLCMD mode and instead relies on dynamic SQL.
Here are the configuration tables.
use This;
create table dbo.SomeToolConfig (
ConfigId int identity(1, 1) primary key clustered,
TargetDatabaseName varchar(128) not null);
create table dbo.SomeToolConfigView (
ConfigId int not null
references SomeToolConfig(ConfigId),
ViewName varchar(128) not null,
Sql varchar(max) not null,
unique(ConfigId, ViewName));
Setting the Configuration
Next you set the configuration. In this case I'm setting the TargetDatabaseName to be That. The SQL that is being inserted into SomeToolConfigView is the DDL for the view. I'm using two variables, one {{ViewName}} and {{TargetDatabaseName}}. These variables are replaced with the configuration values.
insert SomeToolConfig (TargetDatabaseName)
values ('That');
insert SomeToolConfigView (ConfigId, ViewName, Sql)
values
(scope_identity(), 'dbo.my_objects', '
create view {{ViewName}}
as
select *
from {{TargetDatabaseName}}.sys.objects;'),
(scope_identity(), 'dbo.my_columns', '
create view {{ViewName}}
as
select *
from {{TargetDatabaseName}}.sys.columns;');
go
The tool
The tool is a stored procedure that takes a configuration identifier. Then based on that identifier if drops and recreates the views in the configuration.
The signature for the stored procedure may look something like this:
exec SomeTool #ConfigId;
Sorry -- I left out the implementation, because I have to scoot, but figured I would respond sooner than later.
Hope this helps.

Tricks on how to execute string inside a function in Sql Server

Procedure FunctionX, Line 345
Invalid use of a side-effecting operator 'EXECUTE STRING' within a
function.
I get the above error when I execute a dynamic statement inside a function in SQL Server 2012.
Is there a workaround for this? Any tricks?
PS: The sproc (stored procedure) is much too lengthy for its body to be inserted as-is inside the function.
DECLARE #execsql NVARCHAR(2000)
Set #execsql = 'INSERT INTO #TABLE1 EXEC SPROC1 ' + #ID_COMPANY + ',' + #ID_COUNTRY
exec (#execsql)
Many thanks in advance.
Also, I need to be able to delete inside the function as well. I know this contradicts the definition of functions but I am wondering if there are some tricks that can be used
No there are no tricks, see The Curse and Blessings of Dynamic SQL
Dynamic SQL in User-Defined Functions
This is very simple: you cannot use dynamic SQL from used-defined
functions written in T-SQL. This is because you are not permitted to do
anything in a UDF that could change the database state (as the UDF may
be invoked as part of a query). Since you can do anything from dynamic
SQL, including updates, it is obvious why dynamic SQL is not
permitted.
I've seen more than one post on the newsgroups where people have been
banging their head against this. But if you want to use dynamic SQL in
a UDF, back out and redo your design. You have hit a roadblock, and in
SQL 2000 there is no way out.
In SQL 2005 and later, you could implement your function as a CLR
function. Recall that all data access from the CLR is dynamic SQL.
(You are safe-guarded, so that if you perform an update operation from
your function, you will get caught.) A word of warning though: data
access from scalar UDFs can often give performance problems. If you
say
SELECT ... FROM tbl WHERE dbo.MyUdf(somecol) = #value
and MyUdf performs data access, you have more or less created a hidden
cursor.
I was having this same problem with dynamic OPENQUERY statements inside a multi-line table-valued function. SQL Server is trying to prevent users with only db_datareader access, who can select from these functions, from performing SQL injections. Long story short, remove as many single quotes as you can and find a way to do the same thing without using EXEC.
Instead of doing this:
Set #execsql = 'INSERT INTO #TABLE1 EXEC SPROC1 ' + #ID_COMPANY + ',' + #ID_COUNTRY
Do something like this:
INSERT INTO #TABLE1
SELECT *
FROM --some unfiltered version of the table your stored procedure uses
WHERE company = #ID_COMPANY
AND country = #ID_COUNTRY
Since you're calling a function from a stored procedure you can already be sure the table will be up to date. In my case, I was able to have a job refresh the function's underlying table using the stored procedure once every morning. You could also use a trigger to do that.

Declare table dependency in stored procedure when using T-SQL

I am going to use dynamic sql in my stored procedure to remove some of the code duplication. But I see one big drawback for me in this case: I have rather big DB with lots of objects. My stored procedure is using few tables and since it is compiled I can find dependencies easily from Sql Server management studio.But when I rewrite it to dynamically build some of the repeating queries I will loose dependency possibility and next time when I would need to find who is using this table I will need to do raw text search in my code repository rather than asking sql server for dependency. This is probably small concern, but I would still like to try to find solution.
So my question is: is there anything I can do to still have ability to see what dependencies my stored proc has? Like declare some dependencies upfront etc?
You can get dependencies to show up for the stored procedure for sections of code that never execute. For example, if you wanted to "declare" a dependency on a table named TestTable, you could use
CREATE PROC MyStoredProc
AS
DECLARE #SQL VarChar(4000)
SET #SQL = 'SELECT * FROM TestTable'
EXEC (#SQL)
RETURN
SELECT 0 FROM TestTable -- Never executes but shows as dependency
END

Help with sp_msforeachdb -like queries

Where I'm at we have a software package running on a mainframe system. The mainframe makes a nightly dump into sql server, such that each of our clients has it's own database in the server. There are a few other databases in the server instance as well, plus some older client dbs with no data.
We often need to run reports or check data across all clients. I would like to be able to run queries using sp_msforeachdb or something similar, but I'm not sure how I can go about filtering unwanted dbs from the list. Any thoughts on how this could work?
We're still on SQL Server 2000, but should be moving to 2005 in a few months.
Update:
I think I did a poor job asking this question, so I'm gonna clarify my goals and then post the solution I ended up using.
What I want to accomplish here is to make it easy for programmers working on queries for use in their programs to write the query using one client database, and then pretty much instantly run (test) code designed and built on one client's db on all 50 or so client dbs, with little to no modification.
With that in mind, here's my code as it currently sits in Management Studio (partially obfuscated):
use [master]
declare #sql varchar(3900)
set #sql = 'complicated sql command added here'
-----------------------------------
declare #cmd1 varchar(100)
declare #cmd2 varchar(4000)
declare #cmd3 varchar(100)
set #cmd1 = 'if ''?'' like ''commonprefix_%'' raiserror (''Starting ?'', 0, 1) with nowait'
set #cmd3 = 'if ''?'' like ''commonprefix_%'' print ''Finished ?'''
set #cmd2 =
replace('if ''?'' like ''commonprefix_%''
begin
use [?]
{0}
end', '{0}', #sql)
exec sp_msforeachdb #command1 = #cmd1, #command2 = #cmd2, #command3 = #cmd3
The nice thing about this is all you have to do is set the #sql variable to your query text. Very easy to turn into a stored procedure. It's dynamic sql, but again: it's only used for development (famous last words ;) ). The downside is that you still need to escape single quotes used in the query and much of the time you'll end up putting an extra ''?'' As ClientDB column in the select list, but otherwise it works well enough.
Unless I get another really good idea today I want to turn this into a stored procedure and also put together a version as a table-valued function using a temp table to put all the results in one resultset (for select queries only).
Just wrap the statement you want to execute in an IF NOT IN:
EXEC sp_msforeachdb "
IF '?' NOT IN ('DBs','to','exclude') BEGIN
EXEC sp_whatever_you_want_to
END
"
Each of our database servers contains a "DBA" database that contains tables full of meta-data like this.
A "databases" table would keep a list of all databases on the server, and you could put flag columns to indicate database status (live, archive, system, etc).
Then the first thing your SCRIPT does is to go to your DBA database to get the list of all databases it should be running against.
We even have a nightly maintenance script that makes sure all databases physically on the server are also entered into our "DBA.databases" table, and alerts us if they are not. (Because adding a row to this table should be a manual process)
How about taking the definition of sp_msforeachdb, and tweaking it to fit your purpose? To get the definition you can run this (hit ctrl-T first to put the results pane into Text mode):
sp_helptext sp_msforeachdb
Obviously you would want to create your own version of this sproc rather than overwriting the original ;o)
Doing this type of thing is pretty simple in 2005 SSIS packages. Perhaps you could get an instance set up on a server somewhere.
We have multiple servers set up, so we have a table that denotes what servers will be surveyed. We then pull back, among other things, a list of all databases. This is used for backup scripts.
You could maintain this list of databases and add a few fields for your own purposes. You could have another package or step, depending on how you decide which databases to report on and if it could be done programmatically.
You can get code here for free: http://www.sqlmag.com/Articles/ArticleID/97840/97840.html?Ad=1
We based our system on this code.

Resources