String concatenation in procedure parameters - sql

I want to do something like this:
declare #var varchar(50) = '2';
exec sp_myprocedure 'first', 'sec ' + #var
but I get an error:
Incorrect syntax near '+'.
Of course I can set variable before exec procedure:
set #var = 'sec ' + #var;
but I don't want this, because I have many execution in my code and I don't want create million variables.

You need to fix your code, sorry. There is no magic that will make this legal:
EXEC dbo.procedure_name #var + 'constant';
Also, STOP using the sp_ prefix for stored procedures.

I think it's not possible to use string concatenation in procedure execution if you're using EXEC.
From this:
Execute a stored procedure or function
[ { EXEC | EXECUTE } ]
{
[ #return_status = ]
{ module_name [ ;number ] | #module_name_var }
[ [ #parameter = ] { value
| #variable [ OUTPUT ]
| [ DEFAULT ]
}
]
[ ,...n ]
[ WITH <execute_option> [ ,...n ] ]
} [;]
The #parameter must be a value.
But this: 'sec ' + #var is an expression.

You don't need to declare a million variables, you simply need to declare a single variable, and set it prior to each EXEC:
DECLARE #cmd nvarchar(max) = '';
declare #var varchar(50) = '2';
set #cmd = 'sec ' + #var;
exec sp_myprocedure 'first', #cmd
set #cmd = 'try ' + #var;
exec sp_myprocedure 'first', #cmd
set #cmd = 'foo ' + #var;
exec sp_myprocedure 'first', #cmd
set #cmd = 'bar ' + #var;
exec sp_myprocedure 'first', #cmd

Related

JSON_MODIFY does not work with variable parameter

I have a SQL query which modifies some JSON. I'm iterating through the data and modifies part of the JSON based on the iteration.
For that i need to pass a variable parameter into JSON_MODIFY, but for some reason, it doesn't work!
SET #json = JSON_MODIFY(#ProData, '$.' + #ProKey + '.hasAnswer', CAST(1 as BIT))
I also tried passing the whole expression as a variable:
DECLARE #hasAnswerPath VARCHAR(100);
SET #hasAnswerPath = '$.' + #ProKey + '.hasAnswer';
SET #json = JSON_MODIFY(#ProData, #hasAnswerPath, CAST(1 as BIT))
But it has the same output, the hasAnswer is added to the root of the JSON and not in the element specified by #ProKey.
This works just fine:
SET #json = JSON_MODIFY(#ProData, '$.SomeName1.hasAnswer', CAST(1 as BIT))
It's like the #ProKey is disregarded.
Complete query:
BEGIN TRAN
DECLARE #ProID as uniqueidentifier;
DECLARE #ProData as nvarchar(max);
DECLARE #ProKey as varchar(200);
DECLARE ProCursor CURSOR FOR
SELECT Id, [Data] FROM [dbo].[PRO]
OPEN ProCursor;
FETCH NEXT FROM ProCursor INTO #ProID, #ProData;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #json NVARCHAR(max);
DECLARE DataCursor CURSOR FOR
SELECT [key] FROM OPENJSON(#ProData) WHERE type = 5; --5 is object data
OPEN DataCursor;
FETCH NEXT FROM DataCursor INTO #ProKey;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #json=JSON_MODIFY(#ProData, '$.' + #ProKey + '.hasAnswer', CAST(1 as BIT))
SET #json=JSON_MODIFY(#json,'$.' + #ProKey + '.questionType','intro')
FETCH NEXT FROM DataCursor INTO #ProKey;
END;
UPDATE [dbo].[PRO]
SET [Data] = #json
WHERE Id = #ProID
PRINT #json
CLOSE DataCursor;
DEALLOCATE DataCursor;
FETCH NEXT FROM ProCursor INTO #ProID, #ProData;
END
CLOSE ProCursor;
DEALLOCATE ProCursor;
ROLLBACK
Sample JSON:
{
"SomeName1": {
"header": "Some text",
"answer": {
"type": "specified",
"numberValue": 1.0
}
},
"SomeName2": {
"header": "Some text",
"answer": {
"type": "specified",
"numberValue": 4.0
}
},
"SomeName3": {
"header": "Some text",
"answer": {
"type": "specified",
"numberValue": 2.0
}
}
}
Expected result:
},
"SomeName1": {
"header": "Some text",
"answer": {
"type": "specified",
"numberValue": 1.0
}
"hasAnswer": true,
"questionType": "intro",
}
}
Actual result:
},
"SomeName1": {
"header": "Some text",
"answer": {
"type": "specified",
"numberValue": 1.0
}
}
},
"hasAnswer":true,
"questionType":"intro"
}
What am I doing wrong here?
You seem to be overwriting your variables unintentionally:
-- first iteration
SET #json=JSON_MODIFY(#ProData, '$.' + #ProKey + '.hasAnswer', CAST(1 as BIT))
-- #json contains the #prodata after modification to first key (#prodata itself is not changed)
SET #json=JSON_MODIFY(#json,'$.' + #ProKey + '.questionType','intro')
-- #json (its first key to be more precise) is modified further
But on next iteration, this line will revert the modified #json back to the original value of #prodata. Only the last key will retain the modifications:
-- second iteration
SET #json=JSON_MODIFY(#ProData, '$.' + #ProKey + '.hasAnswer', CAST(1 as BIT))
-- you just overwrote your modifications with the value inside #prodata
The solution is to re-arrange the code a little, may be initialize #json outside the loop.

