How to retrieve a part of a stored procedure's header? - sql-server

I have a C# WinForms application that manages stored procedures used by different services. What the users see is something like that:
exec stored_procedure_name param1, param2, param3
And since param1 doesn't mean anything to them (they can't see the stored procedure), I would like to present to them small descriptions of the parameters which are normally saved in the header of the stored procedure.
A typical stored procedure would like:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[stored_procedure_name]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[stored_procedure_name]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
/*************************************************************************************
[Procedure Info]
Author = myName
Description = this stored procedure returns results.
**************************************************************************************
[Parameters Info]
#param1 = this is parameter one
#param2 = this is parameter two
#param3 = this is parameter three
**************************************************************************************
[Changes]
2015-06-17 The stored procedure is optimized.
*/
CREATE PROCEDURE [dbo].[stored_procedure_name]
#param1 int,
#param2 nvarchar(20),
#param3 nvarchar(10)
AS
BEGIN
-- SP code here
END
GO
From the above, I want to get the descriptions in the Parameters info. I know that I can use the following SQL to retrieve the stored procedure as text/table:
CREATE TABLE #tmpHeader
(
TEXT NVARCHAR(1000)
)
INSERT INTO #tmpHeader
EXEC sp_helptext 'stored_procedure_name';
SELECT * FROM #tmpHeader
DROP TABLE #tmpHeader
Any ideas or suggestions how to proceed from here in order to get the content of the parameters info?
I am also open to any other suggestions.

