Utilize QR Codes from Data Stored in a SQL Server Database and a Web-Based App

By:   |   Updated: 2024-01-22   |   Comments   |   Related: > Application Development


Problem

In the first tip of this series (QR Code Generator in Web-Based App from data stored in SQL Server), we developed a web application designed to generate a unique QR code for every employee listed in our database. The second series tip (QR Code Generator Web Based App that uses SQL Server) walked through expanding the web application to scan the employees' QR codes as they arrive or leave the office.

As a result, the Attendance table maintains a one-to-many relationship among the employees and tracks their movements to and from their respective offices. This tip aims to show various types of data concerning employees' activities within the office premises. To do this, the database needs additional preparations and some SQL scripts.

Solution

As we continue enhancing our web application's capabilities, we have exciting developments on the horizon. Our objective is to expand the functionality to provide users with a richer set of data insights. Specifically, we will be adding features that enable us to:

  • Display Arrival and Departure times for each employee on a per-date basis, offering a detailed view of their attendance history.
  • Calculate and showcase the total "At Office" time for each employee, streamlining the tracking of their working hours.
  • Offer a comprehensive list of employees and their "At Office" status at any specified date and time, facilitating real-time monitoring and accountability within the workplace.

These enhancements are poised to elevate the utility of our web application, making it an even more indispensable tool for effective employee management and attendance tracking.

Setting Up the Database Environment

Once the database is properly configured, building the application becomes a straightforward task.

The upcoming sections will guide you through the essential steps to prepare the database to seamlessly present the data in a user-friendly and understandable manner, ideal for management purposes.

You can utilize the conveniently provided SQL scripts to set up the database, tailored for seamless integration with the web application.

Step 1: Remove Old HRDatabase

Use this script to Drop the User in the database, the Login, and the Database.

-- MSSQLTips.com
USE master;
GO
 
-- Drop the User
ALTER AUTHORIZATION ON SCHEMA::[db_owner] TO [dbo]
GO
 
USE HRDatabase;
GO
 
DROP USER [HRDBUser]
GO
 
USE master;
GO
 
-- Sometimes the user is still logged. Kill the SPID of the logged in user to remove it
-- sp_who2
-- KILL 66
 
-- Drop the Login
DROP LOGIN [HRDBLogin];
GO
 
USE master;
GO
 
-- Drop Database if it exist
DROP DATABASE IF EXISTS HRDatabase;
GO

Step 2: Create New HRDatabase

Use this script to Create the HRDatabase, the Login, and the User, and make the User the database owner.

-- MSSQLTips.com
-- Create the New HRDatabase, Login and User
 
USE master;
GO
 
-- Create the database if it doesn't exist
CREATE DATABASE HRDatabase;
GO
 
-- Setup the Login and User that will be used with the Web Application
USE master;
GO
 
-- Create the SQL Server Login: HRDBLogin
CREATE LOGIN HRDBLogin 
WITH PASSWORD = N'MyP@ss3@1' --MUST_CHANGE
, DEFAULT_DATABASE=HRDatabase
, CHECK_EXPIRATION=ON
, CHECK_POLICY=ON
 
USE HRDatabase;
GO
 
-- Create the Database User: HRDBUser
CREATE USER HRDBUser FOR LOGIN HRDBLogin
GO
 
-- Make the new User the Owner of the database
ALTER ROLE db_owner ADD MEMBER HRDBUser
GO
 

Step 3: Create Tables

Use this script to Create the following tables and their foreign keys:

  • Companies
  • Employees
  • Title
  • Gender
  • LeaveTypes
  • Addresses
  • Leave
  • Attendance
-- MSSQLTips.com
-- Create all the tables to be used in the database
 
USE HRDatabase;
GO
 
CREATE TABLE dbo.Companies (
   id            INT CONSTRAINT PK_Companies PRIMARY KEY IDENTITY (100,1),
   CompanyName   VARCHAR(80) NOT NULL,
   CompAddress   VARCHAR(80) NOT NULL,
   CompContactNo VARCHAR(20) NOT NULL,
   IsActive      BIT       CONSTRAINT DF_IsActive_Companies DEFAULT(1),
   CreateDate    DATETIME NOT NULL DEFAULT getdate()
);
 
CREATE TABLE dbo.Employees (
   id           INT CONSTRAINT PK_Employees PRIMARY KEY IDENTITY(100,1),
   EmployeeName VARCHAR(80) NOT NULL,
   ContactNo    VARCHAR(20) NOT NULL,
   Email        VARCHAR(80) NOT NULL,
   CompID       INT       NULL,
   AddressID    INT       NULL,
   TitleID      INT       NULL,
   GenderID     INT       NULL,
   QRCreated    VARCHAR(3) NOT NULL CONSTRAINT DF_QRCreated_Employees DEFAULT ('No'),
   IsActive     BIT       NULL    CONSTRAINT DF_IsActive_Employees DEFAULT(1),
   CreateDate   DATETIME NOT NULL DEFAULT getdate()
);
 
CREATE TABLE dbo.Title (
   id           INT CONSTRAINT PK_Title PRIMARY KEY IDENTITY,
   Title        VARCHAR(80)    NOT NULL,
   TitleDescr   VARCHAR(80) NOT NULL,
   CreateDate   DATETIME NOT NULL DEFAULT getdate()
);
 
CREATE TABLE dbo.Gender (
   id           INT CONSTRAINT PK_Gender PRIMARY KEY IDENTITY,
   Gender       VARCHAR(80)    NOT NULL,
   GenderDescr  VARCHAR(80) NOT NULL,
   CreateDate   DATETIME    NOT NULL DEFAULT getdate()
);
 
