Stored Procedure for Employee Clock table - c#

I have an Employee Clock table with these columns:
EmpID nvarchar(200) (FK)
EmpName nvarchar(100)
Password nvarchar(100)
punchTime datetime
punchType bit
I am going to design an application (in C#) that lets employees check in and check out.
The process will be: when an employee wants to check in, they will input their EmpID and Password, then hit Enter. In the backend, the app will run a stored procedure that enters the values of EmpID, Password, EmpName (will be retrieved from Employee table based on EmpID), punchTime and punchType (= 1 for Check In, 0 for Check Out).
The process of checking out is exactly same as checking in, the employee will just need to enter their EmpID and Password, then hit Enter.
I am looking for a stored procedure that could do something like:
when keyEnter.hit (from C#) => run the stored procedure:
if max(EmpID) has punch = 1 (the employee already checked in), then set punch = 0 with provided punchTime and EmpID
else if max(EmpID) has punch = 0 (the employee already checked out), then set punch = 1 with punchTime and EmpID
I have been messing around with the stored procedure, but did not get the result as I wanted. I appreciate any help.

Having worked on a clock in/out system, I can warn you now that it is not as simple as you think. If a user misses a clock in or out, you will have to manually insert a new record and then correct all the punch types after that point in time... get ready to write that stored procedure before you move to production! Also, I am going to ignore the odd format of the EmployeeClock table - why are you storing the password in it? Regardless, your question is about a stored procedure so let's deal with that.
I assume that there is an Employee table that stores the name and the password. The password should be validated before any record is created in the EmployeeClock so I will do that. Next, you should see if there is an existing record in EmployeeClock for the employee - it could be the first time they ever log in. Finally, if there is an existing record in EmployeeClock, create a new record and flip the bit for the punchType. Finally, you may want to return a result so you know whether it was successful or not. Additionally, you may want to log the information from a bad attempt somewhere (which I will not show). Putting this all together:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE dbo.EmployeeClockInOut
#EmpID NVARCHAR(200),
#Password NVARCHAR(100)
AS
BEGIN
SET NOCOUNT ON;
-- you should make sure that the employee password is
-- correct before you do anything... assume you have a
-- Employee table that you can look up
IF EXISTS(SELECT 1 FROM dbo.Employee WHERE EmpId = #EmpID AND [Password] = #Password)
BEGIN
-- have they ever clocked in?
IF EXISTS(SELECT 1 FROM dbo.EmployeeClock WHERE EmpID = #EmpID)
BEGIN
-- they have clocked in at least once - flip the punch type
-- from their last clock in
INSERT INTO dbo.EmployeeClock(EmpID, EmpName, [Password], punchTime, punchType)
SELECT TOP(1) #EmpID, e.EmpName, #Password, GETDATE(),
CASE WHEN c.punchType = 1 THEN 0 ELSE 1 END
FROM dbo.Employee AS e
INNER JOIN dbo.EmployeeClock AS c ON e.EmpID = c.EmpID
WHERE e.EmpID = #EmpID
ORDER BY c.punchTime DESC;
END
ELSE
BEGIN
-- there is no clock in record - create first clock in entry
INSERT INTO dbo.EmployeeClock(EmpID, EmpName, [Password], punchTime, punchType)
SELECT #EmpID, EmpName, #Password, GETDATE(), 1 FROM dbo.Employee WHERE EmpID = #EmpID
END
SELECT 'Clock in/out successful' AS [Result];
END
ELSE
BEGIN
-- log the attempt or alert the user...
SELECT 'EmpID or Password was invalid' AS [Result];
END
END
GO

Related

I am storing identity value of one table to another table in the foreign key but when make relationship it gives me error

I am storing identity value of one table to another table in the foreign key but when trying to create a relationship, I get an error 332.
ALTER proc [dbo].[spRegisterUser]
#Doj date,
#UserName nvarchar(100),
#Password nvarchar(10),
#Reference nvarchar(50),
#Aadhar nvarchar(50),
#Email nvarchar(50)
AS
BEGIN
Declare #count int
Declare #ReturnCode int
Select #count = COUNT(UserName)
from tblUsers where UserName = #UserName
if #count > 0
Begin
Set #ReturnCode = -1
End
Else
Begin
Set #ReturnCode = 1
insert into tblUsers (DoJ,UserName,Password,Reference,Aadhar,Email)
Output inserted.User_ID into tblUserProfiles(UserID)
values (#DoJ,#UserName,#Password,#Reference,#Aadhar,#Email)
End
Select #ReturnCode as ReturnValue
end
Error is:
The target table 'tblUserProfiles' of the OUTPUT INTO clause cannot be on either side of a (primary key, foreign key) relationship. Found reference constraint 'FK_tblUserProfiles_tblUsers'.
How to overcome this problem? I need PK FK relationship and I also want FK value automatically saved in the column.
Please find a solution for me. Thanks
Since this stored procedure is really only ever inserting one single row of data, you could use this code:
declare #NewUserID INT
insert into tblUsers (DoJ, UserName, Password, Reference, Aadhar, Email)
values (#DoJ, #UserName, #Password, #Reference, #Aadhar, #Email);
-- get the newly created ID for the new user
SELECT #NewUserID = SCOPE_IDENTITY();
-- insert into the other table
INSERT INTO dbo.tblUserProfiles (UserID)
VALUES (#NewUserID);
This assumes that the User_ID column in the tblUsers table is in fact an Identity column.
Another option would be to use a trigger for insert on the users table, that will insert records into the user profiles table whenever they are inserted into the users table. The advantage of this approach is that even if users are inserted directly to your table (I mean, not using your stored procedure), they will still have a record created in the user profiles table:
CREATE TRIGGER tblUsers_INSERT ON tblUsers
FOR INSERT
AS
BEGIN
INSERT INTO tblUserProfiles(UserId)
SELECT User_ID
FROM Inserted
END

How to solve concurrency Insert issue in SQL Server stored procedure

I have a stored procedure which takes #id as input parameter. Table Student has primary key on Id_Column and Count_Column. If any record is present in table for the given id, then I select the max Count_Column from table and insert new row by incrementing max Count_Column by 1 else with zero value.
I'm calling this stored procedure from ado.net code in a WCF service and this service is called from an asp.net web application.
This stored procedure works fine in normal cases, but when multiple users calls it at same time, primary key violation issue happen, same case I have reproduced by making application multithreaded too. I know that this issue is related to concurrency, initially I was using with(nolock) in select query, but I have removed this now.
Somewhere I have read that by setting transaction isolation level it can be solved but when I tried I am some getting rollback transaction exception.
Please let me know any efficient solution for this problem.
declare #iCount = 0;
if exists(select 'x' from Student with(nolock) where Id_Column = #iId)
begin
set #iCount = (select max(Count_Column)
from Student
where Id_Column = #iId)
end
insert into Student
values(#id, #iCount + 1);
2nd solution:
begin try
set transaction isolation level serializable
begin transaction
declare #iCount = 0;
if exists(select 'x' from from Student with(nolock) where Id_Column = #iId)
begin
set #iCount = (select max(Count_Column)
from Student
where Id_Column = #iId)
end
insert into Student
values(#id, #iCount + 1);
commit transaction
end try
begin catch
rollback transaction
end catch
Try something like......
BEGIN TRY
BEGIN TRANSACTION;
DECLARE #iCount INT;
IF EXISTS(SELECT 1 FROM Student WITH(UPDLOCK,HOLDLOCK) WHERE Id_Column = #iId)
BEGIN
select #iCount = ISNULL(max(Count_Column), 0) + 1
from Student WITH(UPDLOCK,HOLDLOCK) where Id_Column = #iId
insert into Student
values(#id, #iCount);
END
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF (##TRANCOUNT <> 0)
ROLLBACK TRANSACTION;
END CATCH
Important Note
You should really be using Identity column here to handle the auto-increment value. If you are using sql server 2012 or later you also have another option of using Sequence Object also an auto-increment .
There is a failsafe strategy: SERIALIZABLE isolation and retry in case of deadlock. The definition of SERIALIZABLE is as-if-serial execution which excludes all race conditions.
I don't know why you are catching exceptions there. All that accomplishes is suppress errors.
Oracle has sequences and MS followed with Sql Server 2012 with their implementation calling it sequence objects which is not table specific.
CREATE SEQUENCE
Schema.MySequence
AS int
INCREMENT BY 1 ;
You can then use it as follow:
DECLARE #iCount int ;
SET #iCount = NEXT VALUE
FOR Schema.MySequence;
This is similar as the nextval in Oracle and will ensure uniqueness. The only question is if you will accept gaps in your count sequence in the case of rolledback/failed transactions or not...

Password change every 90 days in MVC and SQL Server

I want a method to ask the user to change his password after having it for 90 days.
I have something like this
ALTER PROCEDURE [dbo].[spRecoverPassword]
(
#sUsername varchar(50),
#sPassword varchar(100),
#sPasswordSalt varchar (128)
)
AS
BEGIN
SET NOCOUNT ON;
if (exists (select 1
from USER
where Username = #sUsername))
begin
update USER
set Password_Salt = #sPasswordSalt,
Password = #sPassword,
where Username = #sUsername;
select 1;
end
else
begin
select -1;
end
END
Then I have a method to call this stored procedure, from ASP.NET MVC. And somehow I would like to check the date that the password was last changed and do so that if that date is 90 days higher than today it will redirect it to the recover pass again. How should I do this?
Thanks
Add a new column in users Table to save last modified date of password.
on Get method calculate the difference between Last modified date of password and today's date. and do what you want to do.
Very easy:
add a date field in USER table for example last_changed.
update last_changed with now() in the query
check days difference DATEDIFF(now(),last_changed)>90

Build up list first, or query for every record? (Have to check for duplicates)

I'm doing some web scraping to build up a personal SQL database. As I'm looping through the web requests, I'm adding records. The only thing is, duplicates sometimes appear in the web requests and I want to make sure to only add a record if it doesn't already exist in my database. I gather this can be done by performing an SQL query before every insert to make sure that record hasn't already been added, but is this the best way to do it? Would it make more sense to build up a Generic.List first, and then do all my database inserts at the end?
You can create a stored procedure that will attempt to update a record and then insert if the update query did not update any rows. This will minimize the number of queries that need to be run and prevent checking for the row's existence. A little bit of Googling found this. The second option looks like it might be what you are looking for.
/*
Same SP is used to INSERT as well as UPDATE a table.
Here we are avoid unnecessary checking of whether the record exists or not.
Instead try to Update directly. If there is no record then ##RowCount would be 0.
Based on that Insert it as a new record.
*/
CREATE PROCEDURE uspUPSert_Ver2
(
#empID INT,
#fname VARCHAR(25),
#lname VARCHAR(25),
#emailid VARCHAR(50)
)
AS
BEGIN
SET NOCOUNT ON
BEGIN TRAN
UPDATE tblUpsert WITH (SERIALIZABLE)
SET emailid = #emailid ,
firstname = #fname ,
lastname = #lname
WHERE EmpID = #empID
IF ##ROWCOUNT = 0
BEGIN
INSERT INTO tblUpsert VALUES (#empID, #fname, #lname, #emailid)
END
COMMIT TRAN
END
GO
seems like you would need either a primary key or a unique constraint on the columns that identify the rows as duplicate. Then if there is an error in the insert that violates the unique constraint the row won't insert. Catch the exception, log it to a different table for future validation and move to the next row.
http://www.w3schools.com/sql/sql_unique.asp

Stored Procedure for Account Creation/Update/Delete/Authenticate

I'm new to developing stored procedures so in my attempt I got a bit mind-boggled. I understand basically what they are conceptually, but having issues when implementing. For reference, I'm hand-coding, but intend to use SQL Server, ASP.NET, and C# technologies.
The basic idea is for a user to create their own user account, update their information as they see fit, delete their account, and authenticate the information as necessary (usernames&passwords, account information, etc). I presume this must be done with 4 different stored procedures: createAccount, modAccount, delAccount, authentAccount.
My understanding is that the C#/ASP should be doing the actual data collection and then transferring the data to SQL for insertion into the database. Please correct me if I'm wrong or if there's a more efficient method (speed is extremely important for this).
Getting started with the first stored procedure (create), I coded this:
CREATE PROC createAccount
AS
INSERT INTO Customer (cssn, first_name, middle_name, last_name, company, address, phone_number, email, account, occupation, nationality, social, successful_invites)
VALUES ()
GO
What do I put in for values? The variable that's used on the C# side?
I'm also not sure how I should incorporate security in this space and security is going to be important as well.
If you could provide examples with your explanation, that would be EXTREMELY helpful.
Here's the basic form of your SP (first 3 columns shown):
create procedure createAccount
(
#cssn varchar(100),
#first_name varchar(100),
#last_name varchar(100),
... -- remaining columns
)
as
begin
insert into Customer (cssn, first_name, last_name, ... )
values (#cssn, #first_name, #last_name, ... )
end
One side note, ASP has user accounts built in and set up automatically if you want to just use those (SqlMembershipProvider).
CREATE PROCEDURE createAccount
#cssn VARCHAR(100)
, #first_name VARCHAR(100)
, #middle_name VARCHAR(100)
, #last_name VARCHAR(100)
, #company VARCHAR(100)
, #address VARCHAR(150)
, #phone_number VARCHAR(20)
, #email VARCHAR(100)
, #account VARCHAR(100)
, #occupation VARCHAR(100)
, #nationality VARCHAR(100)
, #social VARCHAR(100)
, #successful_invites INT
AS
BEGIN
INSERT INTO Customer ( cssn, first_name, middle_name, last_name, company, address, phone_number, email, account, occupation, nationality, social, successful_invites )
VALUES ( #cssn, #first_name, #middle_name, #last_name, #company, #address, #phone_number, #email, #account, #occupation, #nationality, #social, #successful_invites )
END
I just guessed at the data types. As for security, the only thing you need to add is re-validation rules (i.e. blocking of HTML tags and stuff in your VARCHAR fields). Otherwise, security is built-in automatically because you are using parameters and variables (and not using dynamic sql).
If you want to use SqlMembershipProvider, you have already in asp.net a set of controls that will help you. You may have to use their tables and not your Customer table, but that is ok since the membership provider will take care of everything. Just google for more info about membership provider and the login controls.
You have in other answers examples of stored procedures, but why using stored procedures? An ORM is a much easier and more productive way of doing things. My favorite is NHiberntate. LINQ to SQL, Entity Framework are from Microsoft. Just google for a "linq to sql hello world" to see how it's done.
In 99.99% of the cases an ORM is just fine.There are rare cases when you need to replace the ORM with a sql query or sp.
Stored procedures are nice ways to store SQL scripts in a central location among other things. That's the basic, most simplest concept. If you have a script in your code with (update TABLE set EMAIL = '"+email+"'), you're better off putting into a stored procedure. You can even do additions and updates in the same procedure returning the ID of the existing/updated or newly created record. You can get VERY creative with them.
You can do updates and additions in the same procedure if it's secure of course.
create usp_AddEmail(
#email varchar(50)
)
AS
DECLARE #EMAILID INT;
SET #EMAILID = (SELECT ID FROM TABLE WHERE EMAIL = #EMAIL);
IF ISNULL(#EMAILID,0) = 0
BEGIN
INSERT INTO TABLE(EMAIL) VALUES(#email);
SET #EMAILID = (SELECT ID FROM TABLE WHERE EMAIL = #EMAIL);
END
SELECT #EMAILID AS EMAILID
In C#, you use CommandType = CommandType.StoredProcedure to let it know it's a stored procedure. Then you use .Parameters.AddWithValue(sqlParameter, value) to pass the value. (You can wrap it into a method):
SqlConnection connLoc = new SqlConnection(YourConnectionSring);
SqlCommand commLoc = new SqlCommand();
SqlDataReader drLoc;
commLoc.Connection = connLoc;
commLoc.CommandType = CommandType.StoredProcedure;
commLoc.CommandText = "usp_AddEmail";
commLoc.Parameters.AddWithValue(#email, emailString);
connLoc.Open();
drLoc = commLoc.ExecuteReader(CommandBehavior.CloseConnection);

Categories

Resources