How to translate Oracle package to SQL Server? - sql-server

In Oracle I have lots of stored procedures using a package which basically stores (encapsulates) and initializes all variables used by these procedures. There is one function in the package as well which takes care of initializing all it's package variables.
My question is: how to port this to SQL Server?
My first attempt is to declare all package variables and use them as OUTPUT parameters for a procedure to initialize them, but then I need to declare these variables over and over again in each procedure using them (and there are a lots of them in the package). Is there any better (and DRY) way to do this on SQL Server?
Some code to explain it:
ORACLE:
The package:
create or replace
PACKAGE MYPARAMS AS
/**
param container
*/
type rc_params is record
(
var1 varchar2(30),
var2 integer
);
/**
init param container
use: v_params rc_pkp_plan_params := MYPARAMS.f_get_params(initvar)
*/
function f_get_params(initvar number) return rc_params;
END MYPARAMS;
/
The package body:
CREATE OR REPLACE
PACKAGE BODY MYPARAMS AS
function f_get_params(initvar number) return rc_params AS
retval rc_params;
BEGIN
retval.var1 := 'MY_VAR1';
retval.var2 := initvar;
return retval;
END f_get_params;
END MYPARAMS;
/
Some usage example:
declare
initvar integer := 22;
v_params MYPARAMS.rc_params := MYPARAMS.f_get_params(initvar);
begin
dbms_output.put_line(v_params.var1 || ' initialized by ' || v_params.var2);
end;
SQL Server:
if exists (select * from sysobjects where id = object_id('f_get_params'))
drop procedure f_get_params
go
create procedure f_get_params(
#initvalue integer,
#var1 varchar(30) OUTPUT,
#var2 integer OUTPUT
)
as
set #var1 = 'MYVAR1'
set #var2 = #initvalue
go
-- this block i would like to avoid:
declare
#initvalue integer = 33,
#var1 varchar(30),
#var2 integer
exec f_get_params #initvalue, #var1 OUTPUT, #var2 OUTPUT
print #var1 + ' initialized by ' + convert(varchar(2), #var2)
Hope the description is clear enough...

Unfortunately, T-SQL doesn't have anything like Oracle's packages, package variables, or structures of variables. Oh that it did.
What you've done is probably the easiest way to accomplish it in T-SQL, even if it does require duplicating the variables.
You can use a # table, i.e. create a #params table in f_get_parms that contains all of the variables, then use that # table in all of the other procs to retrieve them. The downside is that you still either have to declare the variables in the calling procedures, or use DML to access the columns in the # table, which is a lot more cumbersome than having them as variables.
Another way I've used before is to use XML to pass multiple variables around but treat them as a single one. It's still more cumbersome to access the values than in variables, but it has the advantage of allowing you to use a function instead of a procedure to get the values.
CREATE FUNCTION dbo.uf_get_params (
#initvar int
)
RETURNS xml
AS
BEGIN
DECLARE #xml xml,
#var1 varchar(30) = 'MYVAR' -- setting value on DECLARE requires SQL2008+
SELECT #xml = (SELECT #var1 AS var1,
#initvar AS initvar
FOR XML RAW('params'))
RETURN #xml
END
go
In your calling procedure, you would have
DECLARE #params xml = (SELECT dbo.uf_get_parms(12))
to get the parameters, then use standard XML/XQUERY functions to retrieve the individual variables (attributes) from the #params XML variable.

Related

how to use declare variable in select query in stored procedure using sql server