CREATE TABLE dbo.LeaveTypes (
   id           INT CONSTRAINT PK_LeaveTypes PRIMARY KEY IDENTITY,
   LeaveType    VARCHAR(80)    NOT NULL,
   LeaveDescr   VARCHAR(80) NOT NULL,
   CreateDate   DATETIME    NOT NULL DEFAULT getdate()
);
 
CREATE TABLE dbo.Addresses (
   id           INT CONSTRAINT PK_Addresses PRIMARY KEY IDENTITY,
   [Address]    VARCHAR(256) NOT NULL,
   CreateDate   DATETIME    NOT NULL DEFAULT getdate()
);
 
CREATE TABLE dbo.Leave (
   id           INT CONSTRAINT PK_Leave PRIMARY KEY IDENTITY,
   LeaveTypeID  INTEGER       NOT NULL,
   StartDate    DATE       NOT NULL,
   EndDate      DATE       NOT NULL,
   LeaveNotes   VARCHAR(800) NOT NULL,
   EmpID        INT          NOT NULL,
   CreateDate   DATETIME    NOT NULL DEFAULT getdate()
);
 
CREATE TABLE dbo.Attendance (
   id           INT CONSTRAINT Pk_Attendance PRIMARY KEY IDENTITY,
   EmpID        INT NOT NULL,
   ArrDepWork   VARCHAR(20)    NOT NULL DEFAULT('Arrive'),
   CreatedDate  DATETIME    NOT NULL CONSTRAINT [DF_CreatedDate_Scans] DEFAULT (getdate())
);
GO
 
/****************************************************************************************************/
-- Add the foreign keys that creates the links between the respective tables
 
ALTER TABLE dbo.Employees ADD CONSTRAINT FK_Employees_Companies 
FOREIGN KEY(CompID)       REFERENCES Companies(id) ON DELETE CASCADE ON UPDATE CASCADE;

ALTER TABLE dbo.Employees ADD CONSTRAINT FK_Employees_Addresses 
FOREIGN KEY(AddressID)    REFERENCES Addresses(id) ON DELETE NO ACTION ON UPDATE NO ACTION;

ALTER TABLE dbo.Employees ADD CONSTRAINT FK_Employees_Titles   
FOREIGN KEY(TitleID)      REFERENCES Title(id) ON DELETE NO ACTION ON UPDATE NO ACTION;

ALTER TABLE dbo.Employees ADD CONSTRAINT FK_Employees_Genders    
FOREIGN KEY(GenderID)     REFERENCES Gender(id) ON DELETE NO ACTION ON UPDATE NO ACTION;
 
ALTER TABLE dbo.Leave     ADD CONSTRAINT FK_Leave_Employees    
FOREIGN KEY(EmpID)        REFERENCES Employees(id) ON DELETE NO ACTION ON UPDATE NO ACTION;

ALTER TABLE dbo.Leave     ADD CONSTRAINT FK_Leave_LeaveTypes   
FOREIGN KEY(LeaveTypeID)  REFERENCES LeaveTypes(id) ON DELETE NO ACTION ON UPDATE NO ACTION;
 
ALTER TABLE dbo.Attendance ADD CONSTRAINT FK_Attendance_Employee 
FOREIGN KEY (EmpID)        REFERENCES dbo.[Employees] (id) ON DELETE CASCADE ON UPDATE CASCADE
 
USE master;
GO
 

Step 4: Insert Dummy Data

Use this script to insert dummy data for the above tables and a single record for an employee going on leave.

-- MSSQLTips.com
 
-- Insert dummy data to all the tables
 
USE HRDatabase;
GO
 
INSERT INTO dbo.Companies (CompanyName, CompAddress, CompContactNo) VALUES
('Elit Pharetra Ut LLP'    , '576-5379 Eu St, Gwadar, 6269'                 , '582 593 5311'),
('Aenean Incorporated'     , '434-4279 Adipiscing, Rd, Murdochville, 687'   , '462 465 8259'),
('Odio Nam PC'             , '672-2415 Mauris Av, Puntarenas, 216678'       , '104 263 3671'),
('Luctus Et Inc'           , '540-6232 Leo Road, Ratlam, 557662'            , '408 566 8546'),
('Leo Incorporated'        , '838-9516 Ut Street, Guarapuava, 545721'       , '800 772 9022'),
('Donec Company'           , '679-3154 Sapien St, Nova Kakhovka, 7845'      , '348 984 4306'),
('Turpis LLC'              , '795-1817 Vitae, St, Stargard Szczecinski, 431', '478 294 8631'),
('Mauris Incorporated'     , '946-6131 Magna Avenue, Hospet, 45062'         , '860 335 3458'),
('Ac Consulting'           , '168-8242 Massa Av, Harstad, 88-37'            , '984 737 1838'),
('Justo Corporation'       , '843 Fusce Street, Christchurch, 58836'        , '513 215 1519')

SELECT * FROM dbo.Companies
 
INSERT INTO dbo.Title (Title, TitleDescr) VALUES
('Mr' , 'Mister'),
('Mrs' , 'Mrs'),
('Miss' , 'Misses')

SELECT * FROM Title
 
INSERT INTO dbo.Gender (Gender, GenderDescr) VALUES 
('Male'    , 'Male'),
('Female' , 'Female')

SELECT * FROM dbo.Gender
 
INSERT INTO dbo.LeaveTypes (LeaveType, LeaveDescr) VALUES
('Annual' , 'Annual Leave'),
('Study' , 'Study Leave'),
('Sick'  , 'Sick Leave'),
('Unpaid' , 'Unpaid Leave')

SELECT * FROM dbo.LeaveTypes
 
