Is it possible to assign a value to a variable, pass it to a stored proc, and then within the stored proc: 1.) use the value passed in, 2.) change the value assigned to the variable, and 3.) pass the variable back out?
I am attempting to setup a "Time Hack" that I can intersperse throughout procedures to check which statements are running slowly. Below is an example of the procedure and a call to it.
CREATE PROC [dbo].[usp_TIME_HACK_TEST]
#TITLE VARCHAR(255),
#START_TIME DATETIME OUT
AS
BEGIN
PRINT #TITLE + ': ' + RIGHT(CONVERT(VARCHAR(50),GETDATE() - #START_TIME,13),12)
SET #START_TIME = GETDATE()
END
GO
DECLARE #x int = 0
DECLARE #T DATETIME = GETDATE()
Print '#T Value at Beginning: ' + convert(VARCHAR(50),#T,21)
WHILE #x < 1000000
SET #x += 1
EXEC usp_TIME_HACK_TEST
#TITLE = 'Test Run',
#START_TIME = #T
Print '#T Value at End: ' + convert(VARCHAR(50),#T,21)
Here's what the result looks like. It looks like #START_TIME got treated as an input variable, and the procedure did not change the value of #T. Why did this happen if the variable was declared as an output variable?
#T Value at Beginning: 2016-07-27 11:21:19.720
Test Run: 00:00:00:607
#T Value at End: 2016-07-27 11:21:19.720
Thanks in advance for any help.
You need OUT[PUT] in the calling code too.
EXEC dbo.usp_TIME_HACK_TEST
#TITLE = 'Test Run',
#START_TIME = #T OUTPUT
BTW: Regarding your stated goal there are built in DMVs that can be queried to get information about long running statements already, no need to reinvent the wheel...
Related
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
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'''
So here is the code for the stored procedure and my execution. I keep getting this message when I try to execute my command:
Msg 8146, Level 16, State 2, Procedure sp_LabelFilm, Line 0
Procedure sp_LabelFilm has no parameters and arguments were supplied.
Any idea why? I am trying to update a column in the table tblfilm to say if a movie is short, medium, or long based on its run time.
ALTER PROCEDURE [dbo].[sp_LabelFilm]
AS
BEGIN
DECLARE #Minutes INT, #duration char(10)
DECLARE Filmcursor CURSOR FOR
(SELECT filmruntimeminutes, Duration FROM tblFilm)
OPEN filmcursor
FETCH NEXT FROM filmcursor INTO #duration
WHILE (##FETCH_STATUS = 0)
BEGIN
SELECT #Minutes = FilmRunTimeMinutes FROM tblFilm
IF #Minutes < 120
SET #duration = 'short'
ELSE IF #Minutes < 150
SET #duration = 'medium'
ELSE
SET #duration = 'long'
FETCH NEXT FROM filmcursor INTO #duration
UPDATE tblFilm
SET Duration = #duration
END
CLOSE filmcursor
DEALLOCATE filmcursor
END
DECLARE #Minutes INT, #duration CHAR(10)
EXECUTE [dbo].[sp_LabelFilm] #minutes, #duration
the error means exactly what it says. That you are passing arguments (variables #minutes and #duration) but there are no parameters defined on the stored procedure.
To declare parameters (input variables) you actually declare them before the AS like so:
use Movies
go
alter PROC [dbo].[sp_LabelFilm]
#Minutes INT
,#duration CHAR(10)
AS
BEGIN
DECLARE Filmcursor CURSOR
......
Notice you don't need to use the key word DECLARE and once they are a declared as parameters you don't actually need to declare them again.
Next I am not totally sure what you are attempting to accomplish with the parameters in the stored procedure but it actually looks like you don't want to pass them but rather you want to get them as out put which would be like this:
use Movies
go
alter PROC [dbo].[sp_LabelFilm]
#Minutes INT OUTPUT
,#duration CHAR(10) OUTPUT
AS
BEGIN
DECLARE Filmcursor CURSOR
....
And your execution statement would look like this:
declare #Minutes INT, #duration char(10)
execute [dbo].[sp_LabelFilm] #minutes = #Minutes OUTPUT, #duration = #duration OUTPUT
I had defined the parameters on the stored procedure, before the AS, but still I was facing the same problem until I realized that the procedure had 'create' instead of 'alter'. Changing it to alter procedure worked for me.(Faced this issue while I was trying to debug).
Apart from the first answer which is apt - In my case I did not have any parameters and while EXEC was getting a similar error.
However the difference being - I was putting a "go" below the EXEC statement.
After removing the go it was executed properly.
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
I've declared a variable in a stored procedure:
DECLARE #CurrentChunk NVARCHAR(250)
I would like to use the length of the variable, i.e. 250, later in my sp for computational purposes, and I want to keep my code as dry as possible.
Here's my code (assume #Narrative is a param to the SP):
DECLARE #ChunkSizeCharacters INT,
#NumChunks INT,
#LoopIndex INT,
#CurrentChunk NVARCHAR(250)
SET #ChunkSizeCharacters = 250 -- HERE'S WHERE I WANT THE LENGTH OF #CurrentChunk
SET #NumChunks = CEILING((LEN(#Narrative) * 1.0)/#ChunkSizeCharacters)
SET #LoopIndex = 0;
WHILE (#LoopIndex < #NumChunks)
BEGIN
SET #CurrentChunk = SUBSTRING(#Narrative,
((#LoopIndex * #ChunkSizeCharacters) + 1), #ChunkSizeCharacters)
INSERT INTO [dbo].[Chunks] ([Chunk]) VALUES (#CurrentChunk)
SET #LoopIndex = #LoopIndex + 1
END
Is there a way to ascertain the length of an NVARCHAR or VARCHAR variable definition (please read carefully -- I'm not looking for LEN())?
It seems the MaxLength variant property returns the value you're looking for.
DECLARE #Banana varchar(255) = 'This banana'
SELECT SQL_VARIANT_PROPERTY(#Banana, 'MaxLength')
Returns 255.
If you don't mind overwriting the variable (and if you do, you can assign it to a temp NVARCHAR(MAX)):
SELECT #CurrentChunk = REPLICATE(0, 8000);
SELECT #ChunkSizeCharacters = LEN(#CurrentChunk);
This trick does not and cannot work for NVARCHAR(MAX), but that's presumably no problem, given it's enormous maximum size.
Unfortunately T-SQL has nothing in the way of metadata properties for variables. Even determining the type of an expression is a chore.
Interestingly, the value returned by that SELECT SQL_VARIANT_PROPERTY statement doesn't select into a plain, predefined variable. In the end, I used:
DECLARE #Text VARCHAR(400), #TextLen INT
SELECT #TextLen = CAST(SQL_VARIANT_PROPERTY(ISNULL(#Text, ''), 'MaxLength') AS INT)
Works like a charm for me!