Multiple variables in SQL Server stored procedure - sql-server

I'm stuck on assigned variable values in this stored procedure.
#EntityID int,
#Title1 varchar(10) OUTPUT,
#Title2 varchar(10) OUTPUT
AS
Declare #T1 varchar(10)
Declare #T2 varchar(10)
BEGIN
SELECT
dbo.Entity.EntityID, dbo.Types.TypeName AS Title1, Types_1.TypeName AS Title2
FROM
dbo.Entity
LEFT OUTER JOIN
dbo.Types AS Types_1 ON dbo.Entity.Title2 = Types_1.TypeID
AND dbo.Entity.Title2 = Types_1.TypeID
LEFT OUTER JOIN
dbo.Types ON dbo.Entity.Title1 = dbo.Types.TypeID
WHERE
(dbo.Entity.EntityID = #EntityID)
END
I'm trying to return the values of Title1 and Title2. The query works, and returns values, but I need to use them elsewhere.

You need to do two things:
First, set the values of those parameters somewhere in the PROC, using SELECT or SET. As #Andomar mentioned this can be done by changing your SELECT statement like this (in your existing code you are returning the values in a column with the same name but that won't actually set the parameters):
SELECT #Title1 = dbo.Types.TypeName, #Title2 = Types_1.TypeName
Then you need to capture those values in your calling program. I'm not sure whether you're calling this stored proc from another bit of SQL code or from code such as ADO.NET. If from SQL, you'll need to first declare the output parameters and then call the proc like this:
DECLARE #Title1 VARCHAR(10), #Title2 VARCHAR(10)
EXEC MyProc #Title1=#Title1 OUTPUT, #Title2=#Title2 OUTPUT
If you're calling from ADO.NET, you need to set the ParameterDirection as Output or InputOutput, call the proc with the parameter, and then read the value of the parameter afterwards.

SELECT #Title1 = dbo.Types.TypeName
, #Title2 = Types_1.TypeName
...

Related

Stored procedure to delete some entries and return their IDs

I want to create a stored procedure or database function to delete some entries and return their IDs to the client. I started off with this FUNCTION:
CREATE FUNCTION dbo.FixHangingCarriers ()
RETURNS #returntable TABLE
(
ID_Plant INT NOT NULL,
ID_ChargeCarrier INT NOT NULL
)
AS
BEGIN;
-- Declare a temporary table
DECLARE #EntriesToDelete TABLE (
ID_ChargeCarrier INT NOT NULL,
ID_Storage INT NOT NULL,
StoredOn DATETIME2(2) NOT NULL
);
-- Select all the entries that should be deleted
INSERT INTO #EntriesToDelete
SELECT
ID_ChargeCarrier,
ID_Storage,
StoredOn
FROM dbo.CurrentStorage
WHERE ID_StorageType = 4
AND StoredOn < DATEADD(MINUTE, -30, GetDate());
-- Return immediately, if there is currently nothing to delete
IF (SELECT 1 FROM #EntriesToDelete) = 1
RETURN;
-- Delete hanging entries
DELETE ccs
FROM dbo.ChargeCarrier_Storage ccs
INNER JOIN #EntriesToDelete d ON
d.ID_ChargeCarrier = ccs.ID_ChargeCarrier AND
d.ID_Storage = ccs.ID_Storage AND
d.StoredOn = ccs.StoredOn;
-- Prepare return table
INSERT INTO #returntable
SELECT cs.ID
FROM #EntriesToDelete d
INNER JOIN dbo.ChargeCarriersCurrentlyInPlant cp ON
cp.ID_ChargeCarrier = d.ID_ChargeCarrier;
-- Return deleted entries to caller
RETURN;
END;
Obviously this doesn't work, because SQL functions cannot delete anything. I wanted to change it into a PROCEDURE, but then I saw, that procedures naturally seem not to be able to return a table. How can I solve this issue?
As the comments suggest, this seems like all you need is this:
CREATE PROC dbo.FixHangingCarriers
AS BEGIN
DELETE ccs
OUTPUT deleted.ID
FROM dbo.ChargeCarrier_Storage ccs
INNER JOIN CurrentStorage cs ON cs.ID_ChargeCarrier = ccs.ID_ChargeCarrier
AND cs.ID_Storage = ccs.ID_Storage
AND cs.StoredOn = ccs.StoredOn
WHERE cs.ID_StorageType = 4
AND cs.StoredOn < DATEADD(MINUTE, -30, GETDATE());
END;
You basically have two choices.
One is to return the rows as part of a SELECT statement as suggested. However, if you want to consume those in the next bit of T-SQL, you can't just SELECT from a table returned by a stored procedure.
Lots of people ask for a command like SELECT * FROM (EXEC someproc) but that doesn't exist.
What you can do instead is to define a table variable like this:
DECLARE #OutputIDs TABLE
(
OutputIDKey int IDENTITY(1,1) PRIMARY KEY,
OutputID int
);
Then you can use an INSERT EXEC to get the values:
INSERT #OutputIDs (OutputID)
EXEC dbo.MyStoredProcedure;
And then you have the IDs in the table variable to do what you want with. (Note: you could also do that with a temporary table but it isn't as good an option)
The other option is to use an OUTPUT parameter to the stored procedure. However, you'd need to pack all your IDs into a string (perhaps a comma-delimited list) before you return it. You then use a function to unpack the string back to a set of IDs. (If you're on SQL Server 2016 or later, you could use the STRING_SPLIT function).
The trick with using OUTPUT parameters is that you have to define it as OUTPUT when you are calling the procedure, and in the header of the procedure itself. Here's the skeleton code:
CREATE PROCEDURE dbo.DoSomething
#OtherParameter int,
#IDsToOutput nvarchar(max) OUTPUT,
#YetAnotherParameter int
AS
BEGIN
...
END;
And then when you call the procedure, you do this:
DECLARE #OutputIDs nvarchar(max);
EXEC dbo.DoSomething #OtherParameter = 1,
#IDsToOutput = #OutputIDs OUTPUT,
#YetAnotherParameter = 2;
SELECT * FROM STRING_SPLIT(#OutputIDs, ',');
Hope that helps.

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

T-SQL stored procedure - Detecting if a parameter is supplied as OUTPUT

Consider the following T-SQL code snippet:
CREATE PROC dbo.SquareNum(#i INT OUTPUT)
AS
BEGIN
SET #i = #i * #i
--SELECT #i
END
GO
DECLARE #a INT = 3, #b INT = 5
EXEC dbo.SquareNum #a OUTPUT
EXEC dbo.SquareNum #b
SELECT #a AS ASQUARE, #b AS BSQUARE
GO
DROP PROC dbo.SquareNum
The result set is:
ASQUARE BSQUARE
----------- -----------
9 5
As can be seen, #b is not squared, b/c it was not passed-in as output parameter (no OUTPUT qualifier when passing in the parameter).
I would like to know if there is a way I could check within stored procedure body (dbo.SquareNum body in this case) to see if a parameter has indeed been passed in as an OUTPUT parameter?
------ THIS WILL GIVE YOU THE BOTH VALUE IN squared------
CREATE PROC dbo.SquareNum(#i INT OUTPUT)
AS
BEGIN
SET #i = #i * #i
--SELECT #i
END
GO
DECLARE #a INT = 3, #b INT = 5
EXEC dbo.SquareNum #a OUTPUT
EXEC dbo.SquareNum #b OUTPUT
SELECT #a AS ASQUARE, #b AS BSQUARE
GO
DROP PROC dbo.SquareNum
-----TO CHECK STORED PROCEDURE BODY-----
SELECT OBJECT_NAME(object_id),
OBJECT_DEFINITION(object_id)
FROM sys.procedures
WHERE OBJECT_DEFINITION(object_id) =(SP_NAME)
Actually, there is a very simple way!
Make the parameter optional by setting a default value (#Qty AS Money = 0 Below)
Then, pass a value OTHER THAN THE DEFAULT when calling the procedure. Then immediately test the value and if it is other than the default value you know the variable has been passed.
Create Procedure MyProcedure(#PN AS NVarchar(50), #Rev AS NVarchar(5), #Qty AS Money = 0 OUTPUT) AS BEGIN
DECLARE #QtyPassed AS Bit = 0
IF #Qty <> 0 SET #QtyPassed = 1
Of course that means the variable cannot be used for anything other than OUTPUT unless you have a default value that you know will never be used as an INPUT value.
You can do this by query to sys views:
select
p.name as proc_name,
par.name as parameter_name,
par.is_output
from sys.procedures p
inner join sys.parameters par on par.object_id=p.object_id
where p.name = 'SquareNum'
or check in Management Studio in database tree:
[database] -> Programmability -> Stored Procedures -> [procedure] -> Parameters
Maybe I'm wrong but I don't believe it's possible. OUTPUT is part of the stored procedure definition so you should know when a parameter is or not OUTPUT. There is no way to set it dynamically so I think it's pointless to determine by code when a parameter is output or not because you already know it.
If you are trying to write a dynamic code, Piotr Lasota's answer should drive you to the correct way to realize when a parameter is Output.
Use the following query to get the name of all the parameters and to check if it is a output parameter:
select name, is_output from sys.parameters

Table-Valued Parameters not visible

For a reason unknown to me, I cannot access the contents of my TVP. The debugger says #_AccountList and #AccountTVP are tables, but I cannot view the contents, print them, nor will intellisense offer me auto completion for a column. I feel like probably declared something wrong or have an ambiguous definition. Any suggestions?
CREATE TYPE AccountList
AS TABLE
(
AccountNumber varchar(50),
AccountType varchar(50)
)
GO
CREATE PROCEDURE [dbo].[updateNumbers_ArchiveDB]
#_AccountList AccountList READONLY,
#padding int,
#proc_dateStart datetime,
#proc_dateEnd datetime
AS
DECLARE #AccountTVP AS AccountList;
BEGIN
SET NOCOUNT ON;
SELECT * FROM #AccountTVP;
UPDATE dbo.Archive2007001
SET LogicalAccount = #padding + AccountNumber
FROM dbo.Archive2007001 INNER JOIN #AccountTVP AS tvp
ON dbo.Archive2007001.LogicalAccount = tvp.AccountNumber
WHERE ProcessDate BETWEEN #proc_dateStart AND #proc_dateEnd
UPDATE dbo.DailyArchive
SET LogicalAccount = #padding + AccountNumber
FROM dbo.DailyArchive INNER JOIN #AccountTVP AS tvp
ON dbo.DailyArchive.LogicalAccount = tvp.AccountNumber
WHERE ProcessDate BETWEEN #proc_dateStart AND #proc_dateEnd
-- does not work PRINT N'tvp.AccountNumber is ' + #AccountTVP.AccountNumber
END
Here is how I am executing the procedure.
declare #p1 dbo.AccountList
insert into #p1 values(N'Account Number',N'Account Type')
insert into #p1 values(N'7463689',N'Basic')
insert into #p1 values(N'1317893',N'Premium')
insert into #p1 values(N'2806127',N'Basic')
exec updateNumbers_ArchiveDB
#_AccountList=#p1,
#padding=N'111',
#proc_dateStart='2008-01-04 11:24:46',
#proc_dateEnd='2008-01-04 11:24:46'
Answer: The data I was looking for was loaded in #_AccountList, not #AccountTVP.
#AccountTVP.AccountNumber represents a row of data, not a single value, so trying to print it will not work.
You should be able to see the incoming values by using a SELECT statement:
SELECT * FROM #_AccountList;
It seems like your SELECT statement from the top of the stored procedure should let you see the values, however, you have not actually set any values in #AccountTVP so it would be empty.
I am not sure but I suspect the debugger may not work because AccountList is a custom type.

SQL Insert Procedure

Why isn't my insert procedure returning the ID of the newly inserted row? AND, when calling this procedure, why do I have to supply a value for #EventId? That column is a PK with IDENTITY.
IF OBJECT_ID ( 'vjsql.EventsINSERT', 'P') IS NOT NULL
DROP PROCEDURE EventsINSERT
GO
CREATE PROCEDURE EventsINSERT
#EventId int OUTPUT,
#EventDate datetime,
#Title varchar(100),
#IsActive bit
AS
BEGIN
INSERT INTO EventCalendar ( EventDate, Title, IsActive)
VALUES ( #EventDate, #Title, #IsActive)
SELECT #EventId = SCOPE_IDENTITY()
END
How are you making a call to the stored procedure?
This SP is returning the value of EventID by means of using OUTPUT parameters.
i.e. In programming terms, this is a procedure (not a function) that accepts an OUTPUT parameter which will be set with the value during the execution of the stored procedure.
For this, you will have to pass the variable for #EventID. The value of which will be set within the procedure and you will be able to read the value of it, once the procedure has finished.
See the example code below.
DECLARE #NewEventID INT
EXEC EventsINSERT
#EventId = #NewEventID OUTPUT,
#EventDate = '08/04/09',
#Title = 'Hello World',
#IsActive = 0
SELECT #NewEventID
Try adding some statement terminators:
BEGIN
INSERT INTO EventCalendar ( EventDate, Title, IsActive)
VALUES ( #EventDate, #Title, #IsActive);
SELECT #EventId = SCOPE_IDENTITY();
END
AND, when calling this procedure, why do I have to supply a value for #EventId? That column is a PK with IDENTITY.
You don't, but you do need to supply a variable of type int (or compatible with int) for the output value to be put into.
You don't need to specify a value for the OUTPUT parameter, you need to specify which local variable the output gets put into:
By default, SQL Management Studio names the parameter and the variable the same, which can be confusing. Here's an example of your SP being called:
DECLARE #InsertedEventId int
EXEC [dbo].[EventsINSERT]
#EventId = #InsertedEventId OUTPUT,
#EventDate = N'2009-08-05',
#Title = N'Some event',
#IsActive = 1
-- Display ID as result set
SELECT #InsertedEventId
Just to clarify: your stored procedure is fine. I used it as-is.
Why isn't my insert procedure
returning the ID of the newly inserted
row?
Your code should work. Try in the console instead of
SELECT #EventId = SCOPE_IDENTITY()
doing
SELECT SCOPE_IDENTITY()
and view what happens. Is possible that you are calling it the wrong way. You should store the value of the OUTPUT variable in a variable in the scope where you call this SP.
when calling this procedure, why do I
have to supply a value for #EventId?
Because you have to supply a value for every parameter you have. It doesn't matter if is a real value, it will be discarded, but you must call the stored procedure with a variable in this parameter to catch the returned value.
I'm pretty rusty with tsql, but don't you need to explicitly select ##identity to get that row id? That's where i'd go digging as I think scope_identity() may not return a value in the context of a user function/procedure.

Resources