INSERT INTO dbo.Addresses (Address) VALUES
('123 North Street, Garsfontein, Pretoria') ,
('456 South Street, Brooklyn, Pretoria'),
('987 West Street, Lynnwood, Pretoria'),
('258 East Street, The Meadows, Pretoria'),
('100 Amber Street, Hatfield, Pretoria')

SELECT * FROM dbo.Addresses
 
INSERT INTO dbo.Employees (EmployeeName, ContactNo, Email, CompID, AddressID, TitleID, GenderID) VALUES
('Brendan Hewitt',    '338 325 7289', '[email protected]'    ,  105 ,1, 1, 1),
('Bethany Joseph',    '449 751 1256', '[email protected]',  105 ,2, 2, 2),
('Hadley Coleman',    '562 813 8455', '[email protected]'       ,  105 ,5, 3, 1),
('Darryl Dominguez',  '172 884 1864', '[email protected]'      ,  107 ,4, 3, 2),
('Brock Dickerson',   '788 654 4950', '[email protected]'        ,  103 ,3, 2, 1),
('George Kramer',     '366 279 8756', '[email protected]'        ,  102 ,1, 2, 1),
('Zeph Doyle',        '587 403 0422', '[email protected]'      ,  104 ,1, 1, 2),
('Gabriel Graham',    '245 566 2219', '[email protected]'           ,  108 ,2, 2, 1),
('Arden Graves',      '976 914 7081', '[email protected]'        ,  105 ,2, 3, 2),
('Veronica Charles',  '897 642 6386', '[email protected]'           ,  106 ,3, 3, 2),
('Amy Steele',        '564 838 1446', '[email protected]'            ,  108 ,2, 2, 1),
('Hashim Howe',       '581 251 0163', '[email protected]'              ,  102 ,1, 2, 1),
('Kevin Ortega',      '725 555 3112', '[email protected]'           ,  102 ,5, 1, 2)
 
SELECT * FROM dbo.Employees
 
/****************************************************************************************************/
-- Add some leave for employee Jane Doe
-- Choose an appropriate date if you want
 
INSERT INTO dbo.Leave (LeaveTypeID, StartDate, EndDate, LeaveNotes, EmpID) VALUES
(1, 
CAST(DATEADD(WEEK, 3, DATEADD(DAY, -DATEPART(WEEKDAY, GETDATE()) + 2, CAST(GETDATE() AS DATE)))AS DATE)
, CAST(DATEADD(WEEK, 3, DATEADD(DAY, -DATEPART(WEEKDAY, GETDATE()) + 6, CAST(GETDATE() AS DATE)))AS DATE)
, 'Annual leave to enjoy vacation at the coast', 102)

SELECT * FROM dbo.Leave
 
USE master;
GO

Step 5: Create Stored Procedures

Use this script to Create the five stored procedures to use with the Companies and Employees tables. The five stored procedures to use are to perform the following operations:

  • Get a list of Companies / Employees
  • Get a single Company / Employee
  • Insert a Company / Employee
  • Update a Company / Employee
  • Delete a Company / Employee
-- MSSQLTips.com
-- Drop the Stored Procedures if they exist and you need to create them again
 
USE [HRDatabase]
GO
 
DROP PROCEDURE dbo.[usp_GetCompanies]
DROP PROCEDURE dbo.[usp_GetCompany]
DROP PROCEDURE dbo.[usp_InsCompany]
DROP PROCEDURE dbo.[usp_UpdCompany]
DROP PROCEDURE dbo.[usp_DelCompany]
DROP PROCEDURE dbo.[usp_GetEmployees]
DROP PROCEDURE dbo.[usp_GetEmployee]
DROP PROCEDURE dbo.[usp_InsEmployee]
DROP PROCEDURE dbo.[usp_UpdEmployee]
DROP PROCEDURE dbo.[usp_DelEmployee]
DROP PROCEDURE dbo.[usp_InsQRScan]
 
*/
 
----------------------------------------------------------------------------
 
USE [HRDatabase];
GO
 
-- Stored Procedure to return a list of all the companies
CREATE OR ALTER PROCEDURE dbo.usp_GetCompanies
AS
BEGIN
   SELECT ID
         ,CompanyName
         ,CompAddress
         ,CompContactNo
         ,CreateDate
   FROM dbo.Companies
END;
GO
 
/* To run the Stored Procedure you would execute the following:
EXEC dbo.usp_GetCompanies;
go
*/
 
----------------------------------------------------------------------------
-- Stored Procedure to return a single Company based on an ID
 
CREATE OR ALTER PROCEDURE dbo.usp_GetCompany
   @ID    int
AS
BEGIN
   SELECT ID
         ,CompanyName
         ,CompAddress
         ,CompContactNo
         ,CreateDate
   FROM dbo.Companies
   WHERE ID = @ID
END;
GO
 
/* To run the Stored Procedure you would execute the following:
EXEC dbo.usp_GetCompany @ID = 104;
go
*/
 
----------------------------------------------------------------------------
-- Stored Procedure to Insert a single Company
 
CREATE OR ALTER PROCEDURE dbo.usp_InsCompany
   @CompanyName varchar(80),
   @CompAddress varchar(80),
   @CompContactNo varchar(20)
AS
BEGIN
   INSERT INTO dbo.Companies
            (CompanyName
            ,CompAddress
            ,CompContactNo
            ,CreateDate)
       VALUES
            (@CompanyName
            ,@CompAddress
            ,@CompContactNo
            ,getdate())
END;
GO
 
/* To run the Stored Procedure you would execute the following:
EXEC dbo.usp_InsCompany
   @CompanyName = 'Zulu-Yankee Company',
   @CompAddress = '123 Some street, Somewhere Far away, Europe ext 10',
   @CompContactNo = '999 852 7401';
go
 
SELECT * FROM dbo.Companies;
go
*/
 