Functions with returns 'table' has no begin...end?

If I ever add 'Begin' after 'As' It says incorrect syntax. It works just fine If I'm trying to return one value though.
Instead I should add 'return' after 'as' and open brackets ()
Why is that?
With one value as returns, I can do it fine:
CREATE FUNCTION MATHEE(#A int,#B int)
returns int
as
begin
DECLARE #C int
SET #C = #A + #B
return #C
end
With table as returns, I cant:
CREATE FUNCTION TOP_USERSs(#number int,#bob varchar(20))
RETURNS TABLE
AS
BEGIN
RETURN SELECT agent from Agents where agent = #bob and prime > #number
END
It says :
Incorrect syntax near 'BEGIN'.
It works fine using brackets and without begin..end though:
CREATE FUNCTION TOP_USERSs(#number int,#bob varchar(20))
RETURNS TABLE
AS
RETURN
(
SELECT agent from Agents where agent = #bob and prime > #number
)
RETURNS TABLE in an Inline User-Defined Function & these do not have function bodies
There is no function_body delimited by BEGIN and END.
For example if you added set #number += 1 before the RETURN this would also fail to parse.
RETURNS TABLE creates an inline table-valued function. It supposed to have a single select wrapped in return as its body:
CREATE FUNCTION TOP_USERSs(#number int,#bob varchar(20))
returns table
as return (
select ...
);
If you want the begin-end syntax, that is a multi-statement table-valued function, and you will then need to declare the table it returns:
CREATE FUNCTION TOP_USERSs(#number int,#bob varchar(20))
returns #result table (agent varchar(10) not null)
as
begin
insert into #result (agent) ... ;
...
return;
end;
You can declare fields of Return table as following code:
CREATE FUNCTION TOP_USERSs(#number int,#bob varchar(20))
RETURNS #Result TABLE ( Agent varchar(20) )
AS
BEGIN
Insert into #Result
SELECT agent from Agents where agent = #bob and prime > #number
RETURN
END
Per the documentation, there are two kinds of table-valued function, with different syntaxes:
-- Transact-SQL Inline Table-Valued Function Syntax
CREATE [ OR ALTER ] FUNCTION [ schema_name. ] function_name
( [ { #parameter_name [ AS ] [ type_schema_name. ] parameter_data_type
[ = default ] [ READONLY ] }
[ ,...n ]
]
)
RETURNS TABLE
[ WITH <function_option> [ ,...n ] ]
[ AS ]
RETURN [ ( ] select_stmt [ ) ]
[ ; ]
This is an inline TVF. As the syntax shows, it can include solely a RETURN followed by a SELECT. Nothing more, nothing less.
-- Transact-SQL Multi-Statement Table-Valued Function Syntax
CREATE [ OR ALTER ] FUNCTION [ schema_name. ] function_name
( [ { #parameter_name [ AS ] [ type_schema_name. ] parameter_data_type
[ = default ] [READONLY] }
[ ,...n ]
]
)
RETURNS #return_variable TABLE <table_type_definition>
[ WITH <function_option> [ ,...n ] ]
[ AS ]
BEGIN
function_body
RETURN
END
[ ; ]
This is a multi-statement TVF. As the name suggests, you can include multiple statements in the definition. But note that you must specify the schema of the returned table as part of the function definitions.

Using variables when calling sp_rename

I try to make a stored proc which will
Drop a primary key
Rename the column name where the primary key was set
Create the new primary key
I'm struggling with the point number 2.
I'm trying to rename the column with sp_rename with the parameters passed to the stored proc like this:
EXEC sp_rename '[' + #SCHEMA + '].[' + #TABLE + '].[ID]' , 'Id', 'COLUMN'
But this way I got this error:
Procedure or function 'sp_RENAME' expects parameter '#newname', which was not supplied.
How can I use sp_rename with parameters ?
Try like this
DECLARE #SCHEMA NVARCHAR(30)='your schema name'
DECLARE #TABLE NVARCHAR(30)='table Name'
DECLARE #OLD NVARCHAR(30) = '[' + #SCHEMA + '].[' + #TABLE + '].[ID]'
EXEC sp_rename #OLD, 'Id'
The problem isn't with sp_rename, per se, it's actually a problem with EXEC.
For each parameter you wish to pass, you may supply a value (a literal of some kind), a variable or the keyword DEFAULT. What you may not pass is an expression that computes a value.
[ { EXEC | EXECUTE } ]
{
[ #return_status = ]
{ module_name [ ;number ] | #module_name_var }
[ [ #parameter = ] { value
| #variable [ OUTPUT ]
| [ DEFAULT ]
}
]
[ ,...n ]
[ WITH <execute_option> [ ,...n ] ]
}
[;]
All this means that, if you wish to compute something, you need to do it as separate statement(s), and store the result of the computation in a variable, before the EXEC, as shown in JaydipJ's answer.

SQL Server stored procedure with empty body

CREATE PROCEDURE syntax:
CREATE { PROC | PROCEDURE } [schema_name.] procedure_name [ ; number ]
[ { #parameter [ type_schema_name. ] data_type }
[ VARYING ] [ = default ] [ OUT | OUTPUT | [READONLY]
] [ ,...n ]
[ WITH <procedure_option> [ ,...n ] ]
[ FOR REPLICATION ]
AS { [ BEGIN ] sql_statement [;] [ ...n ] [ END ] }
[;]
<sql_statement> ::=
{ [ BEGIN ] statements [ END ] }
[ ] (brackets) Optional syntax items. Do not type the brackets.
{ } (braces) Required syntax items. Do not type the braces.
And human readable form:
Let's try to write stored procedure with empty body:
CREATE PROC my_proc AS
-- please treat it as separate call, for example with different session
EXEC my_proc
is perfect valid syntax.
LiveDemo
So it looks like that sql_statement could be empty.
Now let's try the same with but this time with BEGIN/END block:
CREATE PROC my_proc AS
BEGIN
END
-- Incorrect syntax near 'END'.
LiveDemo2
Why is the first example valid? If sql_statement allows nothing then second example should work too or the doc is inaccurate.
EDIT
well, that's because in the first example it isn't an empty body, your sp will be: EXEC my_proc
The case was to show that I could call SP. But you could add GO or use EXEC:
CREATE PROC my_proc AS
GO
EXEC my_proc
or
EXEC('CREATE PROC my_proc AS')
EXEC my_proc
LiveDemo3
The syntax error is not related to the proper syntax for stored procs. It is the proper syntax for "BEGIN/END". BEGIN/END requires some SQL inside of it to be valid. The documentation for BEGIN/END shows this:
https://msdn.microsoft.com/en-us/library/ms190487.aspx
BEGIN
{
sql_statement | statement_block
}
END
The grammar in the CREATE PROC documentation is indeed not fully correct, as it says that sql_statement is required for "CREATE PROC", when it is actually not required.

SQL Server stored procedure parameter assignment

Why can I not use functions to directly assign stored procedure parameters?
e.g.
exec myStoredProc #firstParam = aFunctionThatReturnsAnAppropriateValue()
but I find I have to decalre a variable just to hold the value
declare #temp type = aFunctionThatReturnsAnAppropriateValue()
exec myStoredProc #firstParam = #temp
which seems redundant
Quoting EXECUTE (Transact-SQL):
Execute a stored procedure or function
[ { EXEC | EXECUTE } ]
{
[ #return_status = ]
{ module_name [ ;number ] | #module_name_var }
[ [ #parameter = ] { value
| #variable [ OUTPUT ]
| [ DEFAULT ]
}
]
[ ,...n ]
[ WITH RECOMPILE ]
}
[;]
You can see that it explicitly says #variable or value. I guess this is a language limitation, as you can neither write a call to a function as a variable or as a value; it is executable code (an expression), and the short-hand assignment during variable declaration is just a misleading bonus.
EDIT: Compare the difference of description for DECLARE and EXECUTE:
For DECLARE
=value
Assigns a value to the variable in-line. The value can be a constant or an expression, but it must either match the variable
declaration type or be implicitly convertible to that type.
When looking through the page for EXECUTE, I do not see the mention of an expression. It seems to me that it would be handy, as I think you are trying to point out.