Consider using extended properties to store meta-data. This is much cleaner than parsing the module text.
EDIT:
The example below returns all parameters plus descriptions for those parameters with extended properties. In your code, you can pass the schema and object names as parameters instead of the local variables used here for illustration.
EXEC sp_addextendedproperty #name = N'Description',
#value = 'this is parameter one', #level0type = N'Schema',
#level0name = 'dbo', #level1type = N'Procedure',
#level1name = 'stored_procedure_name', #level2type = N'Parameter',
#level2name = '#param1';
GO
EXEC sp_addextendedproperty #name = N'Description',
#value = 'this is parameter two', #level0type = N'Schema',
#level0name = 'dbo', #level1type = N'Procedure',
#level1name = 'stored_procedure_name', #level2type = N'Parameter',
#level2name = '#param2';
GO
EXEC sp_addextendedproperty #name = N'Description',
#value = 'this is parameter three', #level0type = N'Schema',
#level0name = 'dbo', #level1type = N'Procedure',
#level1name = 'stored_procedure_name', #level2type = N'Parameter',
#level2name = '#param3';
GO
DECLARE
#SchemaName sysname = 'dbo'
, #ObjectName sysname = 'stored_procedure_name';
SELECT properties.objtype
, properties.objname
, parms.name
, properties.value
FROM sys.parameters AS parms
LEFT JOIN fn_listextendedproperty('Description', 'Schema', #SchemaName, 'Procedure',
#ObjectName, 'Parameter', DEFAULT) AS properties ON
properties.objname COLLATE DATABASE_DEFAULT = parms.name
WHERE
parms.object_id = OBJECT_ID(QUOTENAME(#SchemaName) + '.' + QUOTENAME(#ObjectName));
GO

This should make the think your looking for.
SELECT SUBSTRING(definition,CHARINDEX(N'[Parameters Info]',definition),CHARINDEX(N'[',definition,CHARINDEX(N'[Parameters Info]',definition)+1)-CHARINDEX(N'[Parameters Info]',definition))
FROM sys.sql_modules
WHERE object_id = OBJECT_ID('YOUR PROCEDURE!!!')
It searches for Parameters Info and goes any further until if finds another header block (beginning with [). You can also specify that it should search for *.

Related

Concatenate passed parameter to string variable in stored procedure

I want to pass 2 parameters to my stored procedure, one of which I wish to use as a part of a variable definition. I am using SQL Server Management Studio 2018. My stored procedure will take a consignment number as a parameter and append it to the variable where the definition will appear similar to this:
passed parameter = #ConsignmentNo
variable #TmpDictionary = 'tmp.' + #ConsignmentNo + '_Dictionary'
So that I may have a generated table name such as
tmp.Cons1234_Dictionary
SQL is throwing an error
Must declare the table variable "#TmpDictionary"
Please see my attempt here:
/****** Object: --------------- Script Date: 29/06/2022 16:17:32 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE InsertProcedureNameHere
#Source varchar (max),
#ConsignmentNo varchar(max)
AS
DECLARE
#sql varchar(MAX),
#loop int,
#max_loop int,
#TmpDictionary nvarchar(max) = 'tmp.' + #ConsignmentNo + '_Dictionary',
#TmpThesaurus nvarchar(max) = 'mail.' + #ConsignmentNo + '_Thesaurus'
--set #TmpLookup = 'tmp.' + #JobNumber + '_Mailing_Lookup'
--set #MailSelection = 'mail. + #JobNumber + '_Mailing_Selection'
SELECT #loop = MIN(ID) FROM #TmpDictionary
WHERE [Source] = #Source
SELECT #max_loop = MAX(ID) FROM #TmpDictionary
WHERE [Source] = #Source
--print #loop print #max_loop
WHILE #loop <= #max_loop
BEGIN
SELECT
---------
FROM #TmpDictionary t
WHERE ID = #loop
BEGIN
SET #sql = '
update t
-------------------
from #Thesaurus t
where 1=1
-------------------
PRINT (#sql)
-- EXEC (#sql)
END
SET #loop = #loop +1
END
Please accept the fact that anything with dashes is just a placeholder for parts of the query that I cannot share or are needed for the purpose of this question. In a nutshell, I need to be able to pass a variable to my stored procedure, and use it within a variable definition to be able to reference tables dynamically using dynamic queries.
I hope this question makes sense!

Is there a way to store full DDL of View or Procedure definition from another database in separate table

I'm trying to store DDLs of some views and stored procedures in a separate table in the Dump database. There are too many similar databases on the server. But some objects are redundant. They will be listed in the table and then dropped from the database. But their DDLs will be backed up if somebody will need them later.
The procedure works fine when the views are small, but if the size of the code exceeds some value - I'm get an error:
XML parsing: line 120, character 31, incorrect CDATA section syntax
Maybe that's I'm using the dbo.sp_sqlexec procedure, but I'm not sure. Will appreciate any ideas.
Definition of the table where those views will be firstly listed and then stored:
CREATE TABLE [dbo].[ViewList_Explicit]
(
[ID] [int] IDENTITY(1,1) PRIMARY KEY NOT NULL,
[ServerName] [sysname] NOT NULL,
[DatabaseName] [sysname] NOT NULL,
[SchemaName] [sysname] NOT NULL,
[ViewName] [sysname] NOT NULL,
[DefinitionText] [xml] NULL,
[IsTransferred] [bit] NOT NULL,
[DateTransferred] [datetime] NULL
);
INSERT INTO [dbo].[ViewList_Explicit] ([ServerName], [DatabaseName], [SchemaName], [ViewName], [DefinitionText], [IsTransferred], [DateTransferred])
VALUES ('dbserver', 'reco', 'dbo', 'v_redundant_toDrop', NULL, 0, NULL)
This is the code of the procedure:
CREATE OR ALTER PROCEDURE [dbo].[sp_moveViews2Dump]
(#DatabaseName SYSNAME)
AS
BEGIN
SET NOCOUNT ON
DECLARE #Serv SYSNAME = ##SERVERNAME;
DECLARE #SQLstringToDrop NVARCHAR(MAX), #SQLstringForDefinition NVARCHAR(MAX);
DECLARE #SchemaName SYSNAME, #ViewName SYSNAME, #ExplicitID INTEGER;
DECLARE #DDLview XML;
DECLARE #Buffer TABLE(line XML);
DECLARE Schedule_cursor CURSOR LOCAL FOR
SELECT ID, SchemaName, ViewName
FROM [Dump].dbo.ViewList_Explicit
WHERE DatabaseName = #DatabaseName
AND ServerName = #Serv
AND IsTransferred = 0
OPEN Schedule_cursor
FETCH NEXT FROM Schedule_cursor INTO #ExplicitID, #SchemaName, #ViewName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQLstringForDefinition = 'SELECT CONCAT(''<query><![CDATA['', VIEW_DEFINITION, '']]></query>'') FROM ['
+ #DatabaseName + '].INFORMATION_SCHEMA.VIEWS
WHERE TABLE_NAME = '''+ #ViewName + ''' AND TABLE_SCHEMA = ''' + #SchemaName + ''';'
--PRINT #SQLstringForDefinition
INSERT #Buffer EXECUTE dbo.sp_sqlexec #SQLstringForDefinition
SELECT #DDLview = line FROM #Buffer
SELECT #SQLstringToDrop = 'USE [' + #DatabaseName + ']
DROP VIEW IF EXISTS [' + #SchemaName + '].[' + #ViewName + ']'
--EXECUTE dbo.sp_sqlexec #SQLstringToDrop -- Commented out to avoid the deletion
UPDATE [Dump].dbo.ViewList_Explicit
SET [DefinitionText] = #DDLview, IsTransferred = 1, DateTransferred = GETDATE()
WHERE ID = #ExplicitID
DELETE FROM #Buffer
FETCH NEXT FROM Schedule_cursor INTO #ExplicitID, #SchemaName, #ViewName
END
CLOSE Schedule_cursor
DEALLOCATE Schedule_cursor
SET NOCOUNT OFF
END
Not sure why you're storing module definitions as XML but you should be able to do this in one step, without the cursor, unsupported system procedures from decades ago, and INFORMATION_SCHEMA which is generally garbage (<-- see the section on Module Definitions):
DECLARE #exec nvarchar(1024) = QUOTENAME(#DatabaseName)
+ N'.sys.sp_executesql';
DECLARE #sql nvarchar(max) = N';WITH vws AS
(SELECT SchemaName = s.name, ViewName = v.name,
DefinitionText = CONCAT(''<query><![CDATA['',
OBJECT_DEFINITION(v.[object_id]), N'']]></query>'')
FROM sys.schemas AS s
INNER JOIN sys.views AS v
ON s.[schema_id] = v.[schema_id]
)
UPDATE vle
SET vle.DefinitionText = sps.DefinitionText,
vle.IsTransferred = 1,
vle.DateTransferred = GETDATE()
FROM [Dump].dbo.ViewList_Explicit AS vle
INNER JOIN vws
ON vle.SchemaName = vws.SchemaName
AND vle.ViewName = vws.ViewName
WHERE vle.DatabaseName = #db
AND vle.ServerName = ##SERVERNAME
AND vle.IsTransferred = 0;';
EXEC #exec #sql, N'#db sysname', #DatabaseName;
The main problem was mentioned in a comment already: VIEW_DEFINITION is limited to 4,000 characters.
The purpose of #SQLstringToDrop is unclear. If you're on a modern enough version of SQL Server, you could instead inject CREATE OR ALTER into the definition, or generate that only at time of actual execution / deployment ... that doesn't change per view so there's no reason to store the entire IF EXISTS / DROP sequence for each and every view.
If you want to drop the views after you've backed up their definitions (though, really, why you aren't using proper version control system for this is a mystery), you can simply use the same technique (all in one shot instead of in a loop):
DECLARE #exec nvarchar(1024) = QUOTENAME(#DatabaseName)
+ N'.sys.sp_executesql';
DECLARE #sql nvarchar(max) = N'';
SELECT #sql += CONCAT(N'DROP VIEW IF EXISTS ',
QUOTENAME(SchemaName), N'.',
QUOTENAME(ViewName), N';')
FROM [Dump].dbo.ViewList_Explicit
WHERE DatabaseName = #DatabaseName
AND ServerName = ##SERVERNAME;
EXEC #exec #sql;
As an additional tip, don't ever put square brackets around names manually (e.g. [' + #SchemaName + ']) - this does not protect you against SQL injection. And while it's unlikely someone put nefarious object names into the system you're working against now, it sets a bad example.

Run dynamic SQL Server stored procedure with linked server

This is a dynamic stored procedure that will pass the database, linked server and state. When executing this stored procedure, it runs the stored procedure on the database on the linked server and gives the results back.
Working code - here the linked server is absolute and not passed as a variable
EXECUTE MYPROC 'CA','MYDB'
CREATE PROCEDURE [dbo].[MYPROC]
(
#state varchar(2),
#DATABASE char(20)
)
AS
DECLARE #SQL #VARCHAR(MAX)
SELECT #SQL = 'use ' + #DATABASE + ';
SELECT * FROM pubs.dbo.authors WHERE state = #state'
EXEC MYLINKSERVER.master.dbo.sp_executesql
#SQL, N'#state char(2)', #state
Not working code: here the linked server is passed through a variable.
I get a "Syntax error" at #LINKEDSERVER**.**master
EXECUTE MYPROC 'CA','MYDB','MYLINKSERVER'
CREATE PROCEDURE [dbo].[MYPROC]
(
#state varchar(2),
#DATABASE char(20),
#LINKEDSERVER VARCHAR(20)
)
AS
DECLARE #SQL #VARCHAR(MAX)
SELECT #SQL = 'use ' + #DATABASE + ';
SELECT * FROM pubs.dbo.authors WHERE state = #state'
EXEC #LINKEDSERVER.master.dbo.sp_executesql
#SQL, N'#state char(2)', #state
Thanks in advance
Try this in your SP:
DECLARE #SQL VARCHAR(MAX);
SET #SQL = FORMATMESSAGE ( 'SELECT * FROM [%s].[%s].[dbo].[authors] WHERE [state] = ''%s'';', #LINKEDSERVER, #DATABASE, #state );
EXEC ( #SQL );
This would create the following SQL statement to be executed based on your sample parameters above:
SELECT * FROM [MYLINKSERVER].[MYDB].[dbo].[authors] WHERE [state] = 'CA';
I'm not sure what version of SQL Server you're running, so you may not be able to use FORMATMESSAGE, however, I'm sure you're familiar with concatenating strings.
I prefer to use OPENQUERY as it is usually much faster than using four-part query. So instead of SELECT * FROM [MYLINKSERVER].[MYDB].[dbo].[authors] WHERE [state] = 'CA';, try this:
SELECT * FROM OPENQUERY([MYLINKSERVER], '
SELECT * FROM [MYDB].[dbo].[authors] WHERE [state] = ''CA''
')
And your procedure will be something like this
CREATE PROCEDURE [dbo].[MYPROC]
(
#state CHAR(2),
#DATABASE VARCHAR(20),
#LINKEDSERVER VARCHAR(20)
)
AS
DECLARE #SQL NVARCHAR(500)
SET #SQL = N'SELECT * FROM OPENQUERY(' + QUOTENAME(#LINKEDSERVER) + ', ''
SELECT * FROM ' + QUOTENAME(#DATABASE) + '.dbo.authors WHERE state = ''''' + #state + '''''
'')'
EXEC SP_EXECUTESQL #SQL
--PRINT #SQL -- To see the final query to execute
Or you can use FORMATMESSAGE as the answer given by Critical Error.
SET #SQL = FORMATMESSAGE ('SELECT * FROM OPENQUERY([%s], ''
SELECT * FROM [%s].[dbo].[authors] WHERE [state] = ''''%s'''''');', QUOTENAME(#LINKEDSERVER), QUOTENAME(#DATABASE), #state
);
EXEC (#SQL);
Use QUOTENAME to avoid SQL injection. As the other parameter is limited to char(2), I guess it should be safe.

Dynamic SQL Not working - Complies , but errors out

CREATE TABLE [dbo].[TSQL_DLOOKUP](
[LookUpTable] [nchar](10) NULL,
[LookUpField] [nchar](10) NULL,
[LookUpValue] [nchar](10) NULL,
[LookUpDecode] [nchar](20) NULL
) ON [PRIMARY]
GO
The procedure:
ALTER PROCEDURE [dbo].[MyDLookUp]
#LookUpValue NCHAR(10),
#LookUpTable NCHAR(10),
#LookUpField NCHAR(10)
AS
BEGIN
DECLARE #SQLQuery AS NVARCHAR(500)
DECLARE #ParameterDefinition AS NVARCHAR(100)
/* Build Transact-SQL String by including the parameter */
SET #SQLQuery = 'SELECT LookUpDecode FROM TSQL_DLOOKUP WHERE LookUpTable = #LookUpTable AND LookUpValue = #LookUpValue AND LookUpField =#LookUpField'
/* Execute Transact-SQL String */
EXECUTE sp_executesql #SQLQuery, #LookUpValue,#LookUpTable,#LookUpField
END
enter code here To run:
DECLARE #return_value int
EXEC #return_value = [dbo].[MyDLookUp]
#LookUpValue = N'F',
#LookUpTable = N'LCLASS',
#LookUpField = N'CLASS'
SELECT 'Return Value' = #return_value
The error: Msg 102, Level 15, State 1, Line 1
Incorrect syntax near 'F'.
Msg 137, Level 15, State 2, Line 1
Must declare the scalar variable "#LookUpTable".
The code compiles, but will not run. Please help.
The idea here is to as accurately as possible replicate the MSACCESS DLOOOKUP function. I am on a project in which a lot of repeated SQL statements exist where the coder before me did it this way, I need to simplify things.
As said by LAMAK Clearly there is no need of Dynamic query here u can do the same by using staic query!!
SELECT lookupdecode
FROM tsql_dlookup
WHERE lookuptable = #LookUpTable
AND lookupvalue = #LookUpValue
AND lookupfield = #LookUpField
Function to do the same job
CREATE FUNCTION dlookup(#LookUpValue NCHAR(10),
#LookUpTable NCHAR(10),
#LookUpField NCHAR(10))
RETURNS nchar
AS
BEGIN
declare #lookupdecode nchar(50)
SELECT top 1 #lookupdecode = lookupdecode
FROM tsql_dlookup
WHERE lookuptable = #LookUpTable
AND lookupvalue = #LookUpValue
AND lookupfield = #LookUpField
RETURN #lookupdecode
END;
You are missing the #params parameter from the sp_executesql command which defines the parameters being passed in:
EXECUTE sp_executesql #SQLQuery,
N'#LookUpValue [nchar](10),#LookUpTable [nchar](10),#LookUpField [nchar](10)',
#LookUpValue,#LookUpTable,#LookUpField
As other said in this case there is no need of dynamic sql as it can be executed directly.
For answer to your question, see below.
The parameter definition needed to be passed as an argument to sp_executesql SP to pass parameters to a dynamic query http://msdn.microsoft.com/en-us/library/ms188001.aspx.
So, your new SP will be as follows
alter PROCEDURE [dbo].[MyDLookUp]
#LookUpValue NCHAR(10),
#LookUpTable NCHAR(10),
#LookUpField NCHAR(10)
AS
BEGIN
DECLARE #SQLQuery AS NVARCHAR(500)
DECLARE #ParameterDefinition AS NVARCHAR(100)
/* Build Transact-SQL String by including the parameter */
SET #SQLQuery = 'SELECT LookUpDecode FROM TSQL_DLOOKUP WHERE LookUpValue = #LookUpValue AND LookUpTable = #LookUpTable AND LookUpField =#LookUpField'
DECLARE #ParmDefinition nvarchar(500);
SET #ParmDefinition = N'#LookUpValue NCHAR(10), #LookUpTable NCHAR(10), #LookUpField NCHAR(10)';
/* Execute Transact-SQL String */
EXECUTE sp_executesql #SQLQuery, #ParmDefinition, #LookUpValue,#LookUpTable,#LookUpField
END
Try this Code, if this work for you because there is no need to use Dynamic SQL for simple select statment
---------------------------------------Stored Procedure
Alter PROCEDURE [dbo].[MyDLookUp] --[dbo].[MyDLookUp] 1,2,3
#LookUpValue NCHAR(10),
#LookUpTable NCHAR(10),
#LookUpField NCHAR(10)
AS
BEGIN
SELECT LookUpDecode FROM TSQL_DLOOKUP
WHERE LookUpTable = #LookUpTable AND LookUpValue = #LookUpValue AND LookUpField =#LookUpField
END

transactional replication using script

I am able to configure transactional replication using SSMS and it works properly. But i want to configure it using script so that i use it from my c#/vb application.
Is there any way to do that?
If you complete all the step of transactional replication using SSMS then it's not complicated to do with the script.
Just carefully observe that when you configure distribution, publication and subscription SSMS gives you the option to generate script in every step.
You can use that generated script.
But only difference is when you add articles to publication. You can use the following code to add article
declare #name nvarchar(50)
declare curname cursor for
select name from sysobjects where type = 'U'
open curname
fetch next from curname into #name
while ##FETCH_STATUS = 0
begin
if exists(select * from INFORMATION_SCHEMA.TABLE_CONSTRAINTS where CONSTRAINT_TYPE = 'PRIMARY KEY' AND TABLE_NAME = #name AND TABLE_SCHEMA = 'dbo')
begin
exec sp_addarticle
#publication = N'publication_name', #article = #name, #source_owner = N'dbo',
#source_object = #name, #type = N'logbased', #description = null, #creation_script = null,
#pre_creation_cmd = N'drop', #schema_option = 0x000000000803509F,
#identityrangemanagementoption = N'manual', #destination_table = #name,
#destination_owner = N'dbo', #vertical_partition = N''
end
fetch next from curname into #name
end
close curname
deallocate curname
Or, you can see https://hasibarnab.wordpress.com/category/sql-server/replication/
DECLARE #returncode int
EXEC #returncode = xp_cmdshell 'dtexec /f "C:\thePackage.dtsx"'
Check out sp_addpublication, sp_addarticle, and sp_addsubscription in BOL.

Resources