Generating create stored procedure script using SQL syntax only - sql-server

I am aware it is possible in SQL Server Management Studio to generate a create stored procedure script using the Object Explorer (right click on stored procedure, "Script stored procedure as...", Create To)
Is it possible to generate a create script string using SQL syntax only?
declare #createSPstring varchar(max)
/*
insert code to generate the create stored procedure string and put it into #createSPString...
*/
select #createSPstring

You can get the full text from the view:
sys.sql_modules
However your selectmight not get the full code, since a nvarchar(maX) is cut of in SSMS.
One option is to use print to print each row from the definition. Here I split the definition on char(13) to get each row and use a cursor (yes i know) to print each row.
DECLARE #createSPstring VARCHAR(MAX)
-- get definition of procedure "test"
SELECT #createSPstring = definition
FROM sys.sql_modules
WHERE object_id = OBJECT_ID('test')
-- Declare cursor - split definition on line break
DECLARE rows CURSOR FOR
SELECT [value] row
FROM STRING_SPLIT(#createSPstring, CHAR(13))
DECLARE #row NVARCHAR(MAX)
OPEN rows
FETCH NEXT FROM rows INTO #row
WHILE ##fetch_status = 0
BEGIN
--Print each row
PRINT REPLACE(#row,'char(10)','')
FETCH NEXT FROM rows INTO #row
END

select cast (definition as xml ) from sys.sql_modules
WHERE object_id = OBJECT_ID('test')
click on the result, you can get the sp with the same format you created. only problem is if your proc using > or < operators; it will be replaced with > <
you require to replace manually using ctrl+h

Related

Moving table data to another table if table is not exists

I am generating dynamic script to move data into another database if table is not present then i want to create. This script runs perfectly if it execute directly. But gives error if that script string execute by execute statement. i have tried exec also.
declare #temp as varchar(max)
set #temp='select * into Allocation_Archive.dbo.Users from Users'
execute #temp
Error
Database 'select * into Allocation_Archive' does not exist. Make sure that the name is entered correctly.
Option 1
Add a USE database statement to your variable or specify the full object name including database and schema.
Wrap the #temp variable in parentheses
For example:
declare #temp as varchar(max)
set #temp='select * into Allocation_Archive.dbo.Users from ThisDatabase.dbo.Users'
execute #temp
Option 2
Don't use:
EXECUTE #temp
Instead use
EXEC sp_executesql #temp
You will also need to change your #temp variable to be nvarchar(max)

How can I edit a stored procedure?

I have several stored procedures in my database, structured like this:
CREATE PROCEDURE MyProcedure (.....)
AS
DECLARE #myvar NVARCHAR(100);
SET #myvar = (SELECT .... FROM my_table WHERE ....)
GO
I was asked to replace the table my_table in the FROM clause with another one in every procedure that has it.
I went through a lot of researches, but I should create a script that works by itself, and I haven't found anything suitable. For example I found the sp_helpTetx that shows the source code of a stored procedure, but is there a way to put it into a variable in order to edit it?
You can use tool like REDGATE SqlRefactor that works perfectly or you can script all the stored procedures, replace CREATE command with ALTER and then apply the other REPLACE in text you need...
I do it lot of time, you have to pay attention but it works...
Find all stored procedures with a reference to that table (you can either use the dependencies stuff built into SQL Server or run a query looking for that table name see Search text in stored procedure in SQL Server)
Script them out with an "ALTER" instead of "CREATE" Press CTRL-H (find and replace)
Execute the script.
Here is an article outlining how to handle this using a cursor, and the sp_HelpText as mentioned above (including set as also mentioned).
http://www.ideosity.com/ourblog/post/ideosphere-blog/2013/06/14/how-to-find-and-replace-text-in-all-stored-procedures
-- set "Result to Text" mode by pressing Ctrl+T
SET NOCOUNT ON
DECLARE #sqlToRun VARCHAR(1000), #searchFor VARCHAR(100), #replaceWith VARCHAR(100)
-- text to search for
SET #searchFor = '[MY-SERVER]'
-- text to replace with
SET #replaceWith = '[MY-SERVER2]'
-- this will hold stored procedures text
DECLARE #temp TABLE (spText VARCHAR(MAX))
DECLARE curHelp CURSOR FAST_FORWARD
FOR
-- get text of all stored procedures that contain search string
-- I am using custom escape character here since i need to espape [ and ] in search string
SELECT DISTINCT 'sp_helptext '''+OBJECT_SCHEMA_NAME(id)+'.'+OBJECT_NAME(id)+''' '
FROM syscomments WHERE TEXT LIKE '%' + REPLACE(REPLACE(#searchFor,']','\]'),'[','\[') + '%' ESCAPE '\'
ORDER BY 'sp_helptext '''+OBJECT_SCHEMA_NAME(id)+'.'+OBJECT_NAME(id)+''' '
OPEN curHelp
FETCH next FROM curHelp INTO #sqlToRun
WHILE ##FETCH_STATUS = 0
BEGIN
--insert stored procedure text into a temporary table
INSERT INTO #temp
EXEC (#sqlToRun)
-- add GO after each stored procedure
INSERT INTO #temp
VALUES ('GO')
FETCH next FROM curHelp INTO #sqlToRun
END
CLOSE curHelp
DEALLOCATE curHelp
-- find and replace search string in stored procedures
-- also replace CREATE PROCEDURE with ALTER PROCEDURE
UPDATE #temp
SET spText = REPLACE(REPLACE(spText,'CREATE PROCEDURE', 'ALTER PROCEDURE'),#searchFor,#replaceWith)
SELECT spText FROM #temp
-- now copy and paste result into new window
-- then make sure everything looks good and run
GO
If sp_HelpText returns a table, why not you use a cursor to loop over the results and join the resulting strings together? It's nasty, but would do the trick.

How can I transform the result of a stored procedure into xml on the sql server?

I have a stored procedure that can NOT be modified, the result of this stored procedure is normal select statement as following :
CREATE PROCEDURE LockedProcedure
AS
BEGIN
SELECT * FROM COLORS_TABLE
END
my problem is that I need to get its result as XML result like how the select statement returns when you provide "FOR XML" but without modifying the procedure itself, maybe we can create another stored procedure to call that or user defined function.
This is an example of the procedure that we CAN NOT modify because it is locked.
how to get its result as XML result NOT XML FILE...I don't want any physical file on hard disk.
Thanks.
Solution 1)
Create a temp table matching the output definition of stored procedure.
Use INSERT INTO #Tmp EXEC SPName to insert the stored procedure results into the temp table.
Use FOR XML in combination with SELECT command to fetch the results as xml from temp table.
Solution 2)
Create a CLR User-Defined function to execute the stored procedure and use the BCL facilities to convert the results to xml.
I had a similar problem to this but in my case editing the stored procedure is possible. Even though the OP mentions the stored procedure is locked, I still wanted to post this solution here for others that stumble upon it. This solution assumes the stored procedure uses dynamic SQL to select some data, but could just as easily be adapted to a non-dynamic SQL case.
Add a parameter to the sp such as "#lp_ReturnAsXML BIT = 0". When set to true, you will return the result set as XML using "FOR XML RAW" or some similar command.
Then add this to the end of the stored procedure as an alternative for running the dynamic SQL:
IF #lp_ReturnAsXML = 1
BEGIN
DECLARE #l_XML XML
SET #l_SQL = 'SET #l_XML = (SELECT * FROM ( ' +
#l_SQL + '
) d FOR XML RAW)'
EXEC sp_executesql #l_SQL, N'#l_XML XML OUTPUT', #l_XML = #l_XML OUTPUT
SELECT #l_XML
END
Now something like this should work:
DECLARE #table TABLE
(
Results XML
)
INSERT INTO #table
EXEC p_MyStoredProc ..., #lp_ReturnAsXML = 1

Altering user-defined table types in SQL Server

How can I alter a user-defined table type in SQL Server ?
As of my knowledge it is impossible to alter/modify a table type.You
can create the type with a different name and then drop the old type
and modify it to the new name
Credits to jkrajes
As per msdn, it is like 'The user-defined table type definition cannot be modified after it is created'.
This is kind of a hack, but does seem to work. Below are the steps and an example of modifying a table type. One note is the sp_refreshsqlmodule will fail if the change you made to the table type is a breaking change to that object, typically a procedure.
Use sp_rename to rename the table type, I typically just add z to
the beginning of the name.
Create a new table type with the original name and any modification
you need to make to the table type.
Step through each dependency and run sp_refreshsqlmodule on it.
Drop the renamed table type.
EXEC sys.sp_rename 'dbo.MyTableType', 'zMyTableType';
GO
CREATE TYPE dbo.MyTableType AS TABLE(
Id INT NOT NULL,
Name VARCHAR(255) NOT NULL
);
GO
DECLARE #Name NVARCHAR(776);
DECLARE REF_CURSOR CURSOR FOR
SELECT referencing_schema_name + '.' + referencing_entity_name
FROM sys.dm_sql_referencing_entities('dbo.MyTableType', 'TYPE');
OPEN REF_CURSOR;
FETCH NEXT FROM REF_CURSOR INTO #Name;
WHILE (##FETCH_STATUS = 0)
BEGIN
EXEC sys.sp_refreshsqlmodule #name = #Name;
FETCH NEXT FROM REF_CURSOR INTO #Name;
END;
CLOSE REF_CURSOR;
DEALLOCATE REF_CURSOR;
GO
DROP TYPE dbo.zMyTableType;
GO
WARNING:
This can be destructive to your database, so you'll want to test this on a development environment first.
Here are simple steps that minimize tedium and don't require error-prone semi-automated scripts or pricey tools.
Keep in mind that you can generate DROP/CREATE statements for multiple objects from the Object Explorer Details window (when generated this way, DROP and CREATE scripts are grouped, which makes it easy to insert logic between Drop and Create actions):
Back up you database in case anything goes wrong!
Automatically generate the DROP/CREATE statements for all dependencies (or generate for all "Programmability" objects to eliminate the tedium of finding dependencies).
Between the DROP and CREATE [dependencies] statements (after all DROP, before all CREATE), insert generated DROP/CREATE [table type] statements, making the changes you need with CREATE TYPE.
Run the script, which drops all dependencies/UDTTs and then recreates [UDTTs with alterations]/dependencies.
If you have smaller projects where it might make sense to change the infrastructure architecture, consider eliminating user-defined table types. Entity Framework and similar tools allow you to move most, if not all, of your data logic to your code base where it's easier to maintain.
To generate the DROP/CREATE statements for multiple objects, you can right-click your Database > Tasks > Generate Scripts... (as shown in the screenshot below). Notice:
DROP statements are before CREATE statements
DROP statements are in dependency order (i.e. reverse of CREATE)
CREATE statements are in dependency order
Simon Zeinstra has found the solution!
But, I used Visual Studio community 2015 and I didn't even have to use schema compare.
Using SQL Server Object Explorer, I found my user-defined table type in the DB. I right-mouse clicked on the table-type and selected . This opened a code tab in the IDE with the TSQL code visible and editable. I simply changed the definition (in my case just increased the size of an nvarchar field) and clicked the Update Database button in the top-left of the tab.
Hey Presto! - a quick check in SSMS and the udtt definition has been modified.
Brilliant - thanks Simon.
If you can use a Database project in Visual Studio, you can make your changes in the project and use schema compare to synchronize the changes to your database.
This way, dropping and recreating the dependent objects is handled by the change script.
You should drop the old table type and create a new one. However if it has any dependencies (any stored procedures using it) you won't be able to drop it. I've posted another answer on how to automate the process of temporary dropping all stored procedures, modifying the table table and then restoring the stored procedures.
Just had to do this alter user defined table type in one of my projects. Here are the steps I employed:
Find all the SP using the user defined table type.
Save a create script for all the SP(s) found.
Drop the SP(s).
Save a create script for the user defined table you wish to alter.
4.5 Add the additional column or changes you need to the user defined table type.
Drop the user defined table type.
Run the create script for the user defined table type.
Run the create script for the SP(s).
Then start modifying the SP(s) accordingly.
you cant ALTER/MODIFY your TYPE. You have to drop the existing and re-create it with correct name/datatype or add a new column/s
I created two stored procedures for this. The first one
create_or_alter_udt_preprocess takes the udt name as input, drops all the stored procs/functions that use the udt, drops the udt, and return a sql script to recreate all the procedures/functions.
The second one
create_or_alter_udt_postprocess takes the script outputted from the first proc and executes it.
With the two procs, changing an udt can be done by:
call create_or_alter_udt_preprocess;
create the udt with a new definition;
call create_or_alter_udt_postprocess;
Use a transaction to avoid losing the original procs in case of errors.
create or ALTER proc create_or_alter_udt_postprocess(#udt_postprocess_data xml)
as
begin
if #udt_postprocess_data is null
return;
declare #obj_cursor cursor
set #obj_cursor = cursor fast_forward for
select n.c.value('.', 'nvarchar(max)') as definition
from #udt_postprocess_data.nodes('/Objects/definition') as n(c)
open #obj_cursor;
declare #definition nvarchar(max);
fetch next from #obj_cursor into #definition;
while (##fetch_status = 0)
begin
exec sp_executesql #stmt= #definition
fetch next from #obj_cursor into #definition
end
CLOSE #obj_cursor;
DEALLOCATE #obj_cursor;
end
Create or ALTER proc create_or_alter_udt_preprocess(#udt nvarchar(200), #udt_postprocess_data xml out)
AS
BEGIN
set #udt_postprocess_data = null;
if TYPE_ID(#udt) is null
return;
declare #drop_scripts nvarchar(max);
SELECT #drop_scripts = (
(select N';'+ drop_script
from
(
SELECT
drop_script = N'drop ' + case sys.objects.type when 'P' then N'proc ' else N'function' end
+ sys.objects.name + N';' + + nchar(10) + nchar(13)
FROM sys.sql_expression_dependencies d
JOIN sys.sql_modules m ON m.object_id = d.referencing_id
JOIN sys.objects ON sys.objects.object_id = m.object_id
WHERE referenced_id = TYPE_ID(#udt)
) dependencies
FOR XML PATH (''), type
).value('.', 'nvarchar(max)')
) ;
declare #postprocess_data xml;
set #udt_postprocess_data =
(SELECT
definition
FROM sys.sql_expression_dependencies d
JOIN sys.sql_modules m ON m.object_id = d.referencing_id
JOIN sys.objects ON sys.objects.object_id = m.object_id
WHERE referenced_id = TYPE_ID(#udt)
FOR XML PATH (''), root('Objects'));
exec sp_executesql #stmt= #drop_scripts;
exec sp_droptype #udt;
END
Example usage:
begin tran
declare #udt_postprocess_data xml;
exec create_or_alter_udt_preprocess #udt= 'test_list', #udt_postprocess_data = #udt_postprocess_data out;
CREATE TYPE test_list AS TABLE(
test_name nvarchar(50) NULL
);
exec create_or_alter_udt_postprocess #udt_postprocess_data = #udt_postprocess_data;
commit;
Code to set up the example usage:
CREATE TABLE [dbo].[test_table](
[test_id] [int] IDENTITY(1,1) NOT NULL, [test_name] [varchar](20) NULL
) ON [USERDATA]
GO
CREATE TYPE test_list AS TABLE(test_name nvarchar(20) NULL)
GO
create proc add_tests(
#test_list test_list readonly)
as
begin
SET NOCOUNT ON;
insert into test_table(test_name)
select test_name
from #test_list;
end;
create proc add_tests2(
#test_list test_list readonly)
as
begin
SET NOCOUNT ON;
insert into test_table(test_name)
select test_name
from #test_list;
end;

Access SQL Server temporary tables created in different scope

I am writing a stored procedure for SQL Server 2008 in which I need to extract information from a set of tables. I do not know ahead of time the structure of those tables. There is another table in the same database that tells me the names and types of the fields in this table.
I am doing this:
declare #sql nvarchar(max)
set #sql = 'select ... into #new_temporary_table ...'
exec sp_executesql #sql
Then I iterate doing:
set #sql = 'insert into #another_temporary_table ... select ... from #new_temporary_table'
exec sp_executesql #sql
After that I drop the temporary table. This happens in a loop, so the table with be created, populated and dropped many times, each time with different columns.
This fails with the error:
Invalid object name: #new_temporary_table.
After some googling I have found that:
The table #new_temporary_table is being created in the scope of the call to exec sp_executesql which is different from the one of my stored proc. This is the reason the next exec sp_executesql cannot find the table. This post explains it:
http://social.msdn.microsoft.com/forums/en-US/transactsql/thread/1dd6a408-4ac5-4193-9284-4fee8880d18a
I could use global temporary tables, which are prepended with ##. I can't do this because multiple stored procs could run at the same time and they would be affecting each other's state
In this article it says that if I find myself in this situation I should change the structure of the database. This is not an option for me:
http://www.sommarskog.se/dynamic_sql.html
One workaround I have found was combining all the select into #new_temporary_table.. and all the insert into ... scripts into one gigantic statement. This works fine but it has some downsides.
If I do print #sql to troubleshoot, the text gets truncated, for example.
Do I have any other option? All ideas are welcome.
You could use global temp tables, but use a context id (such as newid()) as part of the global temp table name.
declare #sql varchar(2000)
declare #contextid varchar(50) = convert(varchar(20), convert(bigint, substring(convert(binary(16), newid()), 1, 4)))
set #sql = 'select getdate() as stuff into ##new_temporary_table_' + #contextid
exec (#sql)
I think it's best to use one single script.
You can change how many characters will print in Tools > Options > Query Results > SQL Server > Results to Text - change "Maximum number of characters..." from 256 to the max (8192).
If it's bigger than 8192, then yes, printing is difficult. But you could try a different option in this case. Instead of PRINT #sql; instead use the following (with Results to Grid):
SELECT sql FROM (SELECT #sql) AS x(sql) FOR XML PATH;
Now you can click on the result, and it opens in a new query window. Well, it's an XML file window, and you can't execute it or see color-coding, and you have to ignore that it changes e.g. > to > to make it valid as XML data, but from here it's easy to eyeball if you're just trying to eyeball it. You can copy and paste it to a real query editor window and do a search and replace for the entitized characters if you like. FWIW I asked for them to make such XML windows real query windows, but this was denied:
http://connect.microsoft.com/SQLServer/feedback/details/425990/ssms-allow-same-semantics-for-xml-docs-as-query-windows
#temp tables (not global) are available in the scope they were created and below.
So you could do something like...
while (your_condition = 1) begin
set #sql = 'select ... into #temp1 ...from blah
exec sp_do_the_inserts'
exec(#sql)
end
The sp_do_the_inserts might look like...
select * into #temp2 from #temp1
....your special logic here....
This assumes you create sp_do_the_inserts beforehand, of course.
Don't know if that serves your need.

Resources