----------------------------------------------------------------------------
-- Stored Procedure to Update a single Company
 
CREATE OR ALTER PROCEDURE dbo.usp_UpdCompany
   @ID          int       = null,
   @CompanyName varchar(80) = null,
   @CompAddress varchar(80) = null,
   @CompContactNo varchar(20) = null
AS
BEGIN
   UPDATE dbo.Companies
   SET    CompanyName       = ISNULL(@CompanyName , CompanyName )
         ,CompAddress       = ISNULL(@CompAddress , CompAddress )
         ,CompContactNo     = ISNULL(@CompContactNo , CompContactNo )
   WHERE ID = @ID
END;
GO
 
/* To run the Stored Procedure you would execute the following:
EXEC dbo.usp_UpdCompany
   @ID          = 106,
   @CompanyName = 'Zulu-Yanke Company',
   @CompAddress = null,
   @CompContactNo = '555 852 7401'
 
select * from dbo.Companies;
go
*/
 
----------------------------------------------------------------------------
-- Stored Procedure to Delete a single Company based on its ID
 
CREATE OR ALTER PROCEDURE dbo.usp_DelCompany
   @ID    int
AS
BEGIN
   DELETE FROM dbo.Companies
   WHERE ID = @ID
END;
GO
 
/* To run the Stored Procedure you would execute the following:
EXEC dbo.usp_DelCompany
   @ID = 103;
go
 
select * from dbo.Companies;
go
*/
 
/**************************************************************************************
   Create Stored Procedures for Employees table
*/
----------------------------------------------------------------------------
-- Stored Procedure to return a list of all the employees
CREATE OR ALTER PROCEDURE dbo.usp_GetEmployees
AS
BEGIN
   SELECT e.[ID]
         ,e.[EmployeeName]
         ,e.[ContactNo]
         ,e.[Email]
         ,e.[CompID]
         ,e.[CreateDate]
         ,c.[CompanyName]
         ,e.[QRCreated]
   FROM dbo.[Employees] e
   LEFT JOIN dbo.[Companies] c on c.ID = e.CompID
END;
GO
 
/* To run the Stored Procedure you would execute the following:
EXEC dbo.usp_GetEmployees;
go
*/
 
----------------------------------------------------------------------------
-- Stored Procedure to return a single Employee based on an ID
CREATE OR ALTER PROCEDURE dbo.usp_GetEmployee
   @ID    int
AS
BEGIN
   SELECT e.[ID]
         ,[EmployeeName]
         ,[ContactNo]
         ,[Email]
         ,[CompID]
         ,[QRCreated]
         ,e.[CreateDate]
         ,c.CompanyName
   FROM dbo.[Employees] e
   JOIN dbo.[Companies] c on c.id = e.CompID
   WHERE e.ID = @ID
END;
GO
 
/* To run the Stored Procedure you would execute the following:
EXEC dbo.usp_GetEmployee @ID = 101;
EXEC dbo.usp_GetEmployee @ID = 102;
go
*/
 
----------------------------------------------------------------------------
-- Stored Procedure to Insert a single Employee
CREATE OR ALTER PROCEDURE dbo.usp_InsEmployee
   @EmployeeName varchar(80),
   @ContactNo    varchar(80),
   @Email       varchar(20),
   @CompID       int
 
AS
BEGIN
   INSERT INTO dbo.[Employees]
            ([EmployeeName]
            ,[ContactNo]
            ,[Email]
            ,[CompID])
       VALUES
            (@EmployeeName
            ,@ContactNo
            ,@Email
            ,@CompID)
END;
GO
 
/* To run the Stored Procedure you would execute the following:
EXEC dbo.[usp_InsEmployee]
   @EmployeeName = 'James Brown',
   @ContactNo    = '999 852 7401',
   @Email       = '[email protected]',
   @CompID       = 101;
go
 
SELECT * FROM dbo.Employees;
go
*/
 
----------------------------------------------------------------------------
-- Stored Procedure to Update a single Employee
CREATE OR ALTER PROCEDURE dbo.usp_UpdEmployee
   @ID           int       = null,
   @EmployeeName varchar(80) = null,
   @ContactNo    varchar(80) = null,
   @Email        varchar(20) = null,
   @CompID       int       = null,
   @QRCreated    varchar(3) = null
AS
BEGIN
   UPDATE dbo.[Employees]
   SET    EmployeeName    = ISNULL(@EmployeeName , EmployeeName )
         ,ContactNo       = ISNULL(@ContactNo    , ContactNo    )
         ,Email           = ISNULL(@Email       , Email                             )
         ,CompID          = ISNULL(@CompID    , CompID               )
         ,QRCreated       = ISNULL(@QRCreated    , QRCreated    )
   WHERE ID = @ID
END;
GO
 
/* To run the Stored Procedure you would execute the following:
EXEC dbo.usp_UpdEmployee
   @ID           = 106,
   @EmployeeName = null,
   @ContactNo    = '777 852 7401',
   @Email        = '[email protected]',
   @CompID       = 102
 
SELECT * FROM dbo.Employees;
go
*/
 
----------------------------------------------------------------------------
-- Stored Procedure to Delete a single Employee based on its ID
CREATE OR ALTER PROCEDURE dbo.usp_DelEmployee
   @ID    int
AS
BEGIN
   DELETE FROM dbo.[Employees]
   WHERE ID = @ID
END;
GO
 
/* To run the Stored Procedure you would execute the following:
EXEC dbo.usp_DelEmployee
   @ID = 107;
go
 
SELECT * FROM dbo.Employees;
go
*/
 
---------------------------------------------------------------------
-- Create Attendance Insert Stored Procedure
CREATE or ALTER PROCEDURE dbo.usp_InsQRScan
   @EmpID    int