Hello I want to concate two things one is string and other is int variable. Now, these thing I want to store in one variable and use that variable in select query as a into type to create a temptable in stored procedure using sql server.
Here is my query
USE [FlightExamSoftware]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- For Storing Question in Temp table
-- EXEC [GetQuestionListPerSubjectRatioWise] 1,11
ALTER PROCEDURE [dbo].[GetQuestionListPerSubjectRatioWise]
#SubjectID INT,
#NumberOfQue INT,
#UserID int
AS
BEGIN
DECLARE #strQuery VARCHAR(MAX);
DECLARE #PerChapQue INT;
DECLARE #tempTable VARCHAR(MAX) = 'tempTestUser' + #UserID;
SELECT #PerChapQue = COUNT(appQueID)/#NumberOfQue FROM tblQuestion WHERE appQueSubID=#SubjectID
SELECT COUNT(appQueID)/#PerChapQue ChapwiseQue
,CASE WHEN COUNT(appQueID)>=#PerChapQue THEN COUNT(appQueID)/#PerChapQue ELSE 1 END ChapWiseQuePlusOne
,appQueChapID into #tempTable
FROM tblQuestion
WHERE appQueSubID=#SubjectID
GROUP BY appQueChapID
END
Now, I am talking about these line
DECLARE #tempTable VARCHAR(MAX) = 'tempTestUser' + #UserID;
In these line two things are concate one is string and other is int. And store in varchar variable.
And use in following select query i.e.
SELECT COUNT(appQueID)/#PerChapQue ChapwiseQue
,CASE WHEN COUNT(appQueID)>=#PerChapQue THEN COUNT(appQueID)/#PerChapQue ELSE 1 END ChapWiseQuePlusOne
,appQueChapID into #tempTable
FROM tblQuestion
WHERE appQueSubID=#SubjectID
GROUP BY appQueChapID
END
Now, in these query I want to create a temptable named #tempTable.
But, in these line it showing error i.e. Incorrect syntax near '#tempTable'.
Confuse that where is the syntax is wrong.
Thank You.
There are a number of things wrong with your code.
When concatenating an int to a string, you must first cast the int to varchar. Otherwise, SQL Server will try to implicitly convert the string to int, that will result with an error.
So this: DECLARE #tempTable VARCHAR(MAX) = 'tempTestUser' + #UserID; should become this:
DECLARE #tempTable VARCHAR(MAX) = 'tempTestUser' + CAST(#UserID AS VARCHAR(11)); (you need 11 chars to be able to fit the minimum value of int: -2,147,483,648)
You can't use select...into with a table variable.
You can only use it for actual tables (temporary or regular).
your #tempTable isn't even a table variable (not that it will help with a select...into).
Even if you would use select...into the correct way, unless you are going to use a global temporary table (and that doesn't come without it's risks), Unless your stored procedure uses this temporary table later on, it will be useless, since temporary tables are bound to scope.
Taking all of that into consideration I'm not sure what output you are actually looking for. If you could edit your question to include the desired output of your stored procedure as well as some sample data as DDL+DML, it would be easier to help you write better code.
Hope this Dynamic Query helps you:
Try like this:
USE [FlightExamSoftware]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- For Storing Question in Temp table
-- EXEC [GetQuestionListPerSubjectRatioWise] 1,11
ALTER PROCEDURE [dbo].[GetQuestionListPerSubjectRatioWise]
#SubjectID INT,
#NumberOfQue INT,
#UserID int
AS
BEGIN
DECLARE #strQuery VARCHAR(MAX);
DECLARE #PerChapQue INT;
DECLARE #tempTable VARCHAR(MAX) = 'tempTestUser' + CAST(#UserID AS VARCHAR);
SELECT #PerChapQue = COUNT(appQueID)/#NumberOfQue FROM tblQuestion WHERE appQueSubID=#SubjectID
SET #strQuery='
SELECT COUNT(appQueID)/'+CAST(#PerChapQue AS VARCHAR)+' ChapwiseQue
,CASE WHEN COUNT(appQueID)>='+CAST(#PerChapQue AS VARCHAR)+' THEN COUNT(appQueID)/'+CAST(#PerChapQue AS VARCHAR)+' ELSE 1 END ChapWiseQuePlusOne
,appQueChapID
INTO '+#tempTable+'
FROM tblQuestion
WHERE appQueSubID='+CAST(#SubjectID AS VARCHAR)+'
GROUP BY appQueChapID
/*.................................
And you have to use the temp table inside the String only
.................................*/
'
EXEC (#strQuery)
END

Execute mixed sql: dynamic with static

I'm trying to execute mixed sql: dynamic with static. I have a stored proc with many queries and select-into-temp-table constructions. Portions of it need to be dynamic. Here are some extracted snippets of what I'm trying to do:
#DynamicPrefix = '0001' -- this is passed in by caller
#EngineCd = '070123456' -- this is passed in by caller
DECLARE #DynamicSQL VARCHAR(1000)
DECLARE #EngineKey INT
SET #DynamicSQL = 'set #EngineKey = (select optionnumber from lookup_' + #DynamicPrefix + '_option_001
where salescode = ' + #EngineCd + ')'
EXEC (#DynamicSQL)
Then further down:
Select MyCol
into #Eng
from myTable
where EngineKey = #EngineKey
There's a lot of static sql before, in between, and after my code block above.
The whole reason I'm bothering about dynamic sql is because I don't know certain table names until run time. So #DynamicPrefix enables me to construct the correct table names at execution time.
I can create the proc without errors, but when I run it I get the error Must declare the scalar variable "#EngineKey". It's clear to me that because #EngineKey is inside dynamic sql, it's invisible from within the static sql further down.
I suspect I need to use exec sp_executesql but I can't quite figure out the usage, so I had just started with EXEC.
How can I get this to work? Thanks in advance.
This should do the job:
#DynamicPrefix = '0001'; -- this is passed in by caller
#EngineCd = '070123456'; -- this is passed in by caller
DECLARE #EngineKey INT;
DECLARE #SQL NVARCHAR (MAX);
SET #SQL =N'set #EngineKey = (select optionnumber from lookup_'+
#DynamicPrefix +
'_option_001 where salescode = '+
#EngineCd +')';
EXECUTE sp_executesql
#SQL,
N'#EngineKey INT OUTPUT, #EngineCd VARCHAR(10)',
#EngineKey OUTPUT, #EngineCd;
You have to specify your output parameter with OUTPUT keyword, and set your variables and their datatypes as you can see in the code.
If you don't use the OUTPUT keyword, your variable will always return NULL.
There are examples provided in the docs, see sp_executesql.

SQL Server - Procedure to return results of dynamic Query

I have a rather simple stored procedure that needs to return a results set, something like (the code is a highly simplified version of the real one, but is enough to describe my problem):
CREATE PROCEDURE MyProc(#Par1 VARCHAR(100))
AS
BEGIN
SELECT A,B,C FROM MyTable ;
END ;
This, so far, works perfectly as the invoking procedure does get the values A,B and C of all the records in that table.
Now, I need to allow the addition of "conditions" (i.e. WHERE clause) as provided through the received parameter Par1. To do that, I declare a new local variable aimed to hold a full SQL select statement that would include the conditions, so the same procedure would now look like:
CREATE PROCEDURE MyProc(#Par1 VARCHAR(100))
AS
BEGIN
DECLARE #SQLSTT VARCHAR(1000) ;
SET #SQLSTT = 'SELECT A,B,C FROM MyTable WHERE ' + #Par1 ;
EXECUTE ( #SQLSTT );
END ;
This ALMOST work: The correct number of records are affected, but they are not passed to the invoking procedure.
I thought of using a temporary table (or table variable), but it would be an unneeded overhead since, once the records are selected, there is no further manipulation to take place within this procedure and only need to be passed on to the invoking SP.
So, my question is: What should be the correct syntax to achieve what I want?
Thanks in advance.
EDIT
I found the issue. It had nothing to do with the procedure (i.e. MyProc) but rather with the way I was attempting to see the returned results. Quick fix and it all works are needed.
Thanks for all that took the time to walk though my question and send comments/suggestions/answers.
Declare a table to hold the return.
declare #ret table (a int, b int, c int)
insert into #ret
EXECUTE ( #SQLSTT );
You must use # in front of your parameter
The updated procedure is as follows
CREATE PROCEDURE MyProc(#Par1 VARCHAR(100))
AS
BEGIN
DECLARE #SQLSTT VARCHAR(1000) ;
SET #SQLSTT = 'SELECT A,B,C FROM MyTable WHERE ' + #Par1 ;
EXECUTE ( #SQLSTT );
END ;
This works fine. I changed it to use NVARCHAR. Please check how you are calling the procedure with appropriate single quotations:
CREATE PROCEDURE MyProc(#Par1 NVARCHAR(100))
AS
DECLARE #SQLSTT NVARCHAR(1000) ;
SET #SQLSTT = N'SELECT A,B,C FROM MyTable WHERE ' + #Par1 ;
EXEC( #SQLSTT )
Example call:
exec MyProc 'A <>''myvalue'''

Dynamic sql query with stored procedure and table variable

I want to call a stored procedure dynamically as my names of the procedures are stored somewhere, also I need to store the result of that procedure into a table variable. Hence I had to write following sql code,
In following code
#tblEmailIds is the table variable which I want to store the result of SP in
#tempEmailSource is the name of the procedure
#tempRecipientsIdsCSV is the first argument that my SP is accepting
#ObjectMasterId is the second argument that SP is accepting (optional)
DECLARE #tempTypeName NVARCHAR(100),
#tempTypeId INT,
#tempEmailSource NVARCHAR(100),
#tempRecipientsIdsCSV NVARCHAR(MAX),
#tempIsObjectSpecific BIT,
#sqlQuery NVARCHAR(MAX) = 'INSERT INTO #tblEmailIds '
SELECT TOP 1 #tempTypeName = NAME,
#tempTypeId = Id,
#tempEmailSource = EmailListSourceName
FROM #tbleRecipientsTypes WHERE IsEmailIdsFetched = 0
SELECT #tempRecipientsIdsCSV = SUBSTRING(
(SELECT ',' + CAST(RT.EmailRecipientId AS NVARCHAR(50))
FROM #tbleRecipientsTypes RT WHERE RT.Id = #tempTypeId
ORDER BY RT.EmailRecipientId
FOR XML PATH('')),2,200000)
SELECT #tempRecipientsIdsCSV
SET #sqlQuery = #sqlQuery + 'EXEC ' + #tempEmailSource +' ' +'''' + #tempRecipientsIdsCSV +''''
IF (#tempIsObjectSpecific = 1)
BEGIN
SET #sqlQuery = #sqlQuery + ' ' + #ObjectMasterId
END
PRINT #SQLQUERY
EXECUTE SP_EXECUTESQL
#SqlQuery,'#IdsCSV NVARCHAR(MAX) OUTPUT,
#ObjectMasterId INT = NULL OUTPUT', #tblEmailIds
I am getting the following error
Msg 214, Level 16, State 3, Procedure sp_executesql, Line 6 Procedure
expects parameter '#params' of type 'ntext/nchar/nvarchar'.
There are quite a few problems here.
As the error message clearly states, the parameter list needs to be NVARCHAR so just prefix that string literal with an N (as also stated in #VR46's answer).
Table variables do not work the way that you are trying to use them. First, you do not ever declare #tblEmailIds, but even if you did, the scope of a table variable is local, and they cannot be used as OUTPUT parameters. Instead, you need to create a local temporary table (i.e. #tblEmailIds) and do INSERT INTO #tblEmailIds.
You reference another table variable, #tbleRecipientsTypes, that has not been declare or populated.
You do declare #tempIsObjectSpecific but never set it so it will always be NULL.
Why is #IdsCSV (in the sp_executesql call parameter list) declared as OUTPUT? Not only are you not passing in #tempRecipientsIdsCSV (to be #IdsCSV in the dynamic SQL), it isn't even necessary to be a parameter since you are directly concatenating the value of #tempRecipientsIdsCSV into the Dynamic SQL, and there is no #tempRecipientsIdsCSV variable in the Dynamic SQL to begin with. So remove #IdsCSV NVARCHAR(MAX) OUTPUT, from the parameter list.
You say that "#tempRecipientsIdsCSV is the first argument that my SP is accepting", but then you declare it in the code, which should result in an error.
What datatype is #ObjectMasterId? You say that it is passed into the proc and I see that it is concatenated into the Dynamic SQL, so it needs to either be a string type (i.e. not INT like is shown in the sp_executesql parameter list) or it needs to be in a CONVERT(NVARCHAR(10), #ObjectMasterId.
If #ObjectMasterId is being passed in, then why is it declared as OUTPUT in the sp_executesql parameter list? But the better question is probably: why are you even passing it into sp_executesql anyway since you directly concatenate it into the Dynamic SQL? There is no #ObjectMasterId variable being used in the Dynamic SQL.
Prefix N to the #params of SP_EXECUTESQL.
Also you need to store the result of OUTPUT parameter
Declare #IdsCSV NVARCHAR(MAX),#ObjectMasterId INT = NULL
EXECUTE SP_EXECUTESQL
#SqlQuery,
N'#IdsCSV NVARCHAR(MAX) OUTPUT,#ObjectMasterId INT = NULL OUTPUT',
#IdsCSV = #IdsCSV OUTPUT,
#ObjectMasterId = #ObjectMasterId OUTPUT

SP_ExecuteSQL Generic stored_procedure call without output parameters, but catching the output

I'm scratching my head hard on this pb and I would like some help to figure out some solution.
Inside some TSQL programmable object (a stored procedure or a query in Management Studio)
I have a parameter containing the name of a stored procedure + the required argument for these stored procedures (for exemple it's between the brackets [])
Sample of #SPToCall
EX1 : [sp_ChooseTypeOfResult 'Water type']
EX2 : [sp_ChooseTypeOfXMLResult 'TABLE type', 'NODE XML']
EX3 : [sp_GetSomeResult]
I can't change thoses stored procedures (and I don't have a nice output param to cache, as I would need to change the stored procedure definition)
All these stored procedures 'return' a 'select' of 1 record the same datatype ie: NVARCHAR. Unfortunately there is no output param in those stored procedures definition (it would have been too easy otherwise :D)
So I'm working on something like this but I can't find anything working
DECLARE #myFinalVarFilledWithCachedOutput NVARCHAR(MAX);
DECLARE #SPToCall NVARCHAR(MAX) = N'sp_ChooseTypeOfXMLResult ''TABLE type'', ''NODE XML'';'
DECLARE #paramsDefintion = N'#CatchedOutput NVARCHAR(MAX) OUTPUT'
exec SP_executeSQL #SPToCall , #paramsDefinitions, #CatchedOutput = #myFinalVarFilledWithCachedOutput OUTPUT
-- my goal is to get something inside #myFinalVarFilledWithCachedOutput
Select #myFinalVarFilledWithCachedOutput
Any ideas ?
Here's an example of the syntax to take output of stored procedure that returns a selected value and pass that output to a table variable.
CREATE PROCEDURE tester
AS
BEGIN
DECLARE #var VARCHAR(10)
SET #var = 'beef'
SELECT #var
END
GO
DECLARE #outputtab TABLE ( varfield VARCHAR(100) )
INSERT #outputtab
( varfield )
EXEC tester
GO
SELECT *
FROM #outputtab
From there if you want to get it into a varchar variable:
DECLARE #outputvar VARCHAR(100)
SET #outputvar = ( SELECT TOP 1
*
FROM #outputtab
)

Resources