SQL Procedure not working according to sequence - sql

I wanted to write stored procedure for login table.
This procedure involves, checking if LogInID already exists, and if it exists then dont allow to insert record with same LogInID.
I procedure is as follows:
ALTER PROCEDURE logTRAN
#id varchar(25),
#pass varchar(25)
AS
BEGIN TRANSACTION
insert into login values(#id,#pass)
IF EXISTS (select count(*) from login where LogInID=#id)
BEGIN
PRINT 'USER ALREADY EXISTS'
ROLLBACK
END
ELSE
BEGIN
COMMIT TRANSACTION
END
I execute it as follows:
exec logTRAN '0L036','aaa' //this is repeated record with LogInID '0L036'
But Record gets inserted with following result:
(1 row(s) affected)
USER ALREADY EXISTS
Msg 266, Level 16, State 2, Procedure logTRAN, Line 0
Transaction count after EXECUTE indicates that a COMMIT or ROLLBACK TRANSACTION statement is missing. Previous count = 2, current count = 0.
What can be the mistake in my stored procedure?
Please guid me.

Change your SP to
CREATE PROCEDURE logTRAN
#id varchar(25),
#pass varchar(25)
AS
IF EXISTS (SELECT 1 FROM login WHERE LogInID = #id)
PRINT 'USER ALREADY EXISTS'
ELSE
INSERT INTO login VALUES(#id, #pass)
Here is SQLFiddle demo
On a side note: IMHO you don't need an SP for this at all. That what a UNIQUE constraint is for.

Change your query like below:
ALTER PROCEDURE logTRAN
(
#id varchar(25),
#pass varchar(25)
)
AS
BEGIN
IF EXISTS (SELECT LogInID from login where LogInID=#id)
BEGIN
PRINT 'User Already Exists'
END
ELSE
BEGIN
INSERT INTO login Values(#id,#pass)
END
END
RETURN
Or if you don't want to print just use:
ALTER PROCEDURE logTRAN
(
#id varchar(25),
#pass varchar(25)
)
AS
BEGIN
IF NOT EXISTS (SELECT LogInID from login where LogInID=#id)
BEGIN
INSERT INTO login Values(#id,#pass)
END
END
RETURN
I would recommend to use peterm's Answer. You should use SELECT 1 inplace of SELECT LogInID.

Related

Return user information after validate user in stored procedure?

I have a stored procedure to validate a user. After validate right now I am returning true if user is validated but how can I return all the details of that user instead of true?
IF (#Flag='Authenticate')
BEGIN
IF EXISTS(SELECT UserID FROM UserInformation WITH(NOLOCK)WHERE UserName=#UserName and Password=#UserPassword )
BEGIN
INSERT INTO UserLoginHistory(UserId,LastLoggedIn)
SELECT #Userid,GETDATE()
select 'true' as [Output]
END
END
Try something like below query - You can declare more variables as needed, and store all those information in variables which you want to return.
IF (#Flag='Authenticate')
BEGIN
Declare #UserID varchar(50) = null
SELECT #UserID = UserID FROM UserInformation WITH(NOLOCK) WHERE UserName=#UserName and Password=#UserPassword )
IF (#UserID is not NULL)
BEGIN
INSERT INTO UserLoginHistory(UserId,LastLoggedIn)
SELECT #Userid,GETDATE()
SELECT #Userid
END
END
You don't need a separate "if" to check if the user already exists. You can put that all into a single query:
IF (#Flag = 'Authenticate')
BEGIN
INSERT INTO UserLoginHistory(UserId, LastLoggedIn)
SELECT v.Userid, GETDATE()
FROM (VALUES (#UserId)) v(UserId)
WHERE EXISTS (SELECT 1
FROM UserInformation ui
WHERE ui.UserName = #UserName AND ui.Password = #UserPassword
);
SELECT ui.*
FROM UserInformation ui
WHERE ui.UserName = #UserName AND ui.Password = #UserPassword;
END;
Also, I am concerned about #UserPassword. Hopefully that value is encrypted! You should not have clear-text passwords anywhere in an application that has a user other than you -- even for a demo or course.

Save Exec results in variable

I already have a stored procedure GET_ROW that is doing a select, according to input ID:
SELECT TOP 1 *
FROM MyTable
WHERE ID = #ID
Now, I want to create another stored procedure that checks if an #ID exists. If it exists, return the existing row. Otherwise, create a new row with the requested #ID and return it.
So, I'm thinking of something like this:
Declare ResRow
begin tran
ResRow = Exec GET_ROW #ID
if exists (ResRow)
return ResRow
else
Insert into ...
return Exec GET_ROW #ID
commit
After executing this code, I want to be sure that only one row with #ID exists in the database (no duplicated row with same ID)
You must create table variable in first and then use following query:
Declare #T Table(Col1 int, Col2 int ,...)
Insert Into #T
Exec Get_Row #ID
Select * From #T
How about this:
CREATE PROCEDURE dbo.InsertOrFetch #ID INT
AS
BEGIN
-- check if it doesn't exist yet
IF NOT EXISTS (SELECT * FROM dbo.MyTable WHERE ID = #ID)
INSERT INTO dbo.MyTable(ID)
VALUES (#ID)
-- now return the row
SELECT TOP (1) *
FROM dbo.MyTable
WHERE ID = #ID
END
If you first check if the row doesn't exist it, and if so, insert the new data - then the SELECT afterwards will always return the row (pre-existing or newly inserted) to you.

How Can i Create procedure to forbid duplicated email?

I Have registration Form (asp.net) that include for text box . one of them is email text box . and iam using Linq To SQL .
i have been write procedure to forbid duplication
CREATE PROCEDURE CheckEmail ( #email NVARCHAR(50) )
AS
BEGIN
SET NOCOUNT ON;
IF NOT EXISTS ( SELECT C.Email
FROM Customer C
WHERE Email = #email )
BEGIN
INSERT INTO Customer
( Email )
VALUES ( #email )
END
ELSE
BEGIN
ROLLBACK TRANSACTION
END
END
is this a good way? How can I return error?
You can use RAISEERROR() function to return error. Also because you never tried to insert a duplicate email you do not need to ROLLBACK
CREATE PROCEDURE CheckEmail ( #email NVARCHAR(50) )
AS
BEGIN
SET NOCOUNT ON;
IF NOT EXISTS ( SELECT C.Email
FROM Customer C
WHERE Email = #email )
BEGIN
INSERT INTO Customer
( Email )
VALUES ( #email )
END
ELSE
BEGIN
DECLARE #ErrorMessage VARCHAR(100) = 'ERROR: Attempt to insert duplicate email address'
RAISERROR (#ErrorMessage,11,1)
RETURN
END
END
this is assuming you using sql-server if you are using a different DBMS just look up raise error function for your system.
Note: When you return SQL Error you need to make sure that application code knows how to handle it instead of just crashing.

Trigger on delete or update that captures the sender and the command

I need to find out who is deleting / updating the data on table THETABLE, the time, using what program, and the command that is sent to the database that caused the modification.
From googling and asking some colleagues, the recommended way is on delete trigger. I know how to create trigger, for example:
create trigger whodunit
on THETABLE
for delete
as begin
insert into MyAuditTbl(moddate, ...
end
But how do I get the command that is sent to the DB (query / stored procedure), application name, IP address, etc.?
I found some script and customized it to fit my needs:
create trigger AuditTHETABLE
on THETABLE
for delete, update
as begin
set nocount on
declare #shouldlog bit, #insertcount bigint, #deletecount bigint
select
#shouldlog = 1,
#insertcount = (select count(*) from inserted),
#deletecount = (select count(*) from deleted)
-- if no rows are changed, do not log
if #insertcount < 1 and #deletecount < 1 begin
select #shouldlog = 0
end
-- ... other checks whether to log or not
if #shouldlog = 1 begin
-- prepare variable to capture last command
declare #buffer table (
eventtype nvarchar(30),
parameters int,
eventinfo nvarchar(4000)
)
-- use DBCC INPUTBUFFER to capture last command
-- unfortunately only the first 255 characters are captured
insert #buffer
exec sp_executesql N'DBCC INPUTBUFFER(##spid) WITH NO_INFOMSGS'
declare #lastcommand varchar(max)
select #lastcommand = eventinfo from #buffer
-- insert into audit table
insert into myauditlog(
eventdate, tablename, hostname,
appname, insertcount, deletecount, command
) values(
getdate(),
'THETABLE',
host_name(),
app_name(),
#insertcount,
#deletecount,
#lastcommand
)
end
end

T-SQL Stored Procedure with While Loop causing Errors in Primary Key Constraints

So I have this MS SQL Stored Procedure:
ALTER PROCEDURE [dbo].[Import_Agent_Client_Bucket_2010]
AS
BEGIN
-- Loop Through Each Agent, Create a Bucket, Add their Clients to the Bucket
DECLARE Agent_Cursor CURSOR FOR
SELECT Agent_GUID, Agent_ID
FROM realforms_2011.dbo.Agent
DECLARE #Agent_GUID uniqueidentifier
DECLARE #Agent_ID int
OPEN Agent_Cursor;
FETCH NEXT FROM Agent_Cursor
INTO #Agent_GUID, #Agent_ID;
WHILE ##FETCH_STATUS = 0
BEGIN
-- Create a bucket for each agent
DECLARE #cbPKTable TABLE (cbPK UNIQUEIDENTIFIER, cbID int)
INSERT INTO realforms_2011.dbo.Client_Bucket ([Description] ) OUTPUT inserted.Client_Bucket_GUID, inserted.Client_Bucket_ID INTO #cbPKTable
SELECT ISNULL(a.First_Name, ' ') + ' ' + ISNULL(a.Last_Name, ' ') + '''s Clients'
FROM realforms_2011.dbo.Agent a
WHERE Agent_GUID = #Agent_GUID
DECLARE #Client_Bucket_GUID uniqueidentifier
SELECT #Client_Bucket_GUID = cbPK FROM #cbPKTable
DECLARE #Client_Bucket_ID int
SELECT #Client_Bucket_ID = cbID FROM #cbPKTable
INSERT INTO realforms_2011.dbo.Agent_Client_Bucket (Agent_GUID, Agent_ID, Client_Bucket_GUID, Client_Bucket_ID)
VALUES (#Agent_GUID, #Agent_ID, #Client_Bucket_GUID, #Client_Bucket_ID)
DECLARE #Client_GUID uniqueidentifier
DECLARE #Client_ID int
-- Get clients from the server (2010)
DECLARE Client_Cursor CURSOR FOR
SELECT C.Client_ID
FROM realforms.dbo.Client C
INNER JOIN realforms.dbo.Agent_Client AC ON AC.Client_ID = C.Client_ID
WHERE AC.Agent_ID = #Agent_ID
ORDER BY C.Client_ID ASC
OPEN Client_Cursor;
FETCH NEXT FROM Client_Cursor
INTO #Client_ID
-- loop through each 2010 client
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #myNewPKTable TABLE (myNewPK UNIQUEIDENTIFIER)
INSERT INTO realforms_2011.dbo.Client (Client_ID,Name,Secondary_Name,[Address],Address_2,City_State_Zip,Phone,Email_Address,Secondary_Email_Address,Create_Date,Last_Change_Date,[Status],File_Under,[Year]) OUTPUT inserted.Client_GUID INTO #myNewPKTable
SELECT c.Client_ID,Name,Secondary_Name,[Address],Address_2,City_State_Zip,Phone,Email_Address,Secondary_Email_Address,Create_Date,Last_Change_Date,[Status],File_Under,2010
FROM realforms.dbo.Client C
INNER JOIN realforms.dbo.Agent_Client AC ON AC.Client_ID = C.Client_ID
WHERE AC.Agent_ID = #Agent_ID AND C.Client_ID = #Client_ID
SELECT #Client_GUID = myNewPK FROM #myNewPKTable
INSERT INTO realforms_2011.dbo.Client_Bucket_Client (Client_Bucket_GUID, Client_GUID, Client_ID, Client_Bucket_ID, [Year])
VALUES (#Client_Bucket_GUID, #Client_GUID, #Client_ID, #Client_Bucket_ID, 2010)
PRINT 'Client Bucket GUID: '
PRINT #Client_Bucket_GUID
PRINT 'Client GUID: '
PRINT #Client_GUID
FETCH NEXT FROM Client_Cursor
INTO #Client_ID;
END;
CLOSE Client_Cursor;
DEALLOCATE Client_Cursor;
FETCH NEXT FROM Agent_Cursor
INTO #Agent_GUID, #Agent_ID;
END;
CLOSE Agent_Cursor;
DEALLOCATE Agent_Cursor;
END
But I get an error message on just a very few of the items, it says
Msg 2627, Level 14, State 1, Procedure
Import_Agent_Client_Bucket_2010, Line
71 Violation of PRIMARY KEY constraint
'Client_Bucket_Client_PK'. Cannot
insert duplicate key in object
'dbo.Client_Bucket_Client'. The
statement has been terminated.
EDIT:
OK, I see what you're doing there, I apologize for missing the OUTPUT statement. Based on that information, it seems like the code could break if a record is not inserted into the Client table in the line right before SELECT #Client_GUID = myNewPK FROM #myNewPKTable. If no record is inserted, you would wind up grabbing the GUID from the previous record and when you go to insert that it would cause the PK violation. You might have to check to make sure that records are being inserted into the Client table.
ORIGINAL ANSWER:
It looks like you're declaring a table:
DECLARE #myNewPKTable TABLE (myNewPK UNIQUEIDENTIFIER)
But then you never put anything into it, so this statement must return null:
SELECT #Client_GUID = myNewPK FROM #myNewPKTable
EDIT:
Why not just do this? I don't see why the table #myNewPKTable is even being created.
SET #Client_GUID = NEWID()
EDIT:
I think the reason you are getting the primary key violation is because #Client_Bucket_GUID is null. At the beginning of the procedure, there is this code:
-- Create a bucket for each agent
DECLARE #cbPKTable TABLE (cbPK UNIQUEIDENTIFIER, cbID int)
...
DECLARE #Client_Bucket_GUID uniqueidentifier
SELECT #Client_Bucket_GUID = cbPK FROM #cbPKTable
After this code is run #Client_Bucket_GUID will always be null. Again, you would have to insert records into #cbPKTable if you wanted to get anything out of it. If you're trying to create a new UNIQUEIDENTIFIER and store it in #Client_Bucket_GUID, just use the NEWID() function.