AS
/*
   exec dbo.usp_InsQRScan @EmpID=100
*/
BEGIN
   DECLARE @ArrDep VARCHAR(20),
         @In       INT,
         @Out      INT,
         @AttWork  VARCHAR(3)
 
   SET @In = (
            SELECT COUNT(*) 
            FROM dbo.Attendance 
            WHERE EmpID = @EmpID 
            AND ArrDepWork = 'Arrive'
            AND CAST(CreatedDate AS DATE) = CAST(getdate() as date)
            )
   SET @Out = (
            SELECT COUNT(*) 
            FROM dbo.Attendance 
            WHERE EmpID = @EmpID 
            AND ArrDepWork = 'Depart'
            AND CAST(CreatedDate AS DATE) = CAST(getdate() as date)
            )
 
   SELECT @AttWork = (CASE WHEN @In > @Out THEN 'Yes' ELSE 'No' END)
 
   IF (SELECT @AttWork) = 'Yes' 
      BEGIN
         INSERT dbo.Attendance(EmpID, [ArrDepWork], CreatedDate) 
         VALUES (@EmpID, 'Depart', getdate())
      END
   ELSE
      BEGIN
         INSERT dbo.Attendance(EmpID, [ArrDepWork], CreatedDate) 
         VALUES (@EmpID, 'Arrive', getdate())
      END
END;
go
 
--SELECT * FROM [HRDatabase].[dbo].[Employees]
--SELECT * FROM [HRDatabase].[dbo].[Attendance]
 
 
USE master;
GO
 

Step 6: Insert Dummy Data for the Attendance Table

Our next step is to generate simulated data for the Attendance table, replicating real-life scenarios where employees check in and out of work.

The script is designed to generate a week's worth of data, and all you need to do is modify the start date by selecting any Monday from the past as your reference point.

Note: The script below uses the SQL random function RAND to generate some of the arrival and departure times. As a result, your example time totals may not match the screenprints in this tip.

-- MSSQLTips.com
 
-- Insert some dummy data for Attendance
 
USE HRDatabase;
GO
 
-- SELECT * FROM HRDatabase.dbo.Attendance
-- DELETE FROM HRDatabase.dbo.Attendance;
 
-- Set the initial date for a Monday
DECLARE @theDate date = '2023-10-02'
 
-- Insert the Arrive times for all the employees
BEGIN
   INSERT INTO dbo.Attendance(EmpID, [ArrDepWork], CreatedDate)
   SELECT
      EmpID,
      'Arrive',
      CAST(
         CAST(CAST(@theDate AS DATE) AS VARCHAR(11)) + ' ' +
         CAST(DATEADD(MINUTE, CAST(RAND(CHECKSUM(NEWID())) * 60 AS INT), CAST('07:00' AS TIME)) AS VARCHAR(5))
      AS DATETIME)
   FROM (
      VALUES
         (101), (102), (103), (104), (105), (106), (107), (108), (109), (110), (111), (112)
   ) AS EmployeeList(EmpID);
 
   -- Add some movement to Employees during the day
   INSERT INTO dbo.Attendance(EmpID, [ArrDepWork], CreatedDate) VALUES
   (101, 'Depart', convert(datetime, convert(varchar, @theDate) + ' 10:35')),
   (101, 'Arrive', convert(datetime, convert(varchar, @theDate) + ' 11:25')),
   (103, 'Depart', convert(datetime, convert(varchar, @theDate) + ' 10:00')),
   (103, 'Arrive', convert(datetime, convert(varchar, @theDate) + ' 11:00')),
   (103, 'Depart', convert(datetime, convert(varchar, @theDate) + ' 13:00')),
   (103, 'Arrive', convert(datetime, convert(varchar, @theDate) + ' 14:00'))
 
   -- Insert the Depart times for all the employees
   INSERT INTO dbo.Attendance(EmpID, [ArrDepWork], CreatedDate)
   SELECT
      EmpID,
      'Depart',
      CAST(
         CAST(CAST(@theDate AS DATE) AS VARCHAR(11)) + ' ' +
         CAST(DATEADD(MINUTE, CAST(RAND(CHECKSUM(NEWID())) * 60 AS INT), CAST('16:00' AS TIME)) AS VARCHAR(5))
      AS DATETIME)
   FROM (
      VALUES
         (101), (102), (103), (104), (105), (106), (107), (108), (109), (110), (111), (112)
   ) AS EmployeeList(EmpID);
END
 
-- Set the date for Tuesday
SET @theDate = DATEADD(DAY, 1, @theDate)
BEGIN
   -- Insert the Arrive times for all the employees
   INSERT INTO dbo.Attendance(EmpID, [ArrDepWork], CreatedDate)
   SELECT
      EmpID,
      'Arrive',
      CAST(
         CAST(CAST(@theDate AS DATE) AS VARCHAR(11)) + ' ' +
         CAST(DATEADD(MINUTE, CAST(RAND(CHECKSUM(NEWID())) * 60 AS INT), CAST('07:00' AS TIME)) AS VARCHAR(5))
      AS DATETIME)
   FROM (
      VALUES
         (101), (102), (103), (104), (105), (106), (107), (108), (109), (110), (111), (112)
   ) AS EmployeeList(EmpID);
 
   -- Add some movement to Employees during the day
   INSERT INTO dbo.Attendance(EmpID, [ArrDepWork], CreatedDate) VALUES
   (105, 'Depart', convert(datetime, convert(varchar, @theDate) + ' 10:45')),
   (105, 'Arrive', convert(datetime, convert(varchar, @theDate) + ' 11:45')),
   (108, 'Depart', convert(datetime, convert(varchar, @theDate) + ' 10:20')),
   (108, 'Arrive', convert(datetime, convert(varchar, @theDate) + ' 11:30')),
   (108, 'Depart', convert(datetime, convert(varchar, @theDate) + ' 13:30')),
   (108, 'Arrive', convert(datetime, convert(varchar, @theDate) + ' 14:10'))
 
   -- Insert the Depart times for all the employees
   INSERT INTO dbo.Attendance(EmpID, [ArrDepWork], CreatedDate)
   SELECT
      EmpID,
      'Depart',
      CAST(
         CAST(CAST(@theDate AS DATE) AS VARCHAR(11)) + ' ' +
         CAST(DATEADD(MINUTE, CAST(RAND(CHECKSUM(NEWID())) * 60 AS INT), CAST('16:00' AS TIME)) AS VARCHAR(5))
      AS DATETIME)
   FROM (
      VALUES
         (101), (102), (103), (104), (105), (106), (107), (108), (109), (110), (111), (112)
   ) AS EmployeeList(EmpID);
