Functions - TQL passing function with parameters - tsql

I'm trying to create a simple function that will do a count on a table. I want to pass the table name in form of a parameter to the variable and this function will return the count as an int. Please see my function below and help me understand why this is not working.
CREATE FUNCTION count_rows
(#tablename varchar(100)
RETURNS int AS
BEGIN
DECLARE #emp_count AS int
declare #declaration varchar(100)
#declaration='SELECT count(*)
FROM ' + #tablename
#emp_count=cast(#declaration as int)
RETURN #emp_count
END
GO
The errors I am getting are as follows:
Msg 102, Level 15, State 1, Procedure count_rows, Line 3 Incorrect
syntax near 'RETURNS'. Msg 102, Level 15, State 1, Procedure
count_rows, Line 10 Incorrect syntax near '#declaration'. Msg 178,
Level 15, State 1, Procedure count_rows, Line 14 A RETURN statement
with a return value cannot be used in this context.

you will need to use Dynamic SQL to handle table name that is pass in from application. For dynamic sql, function is out of the question. You will need to use stored procedure to do that.

Related

Is type casting the problem in my procedure call?

I'm trying to call a procedure, but casting the argument, but even performing the casting, the received argument is still the old type.
My procedure:
CREATE PROCEDURE transfer_money(
user_origin_id int,
user_destination_id int,
amount dec
)
LANGUAGE sql
AS $delimiter$
DECLARE
wallet_origin_id integer,
wallet_destination_id integer;
BEGIN
SELECT wallet_id INTO wallet_origin_id
FROM users
WHERE id = user_origin_id;
SELECT wallet_id INTO wallet_destination_id
FROM users
WHERE id = user_destination_id;
UPDATE wallets
SET balance = balance - amount
WHERE id = wallet_origin_id;
UPDATE wallets
SET balance = balance + amount
WHERE id = wallet_destination_id;
commit;
END;
$delimiter$
My call:
CALL transfer_money(1, 2, cast(100.00 as DECIMAL));
Error:
ERROR: procedure transfer_money(integer, integer, numeric) does not exist
LINE 1: CALL transfer_money(1, 2, cast(100.00 as DECIMAL));
^
HINT: No procedure matches the given name and argument types. You might need to add explicit type casts.
SQL state: 42883
Character: 6
Casting is not the problem. Your call would even work without any casting at all, with numeric constants or untyped string constants.
CALL transfer_money(1, 2, 100);
CALL transfer_money('1', '2', '100');
Function type resolution would take care of it (unless you have declared overloaded functions that make the call ambiguous). See:
Is there a way to disable function overloading in Postgres
The problem is, that you never created that procedure to begin with. How I know that? Because what you display is syntactical nonsense that would only raise an exception. It's declared as LANGUAGE sql but uses PL/pgSQL elements. (SQL functions have no block structure with BEGIN and END, they consist of SQL commands only.)
Also, a colon where a semicolon is required.
Consider this fixed and simplified version:
CREATE OR REPLACE PROCEDURE transfer_money(_user_origin_id int
, _user_destination_id int
, _amount dec)
LANGUAGE sql AS
$proc$
UPDATE wallets w
SET balance = w.balance - _amount
FROM users u
WHERE u.id = _user_origin_id
AND w.id = u.wallet_id;
UPDATE wallets w
SET balance = w.balance + _amount
FROM users u
WHERE u.id = _user_destination_id
AND w.id = u.wallet_id;
COMMIT;
$proc$;
BTW, while executing the CALL like you display (without nesting in an explicit transaction with more commands), autocommit would take care of the COMMIT anyway, and there would be no need for an explicit COMMIT;.
But maybe more is going on than you show?

How to create a stored procedure in Postgres and return value?

I am trying to create a stored procedure in PostgreSQL. I have this script to create a stored procedure:
CREATE OR REPLACE PROCEDURE Duplicate_config_v1 (sourcequoteid IN int, sourceconfig IN varchar, destinationquote IN int, scenarioid IN int, flag INOUT varchar(10))
LANGUAGE plpgsql
AS $$
BEGIN
SELECT
Status::varchar(10)
FROM
public.duplicate_scenario_statuses
WHERE
id = 1;
--returning 'Sucess'into flag;
END
$$;
I created the stored procedure as shown above. In the procedure I am passing few input parameters and returning some value from table. I am trying to call procedure as below
Call duplicate_config_v1(1,'1'::varchar(10),1,1);
Whenever I execute this statement, I get an error:
ERROR: procedure duplicate_config_v1(integer, character varying, integer, integer) does not exist
LINE 14: Call duplicate_config_v1(1,'1'::varchar(10),1,1);
^
HINT: No procedure matches the given name and argument types. You might need to add explicit type casts.
SQL state: 42883
Character: 592
Just use function.
In manual 43.6.3. Calling a Procedure the example is something like
CREATE PROCEDURE triple(INOUT x int)
LANGUAGE plpgsql
AS $$
BEGIN
x := x * 3;
END;
$$;
that is very simple, then you can call the procedure to get the value.
manual 43.6.2. Returning from a Procedure
A procedure does not have a return value. A procedure can therefore
end without a RETURN statement. If you wish to use a RETURN statement
to exit the code early, write just RETURN with no expression.
If the procedure has output parameters, the final values of the output
parameter variables will be returned to the caller.
Meaning, that the value will return to the caller, but you need extract step to get it. That's why when " case if return value" people recommend function.
the following demo use DO COMMAND to get the value from caller.
CREATE temp TABLE duplicate_scenario_statuses (
id int,
sourcequoteid int,
sourceconfig text,
destinationquote int,
status text,
scenarioid int,
flag text
);
INSERT INTO duplicate_scenario_statuses (id, status)
VALUES (1, 'hello');
CREATE OR REPLACE PROCEDURE pg_temp.Duplicate_config_v1 (sourcequoteid IN int, sourceconfig IN text, destinationquote IN int, scenarioid IN int, flag OUT text)
LANGUAGE plpgsql
AS $$
BEGIN
SELECT
status INTO flag
FROM
duplicate_scenario_statuses
WHERE
id = 1;
END
$$;
DO $$
DECLARE
_status text;
BEGIN
CALL pg_temp.Duplicate_config_v1 (1, '1', 1, 1, _status);
RAISE NOTICE '_status: %', _status;
END;
$$;
Your procedure is defined with 5 parameters, but you only provide 4. Thus the procedure definition is not found and your CALL results in " No procedure matches the given name and argument types".
You need to provide a dummy value for the inout parameter, e.g. NULL:
call duplicate_config_v1(1,'1',1,1,null);
To "return" a value, just assign it:
flag := 'Success';
Online example

SQL Function Error: Arithmetic Overflow

I have a very simple function to calculate and format a floating point number:
ALTER FUNCTION GetUrbanHours
(
-- Add the parameters for the function here
#driverID int
)
RETURNS int
AS
BEGIN
DECLARE #Result float = 0
SELECT #Result =
FORMAT(
SUM(CONVERT(float,
CONVERT(bigint, RouteSummary.UrbanNightSeconds + RouteSummary.UrbanDaySeconds))/(60*60)),
'##,##0.00')
FROM RouteSummary WHERE DriverID = #driverID
-- Return the result of the function
RETURN #Result
END
When I call the function with a given parameters, I get the following errors:
Msg 8115, Level 16, State 2, Line 6
Arithmetic overflow error converting expression to data type int.
Msg 8114, Level 16, State 5, Line 9
Error converting data type varchar to float.
When I extract the SELECT statement and print #Result, I get the expected result. What is my problem here?
EDIT
I have rewritten the function as follows:
ALTER FUNCTION GetUrbanHours
(
#driverID int
)
RETURNS nvarchar(50)
AS
BEGIN
DECLARE #Result nvarchar(50)
DECLARE #SumSeconds bigint
DECLARE #SumHours float
SELECT #SumSeconds =
SUM(RouteSummary.UrbanNightSeconds + RouteSummary.UrbanDaySeconds)
FROM RouteSummary WHERE DriverID = #driverID
IF #SumSeconds != NULL
BEGIN
SET #SumHours = CONVERT(float, #SumSeconds) / (60*60)
SET #Result =
FORMAT(#SumHours, '##,##0.00')
END
RETURN #Result
END
When I give a parameter that returns rows from the RouteSummary Table, I get NULL. When I give a parameter that returns no rows, I get "Msg 8115, Level 16, state 2, Line 1 Arithmetic overflow error converting expression to data type int." What is wrong now? Also is there a way to identify the line in the function the error message refers to? And why can't I step into the function with the SQL Studio debugger?
To solve the first error do not assign a value to the variable. The second error is caused by your use of the TSQL FORMAT statement. The format statement returns a VARCHAR not a FLOAT. See this link for information on the FORMAT command.
MSDN link

Default Input from variable

I'm trying to call a table valued function with some variable inputs. However, I'd like to use the function's default value if the calling script doesn't set the input value.
So, assume I have the function (yes, this is a silly function)
create function dbo.testing(#countTo int=10)
returns #output table(num int)
as
begin
declare #i int
set #i=0
while #i < #countTo
begin
set #i=#i+1
insert into #output(num)
values(#i)
end
return
end
Then, I could setup a variable for the input:
declare #stop int
set #stop=15
select * from dbo.testing(#stop)
However, if #stop is left as null, I'd like to use the default value, but it will just use null (which returns nothing...). Essentially, I want the following to work:
select * from dbo.testing(isnull(#stop,default))
But it just returns the error:
Msg 156, Level 15, State 1, Line 4 Incorrect syntax near the keyword
'default'.
Is there anyway to do this without modifying the function?
After researching this a bit my conclusion (I could be wrong though) is that you can't do this without either modifying the function as described below or include a conditional null check when you call the function as:
if #stop is null
select * from dbo.testing(default)
else
select * from dbo.testing(#stop)
This might not be practical though. The alternative is to modify your function to include a null check and default value:
create function dbo.testing(#countTo int)
returns #output table(num int)
as
begin
if #countTo is null set #countto = 10
declare #i int
set #i=0
while #i < #countTo
begin
set #i=#i+1
insert into #output(num)
values(#i)
end
return
end
And call it like select * from dbo.testing(isnull(#stop,null))

how to use select case statement with sql transaction

i have one crm application. i found query in my db implementation while staff user post reply of inquiry i have to insert new inquiry ans to one table and modify another table data same time. i applied logic of as well as i represent my stored proc. but error occured in this proc.
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[InquiryPostReply]
(
#Inquiry_id VARCHAR(50),
#Priority_type VARCHAR(25),
#Status_name VARCHAR(50),
#Inquiry_Content VARCHAR(1024),
#NewId VARCHAR(50) OUT
)
AS
BEGIN
SET NOCOUNT ON;
declare #var1 int
declare #var2 int
declare #uniqueRef char(14)
set #uniqueRef = dbo.UniqueRefNum(rand(), rand(), rand(), rand())
set #var1= (SELECT [Id] FROM [OmStocks].[dbo].[tbl_Status_master] WHERE (Status_name=#Status_name))
set #var2= (SELECT [Id] FROM [OmStocks].[dbo].[tbl_Priority_master] WHERE (Priority_name=#Priority_type))
SELECT
CASE #Status_name
WHEN 'Open' THEN
BEGIN TRAN;
BEGIN TRY
INSERT INTO [OmStocks].[dbo].[tbl_Inquiry_master]
([Id],[Inquiry_id],[Priority_id],[Status_id],[Inquiry_Content],[TimeStamp])
VALUES
(#uniqueRef,#Inquiry_id,#var2,#var1,#Inquiry_Content,CONVERT(DATETIME,GETDATE(), 101))
UPDATE [OmStocks].[dbo].[tbl_Inquiry_History]
SET [Priority_id] = #var2,[Status_id] = #var1,[IsDisplay] = 1,[IsReplied] = 1,[TimeStamp] = CONVERT(DATETIME,GETDATE(), 101)
WHERE (Inquiry_id=#Inquiry_id)
COMMIT TRAN;
END TRY;
WHEN 'Close' THEN
BEGIN TRAN;
BEGIN TRY
INSERT INTO [OmStocks].[dbo].[tbl_Inquiry_master]
([Id],[Inquiry_id],[Priority_id],[Status_id],[Inquiry_Content],[TimeStamp])
VALUES
(#uniqueRef,#Inquiry_id,#var2,#var1,#Inquiry_Content,CONVERT(DATETIME,GETDATE(), 101))
UPDATE [OmStocks].[dbo].[tbl_Inquiry_History]
SET [Priority_id] = #var2,[Status_id] = #var1,[IsDisplay] = 0,[IsReplied] = 1,[TimeStamp] = CONVERT(DATETIME,GETDATE(), 101),[Activity_expire_time] = CONVERT(DATETIME,GETDATE(), 101)
WHERE (Inquiry_id=#Inquiry_id)
COMMIT TRAN;
END TRY;
END
SET #NewId = #uniqueRef
END
error occured like:
Msg 156, Level 15, State 1, Procedure InquiryPostReply, Line 21
Incorrect syntax near the keyword 'BEGIN'.
Msg 102, Level 15, State 1, Procedure InquiryPostReply, Line 31
Incorrect syntax near ';'.
Msg 102, Level 15, State 1, Procedure InquiryPostReply, Line 43
Incorrect syntax near ';'.
Msg 102, Level 15, State 1, Procedure InquiryPostReply, Line 46
Incorrect syntax near 'END'.
please help me...
You can't use CASE for this. CASE is an expression that returns a single result, not a statement that can be used for control-of-flow. I do understand that CASE is used that way in some other languages, but it's just not possible in T-SQL.
IF #Status_name = 'Open' THEN
BEGIN
-- do stuff
END
IF #Status_name = 'Close' THEN
BEGIN
-- do other stuff
END