How stored procedure output instead of rows count? - sql-server-2008

My stored procedure always returns 0. I tried unique data and duplicated but the insert is done with success but the return value is always the same #new_identity = 0
CREATE PROCEDURE [dbo].[spAddAuthor]
#Author tyAuthor READONLY,
#new_identity INT = NULL OUTPUT
AS
BEGIN
SET NOCOUNT ON;
-- check if the author exists
IF NOT EXISTS (SELECT Id_Author FROM dbo.Authors
WHERE (dbo.Authors.Username = (SELECT Username FROM #Author)
OR dbo.Authors.phone = (SELECT phone FROM #Author)
OR dbo.Authors.email = (SELECT email FROM #Author)))
BEGIN
INSERT INTO dbo.Authors (Username, sexe, email, phone, address)
SELECT [Username], [sexe], [email], [phone], [address]
FROM #Author
-- output the new row
SELECT #new_identity = ##IDENTITY;
END
ELSE
BEGIN
-- get the author Id if already exists
SELECT #new_identity = (SELECT TOP 1 Id_Author
FROM dbo.Authors
WHERE (dbo.Authors.Username = (SELECT Username FROM #Author)
OR dbo.Authors.phone = (SELECT phone FROM #Author)
OR dbo.Authors.email = (SELECT email FROM #Author)))
END
END

I found that in the declaration of the parameters I put null beside the output and that what caused the problem.
#new_identity INT = NULL OUTPUT
but I don't understand why, I thought the 'null' was like the default value, or when you try to make the parameter optional you add null as default value.
can someone explain, please?

Related

Parameter name conflict with column name in MySQL

The following code works fine when put it outside a function and return 1 if it exists at the table.
SET #Result = (SELECT (
CASE WHEN NOT EXISTS(SELECT 1 FROM `Member` WHERE Username = 'username') THEN 10
ELSE 1
END) r);
SELECT #Result;
But it returns 10 when I am passing the value 'username' to a function like below
CREATE DEFINER=`root`#`localhost` FUNCTION `FN_CheckUsername`(
Username VARCHAR(128)
) RETURNS int
READS SQL DATA
BEGIN
SET #Result = (SELECT (
CASE WHEN NOT EXISTS(SELECT 1 FROM `Member` WHERE Username = Username) THEN 10
ELSE 1
END) r);
RETURN #Result;
END
Please try naming your username parameter to the FN_CheckUsername function something other than Username:
CREATE DEFINER=`root`#`localhost` FUNCTION `FN_CheckUsername`(uname VARCHAR(128))
RETURNS int
READS SQL DATA
BEGIN
SET #Result = (SELECT (
CASE WHEN NOT EXISTS(SELECT 1 FROM `Member` WHERE Username = uname)
THEN 10 ELSE 1 END) r);
RETURN #Result;
END
The parameter and column name could be masking each other, which would result in the exists clause always being true.

How to handle concurrency using SELECT?

I need to validate if a record already exists. The portion, in the stored procedure, that does that is:
SELECT EXISTS (SELECT 1 FROM MyTable
WHERE FirstName = #FirstName
AND LastName = #LastName
AND Address = #Address)
BEGIN
SET #IsNewRecord = 1
END
IF #IsNewRecord = 1
BEGIN
INSERT INTO MyTable
VALUES (#FirtName, #LastName, #Addres, #City, #Phone)
END
That works fine, the issue is when the stored procedure is called by several clients at the same time, it will return the #IsNewRecord = 1.
I already tried surrounding the validation among a TRANSACTION BLOCK, but it still creates a new record.
How can I handle concurrency when using SELECT?
do like this
begin transaction
SELECT EXISTS (SELECT 1 FROM MyTable WHERE FirstName = #FirstName AND LastName = #LastName
AND Address = #Address)
BEGIN
INSERT INTO MyTable
VALUES (#FirtName, #LastName, #Addres, #City, #Phone)
END
commit

SQL Stored Procedure IF ELSE logic error

I am trying to implement an insert stored procedure. Basically, how it works is the stored procedure will check whether the record exists or not, then proceed to perform insert. A variable #status will be used as indicator. However, I found out a particular problem with this query is that when I execute the query, it will return the result #status = 1 no matter the data existed or not. However, the INSERT function is fine without any problems just the #status. The following is my implementation:
CREATE PROCEDURE save_proc
(
#userid varchar(10)
#name varchar(30)
)
AS
DECLARE #status int
if exists ( SELECT * FROM table1 where userID = #userid AND userName = #name)
SET #status = 0
else
INSERT INTO table1 (userID, userName) VALUES (#userid, #name)
SET #status = 1
SELECT #status
Try to put it in BEGIN END
CREATE PROCEDURE save_proc
(
#userid varchar(10)
#name varchar(30)
)
AS
DECLARE #status int
if exists ( SELECT * FROM table1 where userID = #userid AND userName = #name)
SET #status = 0
else
begin
INSERT INTO table1 (userID, userName) VALUES (#userid, #name)
SET #status = 1
end
If you are not putting the BEGIN END then the scope of your else statement is just the next line which is getting executed ie, INSERT INTO table1 (userID, userName) VALUES (#userid, #name) is only under the scope of else block. And SET #status = 1 is outside the scope of else block. So once the else block executes the next query will be executed which is SET #status = 1
On a side note:
When you are checking for if exists then don't use the * wildcard. Instead you can use 1 i.e,
if exists ( SELECT 1 FROM table1 where userID = #userid AND userName = #name)
else
begin
INSERT INTO table1 (userID, userName) VALUES (#userid, #name)
SET #status = 1
end
Modify code as above. You haven't defined scope of your else. It was upto insert statement only. SET #status = 1 was executed everytime

MySQL Stored Proc Variable returns NULL

I have a stored proc that is setting one of the variables to NULL, though the query returns results.
Here is the SP:
CREATE DEFINER=`xxx`#`localhost` PROCEDURE `test`(IN CLIENT_ID INT)
BEGIN
DECLARE BEGIN_RANGE INT UNSIGNED DEFAULT 0;
select client_id into #CLIENT_ID3 from clients where
client_id = CLIENT_ID limit 1;
SET #BEGIN_RANGE = (select var_value from counter where var_name =
'latest_report_id_summarized' limit 1);
SELECT latest_invoice_id into #LATEST_INVOICE_ID from
SQL_AuditPro.clients where client_id = CLIENT_ID;
END
when I run the sp, then select the results :
select #CLIENT_ID3,#BEGIN_RANGE,#LATEST_INVOICE_ID;
I get :
12345,2342342234,NULL
I have also tried:
CREATE DEFINER=`xxx`#`localhost` PROCEDURE `test`(IN CLIENT_ID INT)
BEGIN
DECLARE BEGIN_RANGE INT UNSIGNED DEFAULT 0;
select client_id into #CLIENT_ID3 from clients
where client_id = CLIENT_ID limit 1;
SET #BEGIN_RANGE = (select var_value from counter where var_name =
'latest_report_id_summarized' limit 1);
SET #LATEST_INVOICE_ID = (SELECT latest_invoice_id from
SQL_AuditPro.clients where client_id = CLIENT_ID);
END
Same result. Obviously I don't need to SET #CLIENT_ID3 it was just in there for testing. Any ideas why this won't set the variable? When I run this query by itself I get a result, and if I run :
SELECT latest_invoice_id into #LATEST_INVOICE_ID from
SQL_AuditPro.clients where client_id = 12345;
select #LATEST_INVOICE_ID;
I get :
2342342234
I have tried declaring the variable, not declaring it, and it just seems like this variable seems to fall out of scope after the SP completes, yet the other two don't.
Thanks for any help you may be able to provide.
select client_id into #CLIENT_ID3 from clients where client_id = CLIENT_ID limit 1;
^---- missing #
SET #LATEST_INVOICE_ID = (SELECT [..snip..] where client_id = CLIENT_ID);
^--missing # as well
As written, both'll be intepreted as a self-equality test, e.g. where 1 = 1. There's no way for mysql to know that the client_id on EITHER side of the = is supposed to be the parameter you passed in to the stored proc, so both client_id will be treated as field names.
Since the second query is likely to return MULTIPLE rows of data, and you're trying to assign that result into a single variable, you'll get NULL as the result.
When you have defined arguments names to match with one or more of a table columns, then in your SQL statements you must use column qualifiers to differentiate with argument names. Otherwise, they will be treated and replaced as and by argument values;
See SQL Fiddle Example
In your case, as the condition client_id = CLIENT_ID was causing to (possibly) fetch multiple records, they are not assignable to a variable of non cursor type. Hence a NULL, the default value for user defined variables, was returned.
Change your procedure body as below:
CREATE DEFINER=`xxx`#`localhost` PROCEDURE `test`(IN CLIENT_ID INT)
BEGIN
DECLARE BEGIN_RANGE INT UNSIGNED DEFAULT 0;
select c.client_id into #CLIENT_ID3
from clients c
where c.client_id = CLIENT_ID limit 1;
SET #BEGIN_RANGE =
(select var_value
from counter
where var_name = 'latest_report_id_summarized' limit 1);
SET #LATEST_INVOICE_ID =
(SELECT c.latest_invoice_id
from SQL_AuditPro.clients c
where c.client_id = CLIENT_ID);
END

Stored Procedure for Login Authentication not working MySQL

I am working Login Authentication with Stored Procedures in MySQL Database.
Below is the code, i wrote but its not working. Let me know, what is wrong.
I have below questions
How to check, whether CURSOR is empty or null
Is there any way, we write the procedure.
What I am doing..
Taking two input parameters and two parameters for ouput.
Check if the user or password is not valid, stored those values in OUT parameters
SELECT 'Invalid username and password', 'null' INTO oMessage, oUserID;
If user and password in valid, but isActive column is 0 then
SELECT 'Inactive account', 'null' INTO oMessage, oUserID;
If success,
SELECT 'Success', v_UserID INTO oMessage, oUserID;
SQL Code
DELIMITER $$
USE `acl`$$
CREATE
DEFINER = `FreeUser`#`localhost`
PROCEDURE `acl`.`checkAuthenticationTwo`(
IN iUsername VARCHAR(50),
IN iPassword VARCHAR(50),
OUT oMessage VARCHAR(50),
OUT oUserID INT
)
BEGIN
DECLARE v_isActive INT;
DECLARE v_UserID INT;
DECLARE v_count INT;
DECLARE cur1 CURSOR FOR SELECT UserID, IsActive FROM m_users WHERE (LoginName = TRIM(iUsername) OR Email = TRIM(iUsername)) AND `Password` = iPassword;
OPEN cur1;
SET v_count = (SELECT FOUND_ROWS());
IF (v_count > 0)
FETCH cur1 INTO v_UserID, v_isActive;
IF (v_isActive = 0) THEN
SELECT 'Inactive account', 'null' INTO oMessage, oUserID;
ELSE
SELECT 'Success', v_UserID INTO oMessage, oUserID;
END IF;
ELSE
SELECT 'Invalid username and password', 'null' INTO oMessage, oUserID;
END IF;
END$$
DELIMITER ;
You definitely don't need CURSORs for that; use plain simple SELECT. A more concise version of your SP might look like
DELIMITER $$
CREATE DEFINER = `FreeUser`#`localhost` PROCEDURE `acl`.`checkAuthenticationTwo`
(
IN iUsername VARCHAR(50),
IN iPassword VARCHAR(50),
OUT oMessage VARCHAR(50),
OUT oUserID INT
)
BEGIN
SELECT CASE WHEN IsActive = 0 THEN 'Inactive account' ELSE 'Success' END,
CASE WHEN IsActive = 0 THEN NULL ELSE UserID END
INTO oMessage, oUserID
FROM m_users
WHERE (LoginName = TRIM(iUsername)
OR Email = TRIM(iUsername))
AND `Password` = iPassword
LIMIT 1; -- you better protect yourself from duplicates
SET oMessage = IFNULL(oMessage, 'Invalid username and password');
END$$
DELIMITER ;
What it does it tries to select a row where username or email equals to iUsername and password equals to iPassword and outputs two values to output variables. Along the way it uses CASE to look at isActive value. If it's 0 then sets a message to 'Inactive' and NULL for userid. Otherwise it returns 'Success' message and real userid that has been found. Now, if a user has not been found both variables will be set to NULL. We can leverage that and use IFNULL() function to detect that fact and set a message to 'Invalid username and password'.
Here is SQLFiddle demo
Personally I'd go further and simplify it a bit more and make it a one-statement SP with the following interface:
Returns:
userid (which is > 0) if a user with username and password if found
0 - username and(or) password incorrect
-1 - a user is inactive
The idea is that it's a presentation layer's task to produce appropriate messages for the user and not scatter all those message literals across data layer.
CREATE DEFINER = `FreeUser`#`localhost` PROCEDURE `acl`.`checkAuthenticationThree`
(
IN iUsername VARCHAR(50),
IN iPassword VARCHAR(50),
OUT oUserID INT
)
SET oUserID = IFNULL(
(
SELECT CASE WHEN IsActive = 0 THEN -1 ELSE UserID END
FROM m_users
WHERE (LoginName = TRIM(iUsername)
OR Email = TRIM(iUsername))
AND `Password` = iPassword
LIMIT 1 -- you better protect yourself from duplicates
), 0);
Here is SQLFiddle demo