END
 
-- Set the date for Wednesday
SET @theDate = DATEADD(DAY, 1, @theDate)
BEGIN
   -- Insert the Arrive times for all the employees
   INSERT INTO dbo.Attendance(EmpID, [ArrDepWork], CreatedDate)
   SELECT
      EmpID,
      'Arrive',
      CAST(
         CAST(CAST(@theDate AS DATE) AS VARCHAR(11)) + ' ' +
         CAST(DATEADD(MINUTE, CAST(RAND(CHECKSUM(NEWID())) * 60 AS INT), CAST('07:00' AS TIME)) AS VARCHAR(5))
      AS DATETIME)
   FROM (
      VALUES
         (101), (102), (103), (104), (105), (106), (107), (108), (109), (110), (111), (112)
   ) AS EmployeeList(EmpID);
 
   -- Add some movement to Employees during the day
   INSERT INTO dbo.Attendance(EmpID, [ArrDepWork], CreatedDate) VALUES
   (111, 'Depart', convert(datetime, convert(varchar, @theDate) + ' 09:35')),
   (111, 'Arrive', convert(datetime, convert(varchar, @theDate) + ' 10:25')),
   (102, 'Depart', convert(datetime, convert(varchar, @theDate) + ' 09:30')),
   (102, 'Arrive', convert(datetime, convert(varchar, @theDate) + ' 10:00')),
   (102, 'Depart', convert(datetime, convert(varchar, @theDate) + ' 14:20')),
   (102, 'Arrive', convert(datetime, convert(varchar, @theDate) + ' 14:50'))
 
   -- Insert the Depart times for all the employees
   INSERT INTO dbo.Attendance(EmpID, [ArrDepWork], CreatedDate)
   SELECT
      EmpID,
      'Depart',
      CAST(
         CAST(CAST(@theDate AS DATE) AS VARCHAR(11)) + ' ' +
         CAST(DATEADD(MINUTE, CAST(RAND(CHECKSUM(NEWID())) * 60 AS INT), CAST('16:00' AS TIME)) AS VARCHAR(5))
      AS DATETIME)
   FROM (
      VALUES
         (101), (102), (103), (104), (105), (106), (107), (108), (109), (110), (111), (112)
   ) AS EmployeeList(EmpID);
END
 
-- Set the date for Thursday
SET @theDate = DATEADD(DAY, 1, @theDate)
BEGIN
   -- Insert the Arrive times for all the employees
   INSERT INTO dbo.Attendance(EmpID, [ArrDepWork], CreatedDate)
   SELECT
      EmpID,
      'Arrive',
      CAST(
         CAST(CAST(@theDate AS DATE) AS VARCHAR(11)) + ' ' +
         CAST(DATEADD(MINUTE, CAST(RAND(CHECKSUM(NEWID())) * 60 AS INT), CAST('07:00' AS TIME)) AS VARCHAR(5))
      AS DATETIME)
   FROM (
      VALUES
         (101), (102), (103), (104), (105), (106), (107), (108), (109), (110), (111), (112)
   ) AS EmployeeList(EmpID);
 
   -- Add some movement to Employees during the day
   INSERT INTO dbo.Attendance(EmpID, [ArrDepWork], CreatedDate) VALUES
   (106, 'Depart', convert(datetime, convert(varchar, @theDate) + ' 10:35')),
   (106, 'Arrive', convert(datetime, convert(varchar, @theDate) + ' 11:25')),
   (106, 'Depart', convert(datetime, convert(varchar, @theDate) + ' 12:00')),
   (106, 'Arrive', convert(datetime, convert(varchar, @theDate) + ' 13:00')),
   (107, 'Depart', convert(datetime, convert(varchar, @theDate) + ' 13:00')),
   (107, 'Arrive', convert(datetime, convert(varchar, @theDate) + ' 14:00'))
 
   -- Insert the Depart times for all the employees
   INSERT INTO dbo.Attendance(EmpID, [ArrDepWork], CreatedDate)
   SELECT
      EmpID,
      'Depart',
      CAST(
         CAST(CAST(@theDate AS DATE) AS VARCHAR(11)) + ' ' +
         CAST(DATEADD(MINUTE, CAST(RAND(CHECKSUM(NEWID())) * 60 AS INT), CAST('16:00' AS TIME)) AS VARCHAR(5))
      AS DATETIME)
   FROM (
      VALUES
         (101), (102), (103), (104), (105), (106), (107), (108), (109), (110), (111), (112)
   ) AS EmployeeList(EmpID);
END
 
-- Set the date for Friday
SET @theDate = DATEADD(DAY, 1, @theDate)
BEGIN
   -- Insert the Arrive times for all the employees
   INSERT INTO dbo.Attendance(EmpID, [ArrDepWork], CreatedDate)
   SELECT
      EmpID,
      'Arrive',
      CAST(
         CAST(CAST(@theDate AS DATE) AS VARCHAR(11)) + ' ' +
         CAST(DATEADD(MINUTE, CAST(RAND(CHECKSUM(NEWID())) * 60 AS INT), CAST('07:00' AS TIME)) AS VARCHAR(5))
      AS DATETIME)
   FROM (
      VALUES
         (101), (102), (103), (104), (105), (106), (107), (108), (109), (110), (111), (112)
   ) AS EmployeeList(EmpID);
 
   -- Add some movement to Employees during the day
   INSERT INTO dbo.Attendance(EmpID, [ArrDepWork], CreatedDate) VALUES
   (109, 'Depart', convert(datetime, convert(varchar, @theDate) + ' 08:35')),
   (109, 'Arrive', convert(datetime, convert(varchar, @theDate) + ' 09:25')),
   (109, 'Depart', convert(datetime, convert(varchar, @theDate) + ' 10:00')),
   (109, 'Arrive', convert(datetime, convert(varchar, @theDate) + ' 12:00')),
   (110, 'Depart', convert(datetime, convert(varchar, @theDate) + ' 11:00')),
   (110, 'Arrive', convert(datetime, convert(varchar, @theDate) + ' 12:20'))
 
   -- Insert the Depart times for all the employees
   INSERT INTO dbo.Attendance(EmpID, [ArrDepWork], CreatedDate)
   SELECT
      EmpID,
      'Depart',
      CAST(
         CAST(CAST(@theDate AS DATE) AS VARCHAR(11)) + ' ' +
         CAST(DATEADD(MINUTE, CAST(RAND(CHECKSUM(NEWID())) * 60 AS INT), CAST('16:00' AS TIME)) AS VARCHAR(5))
      AS DATETIME)
   FROM (
      VALUES
         (101), (102), (103), (104), (105), (106), (107), (108), (109), (110), (111), (112)
   ) AS EmployeeList(EmpID);
END
 
-- Check the Attendance table
SELECT * FROM dbo.Attendance
 
USE master;
GO

Step 7: Examine Scenarios Related to Employee Activities

Let's test a few scenarios using our dummy data.

List of All Attendances.

After you have used the above script to insert dummy data, you can perform a SELECT on the Employees table joined with the Attendance table to get a list of all the attendances, in this case, 150 records:

-- MSSQLTips.com
 
USE HRDatabase;
GO
 
SELECT e.id, e.EmployeeName, e.ContactNo, e.Email, a.ArrDepWork, a.CreatedDate
FROM dbo.Employees e
JOIN dbo.Attendance a on a.EmpID = e.id

Check a Specific Employee's Attendance.

You can also use an employee's ID to check a specific employee's attendance for the week:

-- MSSQLTips.com
 
SELECT e.id, e.EmployeeName, e.ContactNo, e.Email, a.ArrDepWork, a.CreatedDate
FROM dbo.Employees e
JOIN dbo.Attendance a on a.EmpID = e.id
WHERE e.id in (101)
ORDER BY a.id

Retrieve the Arrival and Departure Times of Each Employee on Specific Dates.

First, extract some data into a temp table by getting the Arrive and Depart times per employee per date:

-- MSSQLTips.com
-- Get the Arrive and Depart times per employee per date
 
IF OBJECT_ID('tempdb..#Tmp1')IS NOT NULL DROP TABLE #Tmp1;
BEGIN
   WITH PairedRecords AS (
      SELECT
         a1.EmpID,
         e.EmployeeName,
         CONVERT(DATE, a1.CreatedDate) AS AttendanceDate,
         a1.ArrDepWork,
         a1.CreatedDate
      FROM
         dbo.Attendance a1
      JOIN dbo.Employees e ON e.id = a1.EmpID
      WHERE 
         a1.ArrDepWork = 'Arrive'
   )
   SELECT EmpID, EmployeeName, AttendanceDate, CreatedDate [Arrive],
      (SELECT top 1 a2.CreatedDate [Depart]
      FROM dbo.Attendance a2 
      WHERE a2.CreatedDate > PairedRecords.CreatedDate 
      AND a2.EmpID = PairedRecords.EmpID
      AND CONVERT(DATE, a2.CreatedDate) = PairedRecords.AttendanceDate) [Depart]
      , 
      DATEDIFF(MINUTE, CreatedDate, (
         SELECT top 1 a2.CreatedDate [Depart]
         FROM dbo.Attendance a2 
         WHERE a2.CreatedDate > PairedRecords.CreatedDate 
         AND a2.EmpID = PairedRecords.EmpID
         AND CONVERT(DATE, a2.CreatedDate) = PairedRecords.AttendanceDate
      )) TotalTime
   INTO #Tmp1
   FROM PairedRecords 
   ORDER BY 1,4;
END
 
SELECT * FROM #Tmp1 
WHERE EmpID in (101)
ORDER BY EmpID, Arrive
Get the Arrive and Depart times per employee per date

In the above image, you can see that employee Bethany Joseph has two entries for Monday, 2023-10-02, because the employee arrived at 07:32 and departed at 10:35 and arrived again at 11:25 and departed at the end of the day at 16:02. The working time equal to 183 minutes plus 277 minutes for a total of 460 minutes for the day.

Retrieve the Arrive and Depart Times for a Day an Employee Went to Lunch

-- MSSQLTips.com
 
WITH CombinedRecords AS (
    SELECT
        EmpID,
        EmployeeName,
        AttendanceDate,
        MIN(Arrive) AS FirstArrive,
        MAX(Depart) AS LastDepart
    FROM
        #Tmp1
WHERE EmpID in (101)
    GROUP BY
        EmpID,
        EmployeeName,
        AttendanceDate
)
SELECT
    EmpID,
    EmployeeName,
    AttendanceDate,
    FirstArrive,
    LastDepart
FROM
    CombinedRecords;
Get the arrive and depart time for a day for an employee that went out to lunch

In the above image, you can see the employee's consolidated arrival and departure times are only a single consolidated record for that Monday.

Calculate the Total Daily Hours Worked By Each Employee.

-- MSSQLTips.com
SELECT EmpID, EmployeeName, AttendanceDate
      , CONVERT(VARCHAR(5), DATEADD(MINUTE, SUM(TotalTime), 0), 108) AS TotalTime
FROM #Tmp1
WHERE EmpID in (101)
GROUP BY EmpID, EmployeeName, AttendanceDate
Calculating the total daily hours worked by each employee

In the above image, you can see the totals by date for the employee.

Create a List of Employees Present at the Office at a Specific Date and Time.

Let's say it is 11:00 AM on a Monday, and you want to see who is currently at the office.

We need to change the script generating the dummy data to only simulate the situation:

-- MSSQLTips.com
-- Insert some dummy data for Attendance for Monday
 
USE HRDatabase;
GO
 
DELETE FROM HRDatabase.dbo.Attendance;
 
-- Set the initial date for a Monday
DECLARE @theDate date = '2023-10-02'
 
-- Insert the Arrive times for all the employees
BEGIN
   INSERT INTO dbo.Attendance(EmpID, [ArrDepWork], CreatedDate)
   SELECT
      EmpID,
      'Arrive',
      CAST(
         CAST(CAST(@theDate AS DATE) AS VARCHAR(11)) + ' ' +
         CAST(DATEADD(MINUTE, CAST(RAND(CHECKSUM(NEWID())) * 60 AS INT), CAST('07:00' AS TIME)) AS VARCHAR(5))
      AS DATETIME)
   FROM (
      VALUES
         (101), (102), (103), (104), (105), (106), (107), (108), (109), (110), (111), (112)
   ) AS EmployeeList(EmpID);
 
   -- Add some movement to Employees during the day
   INSERT INTO dbo.Attendance(EmpID, [ArrDepWork], CreatedDate) VALUES
   (101, 'Depart', convert(datetime, convert(varchar, @theDate) + ' 10:35')),
   (103, 'Depart', convert(datetime, convert(varchar, @theDate) + ' 10:00')),
   (103, 'Arrive', convert(datetime, convert(varchar, @theDate) + ' 10:50'))
 
END

The above script first removes all the data in the dbo.Attendance table, then it generates data for all employees to arrive at work. However, the data shows that two employees departed the office, and only one returned.

Now, we can check who is at the office at 11:00 AM when the staff meeting is scheduled to start:

-- MSSQLTips.com
 
IF OBJECT_ID('tempdb..#Tmp1') IS NOT NULL DROP TABLE #Tmp1;
BEGIN
   WITH PairedRecords AS (
      SELECT
         a1.EmpID,
         e.EmployeeName,
         CONVERT(DATE, a1.CreatedDate) AS AttendanceDate,
         a1.ArrDepWork,
         a1.CreatedDate
      FROM
         dbo.Attendance a1
      JOIN dbo.Employees e ON e.id = a1.EmpID
      WHERE 
         a1.ArrDepWork = 'Arrive'
   )
   SELECT EmpID, EmployeeName, AttendanceDate, CreatedDate [Arrive],
      (SELECT top 1 a2.CreatedDate [Depart]
      FROM dbo.Attendance a2 
      WHERE a2.CreatedDate > PairedRecords.CreatedDate 
      AND a2.EmpID = PairedRecords.EmpID
      AND CONVERT(DATE, a2.CreatedDate) = PairedRecords.AttendanceDate) [Depart]
      , 
      DATEDIFF(MINUTE, CreatedDate, (
         SELECT top 1 a2.CreatedDate [Depart]
         FROM dbo.Attendance a2 
         WHERE a2.CreatedDate > PairedRecords.CreatedDate 
         AND a2.EmpID = PairedRecords.EmpID
         AND CONVERT(DATE, a2.CreatedDate) = PairedRecords.AttendanceDate
      )) TotalTime
   INTO #Tmp1
   FROM PairedRecords 
   ORDER BY 1,4;
END
SELECT * FROM dbo.Attendance
SELECT * FROM #Tmp1 ORDER BY EmpID

Check who was at the office at a specific date and time:

-- MSSQLTips.com
 
WITH EmployeeStatus AS (
    SELECT DISTINCT
        EmpID,
        EmployeeName,
        CASE
            WHEN (
                (Arrive <= '2023-10-02 11:00:00.000' AND (Depart >= '2023-10-02 11:00:00.000' OR Depart IS NULL))
                OR (Arrive <= '2023-10-02 11:00:00.000' AND Depart IS NULL)
            ) THEN 'Yes, at office'
            ELSE 'No, not at office'
        END AS OfficeStatus
    FROM
        #Tmp1
    WHERE
        AttendanceDate = '2023-10-02'
)
SELECT
    EmpID,
    EmployeeName,
    MAX(OfficeStatus) AS OfficeStatus
FROM
    EmployeeStatus
GROUP BY
    EmpID,
    EmployeeName
ORDER BY EmpID;
Next Steps


sql server categories

sql server webinars

subscribe to mssqltips

sql server tutorials

sql server white papers

next tip



About the author
MSSQLTips author Jan Potgieter Jan Potgieter has more than two decades of expertise in the database industry as a Certified Microsoft SQL Server Administrator and Database Developer.

This author pledges the content of this article is based on professional experience and not AI generated.

View all my tips


Article Last Updated: 2024-01-22

Comments For This Article

















get free sql tips
agree to terms