SQL - Updating records based on most recent date - sql

I am having difficulty updating records within a database based on the most recent date and am looking for some guidance. By the way, I am new to SQL.
As background, I have a windows forms application with SQL Express and am using ADO.NET to interact with the database. The application is designed to enable the user to track employee attendance on various courses that must be attended on a periodic basis (e.g. every 6 months, every year etc.). For example, they can pull back data to see the last time employees attended a given course and also update attendance dates if an employee has recently completed a course.
I have three data tables:
EmployeeDetailsTable - simple list of employees names, email address etc., each with unique ID
CourseDetailsTable - simple list of courses, each with unique ID (e.g. 1, 2, 3 etc.)
AttendanceRecordsTable - has 3 columns { EmployeeID, CourseID, AttendanceDate, Comments }
For any given course, an employee will have an attendance history i.e. if the course needs to be attended each year then they will have one record for as many years as they have been at the company.
What I want to be able to do is to update the 'Comments' field for a given employee and given course based on the most recent attendance date. What is the 'correct' SQL syntax for this?
I have tried many things (like below) but cannot get it to work:
UPDATE AttendanceRecordsTable
SET Comments = #Comments
WHERE AttendanceRecordsTable.EmployeeID = (SELECT EmployeeDetailsTable.EmployeeID FROM EmployeeDetailsTable WHERE (EmployeeDetailsTable.LastName =#ParameterLastName AND EmployeeDetailsTable.FirstName =#ParameterFirstName)
AND AttendanceRecordsTable.CourseID = (SELECT CourseDetailsTable.CourseID FROM CourseDetailsTable WHERE CourseDetailsTable.CourseName =#CourseName))
GROUP BY MAX(AttendanceRecordsTable.LastDate)
After much googling, I discovered that MAX is an aggregate function and so I need to use GROUP BY. I have also tried using the HAVING keyword but without success.
Can anybody point me in the right direction? What is the 'conventional' syntax to update a database record based on the most recent date?

So you want to update the AttendantsRecordsTable, and set the comment to the comment in the most recent CourseDetailsTable for each employee?
UPDATE
dbo.AttendanceRecordsTable
SET
Comments = #Comments
FROM
CourseDetailsTable cd
INNER JOIN
Employee e ON e.EmployeeID = AttendanceRecordTable.EmployeeID
WHERE
e.LastName = #LastName
AND e.FirstName = #FirstName
AND cd.CourseName = #CourseName
AND AttendanceRecordsTable.CourseID = cd.CourseID
AND AttendanceRecordsTable.LastDate =
(SELECT MAX(LastDate)
FROM AttendanceRecordsTable a
WHERE a.EmployeeID = e.EmployeeID
AND a.CourseID = cd.CourseID)
I think something like that should work.
You basically need to do a join between the AttendanceRecordTable, which you want to update, and the Employee and CourseDetailsTable tables. For these two, you have defined certain parameters to select a single row each, and then you need to make sure to update only that last AttendanceRecordTable entry which you do by making sure it's the MAX(LastDate) of the table.
The subselect here:
(SELECT MAX(LastDate)
FROM AttendanceRecordsTable a
WHERE a.EmployeeID = e.EmployeeID AND a.CourseID = cd.CourseID)
will select the MAX (last) of the LastDate entries in AttendanceRecordsTable, based on selection of a given employee (e.EmployeeID) and a given course (cd.CourseID).
Pair that with the selects to select the single employee by first name and last name (that of course only works if you never have two John Miller in your employee table!). You also select the course by means of the course name, so that too must be unique - otherwise you'll get multiple hits in the course table.
Marc

Assuming that you primary key on the AttendanceRecordsTable is id:
UPDATE AttendanceRecordsTable SET Comments = #Comments
WHERE AttendanceRecordsTable.id = (
SELECT AttendanceRecordsTable.id
FROM EmployeeDetailsTable
JOIN AttendanceRecordsTable ON AttendanceRecordsTable.EmployeeID = EmployeeDetailsTable.EmployeeID·
JOIN CourseDetailsTable ON AttendanceRecordsTable.CourseID = CourseDetailsTable.CourseID
WHERE
EmployeeDetailsTable.LastName =#ParameterLastName AND EmployeeDetailsTable.FirstName =#ParameterFirstName AND
CourseDetailsTable.CourseName =#CourseName
ORDER BY AttendanceRecordsTable.LastDate DESC LIMIT 1)
Basically, that sub select will first join the attendence, employee and coursedetail tables, extract those rows where the employee's and course details' name match those given by your parameters and limit the output in reverted order to one line. You might want to test that sub-select statement first.
Edit: I just read your posting again, you don't have a single primary key column on AttendanceRecordsTable. Bummer.

Related

Microsoft SQL server select statements on multiple tables?

so I've been struggling with some of the select statements on multiple tables:
Employee table
Employee_ID
First_Name
Last_Name
Assignment table
Assignment_ID
Employee_ID
Host_Country_arrival_Date
Host_Country_departure_Date
Scheduled_End_Date
I'm being asked to display query to display employee full name, number of days between the host country arrival date and host country departure date, number of days between today's date and the assignment scheduled end date and the results sorted according to host country arrival date with the oldest date on top.
also, I'm not familiar with the sort function in SQL server..
Here's my query and I've been getting syntax errors:
SELECT
First_Name
Last_Name
FROM Employee
SELECT
Host_Country_Arrival_Date
Host_Country_Departure_Date
FROM Assignment;
So, Basically what your code is doing is 2 different queries. The first getting all the employees names, and the second one getting the dates of the assignments.
What you'll want to do here is take advantage of the relationship between the tables using a JOIN. That is basically saying "Give me all employees and all of HIS/HERS assignments". So, for each assignment that the employee has, it will bring a row in the result with his name and the assignment info.
To get the difference between days you use DATEDIFF passing 3 parameters, the timespan in which to calculate the difference, the first and the second date. It will then Subtract the first one from the second one and give you the result in the selected timespan.
And finnaly the sorting: Just add 'ORDER BY' followed by each column that you want to use for ordering and then specify if you want it ascending (ASC) or descending (DESC).
You can check how I would answer the if that question was proposed to me in a coding challenge.
SELECT
CONCAT(E.First_Name,' ', E.Last_Name) FullName,
DATEDIFF(DAY,Scheduled_End_Date,getdate()) DaysTillScheduledDate,
DATEDIFF(DAY,Host_Country_Arrival_Date,Host_Country_Departure_Date) DaysTillScheduledDate
FROM Employee As E --Is nice to add aliases
Inner Join
Assignment As A
on E.Employee_ID = A.Employee_ID -- Read a little bit about joins, there are a lot of material availabel an its going to be really necessary moving forward with SQL
order by Host_Country_Arrival_Date DESC -- Just put the field that you want to order by here, desc indicates that it should be descending
You should use a JOIN to link the tables together on Employee_ID:
SELECT
First_Name,
Last_Name,
Host_Country_Arrival_Date,
Host_Country_Departure_Date
FROM Employee
JOIN Assignment ON Assignment.Employee_ID = Employee.Employee_ID;
What this is saying basically is that for each employee, go out to the assignments table and add the assignments for that employee. If there are multiple assignments, the employee columns will be repeated on each row with the assignment columns for the assignment.
You need to look for the join and group by. Please find this link for reference tutorial
For now you may try this...
SELECT
CONCAT(Emp.First_Name,' ', Emp.Last_Name) FullName,
DATEDIFF(DAY,Scheduled_End_Date,getdate()) DaysTillScheduledDate,
DATEDIFF(DAY,Host_Country_Arrival_Date,Host_Country_Departure_Date) DaysTillScheduledDate
FROM Employee As Emp Inner Join Assignment As Assign on Emp.Employee_ID = Assign.Employee_ID
order by Host_Country_Arrival_Date DESC

I am trying to write a SQL Server query using joins and having some difficulty

Tables are explained in detail as below:
I have 3 tables:
Table A:
It serves as the master table for information about the employees.
EmployeeId(Primary key)
Employee Designation
EmployeeName (More columns of employee data which is not relevant to this particular query)
Table B:
It serves as table where all employees who are accounted for are stored. For ex an employee who has reported sick or is on leave or has pregnancy leave, etc. Bottom line an employee which is not available
EmployeeID (primary key) (also referencing master table A as foreign key)
AccountedFor
AccountedFordurationFrom (datetime)
AccountedForDurationTo (datetime)
Table C:
It serves as a table where excused data of employees are present. For ex we have our organization's time table spread as events, 1st event is morning time conference, then 2nd is silence working time, 3rd is brainstorming sessions etc. Now if an employee is excused for a particular event, it is entered here.
EmployeeID
EventCode
Excuse_DurationFrom
Excuse Duration To
Any specific details
Here EmployeeID and ExcusedForEventCode are both composite primary keys as it is possible to have same employeeId for multiple excuses,but the combination is always unique.
We have built some custom attendance management system and would require the following details:
We need to find all those employees who are neither accounted for nor excused for a specific event(this will be provided through front end) for a time duration selected through the front end.
The result of the above query will subsequently be used to compare with a biometric attendance machine logs which gives
EmployeeId|LogDate(datetime)|EventCodes as a separate table input to our database (Master table A employeeId references this EmployeeId as foreign key)
It will be compared to find out true absentees for a particular event. ie All those employees who are neither accounted for, nor excuses for any particular event and who does not figure out in the biometric scan machine logs are absented for those time duration selected. We need the output of absentee like this EmployeeId|Employee Designation|Employee Name|EventName (have a separate table linking with EventCode)|Date&time (this would be per day per event report of employee who are absent from the selected time duration).
We have tried queries like:
select
employeemastertable.employeeid,
employeemastertable.Designation,
employeemastertable.Name,
EventCodes.EventCodeName as Eventexcusedfrom
from
employeemastertable
inner join
employeeexcusedforevents on employeemastertable.employeeid = employeeexcusedforevents.employeeid
inner join
EventCodes on employeeexcusedforEvents.ExcusedForEventCode = EventCodes.Eventcode
left join
employeeaccountedFor on employeemastertable.employeeid = employeeaccountedFor.employeeid
where
employeeexcusedforevents.ExcusedForEventCode != 1 (Morning conference)
and employeeaccountedFor.employeeid is null;
Names have been changed
I do understand this will give those employees who does not figure out in event Morning conference but even if I do left join instead of inner join between employeemastertable and employeeexcusedForevents and put employeeexcusedforevents.excusedforeventcode is null and employeeexcusedforevents.employeeid is null, I do get all those employees not present in the other two table, but the criteria of event is not satisfied. That means what if the employee is excused for the 2nd event as well in the organization. How would I cater for that in the above code? (PS this is only the 1st part of the equation I understand that, after this I need help for the other part also, where time duration and comparing with logs is concerned)?
I assume there will be just one row for the EventCode=1 in table EventCodes. Below I cross join the wanted event to the employee master table and then exclude any employees that are excused or accounted for.
-- employees neither accounted for nor excused for a specific event
SELECT
em.employeeid
, em.Designation
, em.Name
, ec.EventCodeName AS Eventexcusedfrom
FROM employeemastertable em
CROSS JOIN (
SELECT Eventcode, EventCodeName
FROM EventCodes
WHERE Eventcode = 1
) ec
WHERE NOT EXISTS (
SELECT NULL
FROM employeeexcusedforevents ee
WHERE em.employeeid = ee.employeeid
AND ec.Eventcode = ee.ExcusedForEventCode
)
AND NOT EXISTS (
SELECT NULL
FROM employeeaccountedFor eaf
WHERE em.employeeid = eaf.employeeid
)
;

Extract info from one table based on data from antoher

I am kind of new to SQL and I made a couple of tables to practice. The columns may have some unrelated categories but I don't know what else write...
Anyway, basically what i want to do is get info from two tables based on the first and last name from one table.
Here are my tables:
Order
Host
I want create a query to pull the ticket number, height, order, subtotal and total by first and last name. The only orders I want to pull are from John Smith And Sam Ting. So in the end, I want my extraction to have the following columns:
Ticket Number
First Name
Last Name
Height
Order
Subtotal
Total
Any help or direction would be awesome!
With the assumption the tables both have unique Ticket_Numbers and that will provide a one-to-one mapping between then.
SELECT
Order.Ticket_Number,
First_Name,
Last_Name,
Height,
Order,
Subtotal,
Total
FROM Order
JOIN Host on Host.Ticket_Number = Order.Ticket_Number
WHERE
(First_Name = 'John' AND Last_Name = 'Smith')
OR (First_Name = 'Sam' AND Last_Name = 'Ting')
You need to "call" the table name first, and then the column. After that you need to use the "join" for the 2 tables. And finally you need the "where". I didn't look for the details so you need to check the "names".
SELECT Order.Ticket_Number, Order.First_Name, Order.Last_Name, Order.Height, Order.Order, Cost.Subtotal, Cost.Total
FROM Order
INNER JOIN Cost
where First_Name="Jhon" and Last_Name="blablabla"
or
First_Name="SecondGuy" and Last_Name="blablabla"

Access Query - Update Based on Latest Date

I tried to locate an answer for this on other threads and was unable to find one. Please forgive me if it is covered in another thread.
I am relatively new to T-SQL, so please excuse if this is something I should already know.
I have a simple Access database with two tables: Contacts and Orders.
Contacts columns are ID (PK), FirstName, LastName, Address, a Status field (identifying if the customer is a "PC", which is who matters in this case), and also has a field for "Delayed." This field is to be "true" for those customers who haven't ordered in the last two months (and "false" for those who have).
The Orders table has the order numbers, along with Customer ID (PK related to Contacts), FirstName, LastName, Date.
I'm trying to write a query to update the "Delayed" field in the Contacts table, based on the latest order date from the Orders table. In this case, update that field to "false" for each PC customer if their latest order date falls within the last 60 days. It seemed pretty striaghtforward, but I'm obviously missing something here. I'm using a SELECT subquery to define the conditions. The query seems to be ignoring my conditions and wants to update ALL rows in the database. Any thoughts?
UPDATE Contacts SET Contacts.Delayed = False
WHERE EXISTS (SELECT Contacts.FirstName, Contacts.LastName, Contacts.Status, Max(Orders.Date)
FROM Contacts INNER JOIN Orders ON (Contacts.ID = Orders.ContactID)
GROUP BY Contacts.FirstName, Contacts.LastName, Contacts.Status
HAVING Max(Orders.Date) > Date()-60);
Any help would be greatly appreciated.
Because you are using exists, if the query returns a result, all contacts will have their delayed attribute set.
What you want to do is filter the results you are going to update, using a join. You will need a left join, because you want to find orders that haven't taken place within the last two months.
UPDATE tblContacts
SET Contacts.Delayed = False
FROM Contacts tblContacts
LEFT JOIN ( SELECT Contacts.FirstName ,
Contacts.LastName ,
Contacts.Status ,
MAX(Orders.Date)
FROM Contacts
INNER JOIN Orders ON ( Contacts.ID = Orders.ContactID )
GROUP BY Contacts.FirstName ,
Contacts.LastName ,
Contacts.Status
HAVING MAX(Orders.Date) > Date() - 60
) tbl ON tbl.Contacts.Id = tblContacts.Id
WHERE tblContacts.Id IS NULL

Needing 2 different ID's from the same ID Table

I am pulling reports for my company and am needing to pull a specific report that I am having trouble with. We are using SQL Server 2012 and I am pulling the SQL reports.
What I need is to pull a simple report:
Group Name, List of Members in the group; Supervisor of the group.
However, the problem is that the supervisor as well as the members and the group name all come from one table in order to get the relevant information. Currently here is my SQL code below:
Use DATABASE
go
-- This is the select portion deciding the columns needed.
select
C.group_name
,C2.first_name
,C2.last_name
-- These are the tables that the query is pulling from.
FROM db..groups AS G
LEFT OUTER JOIN db..contact AS C
ON G.group_id=C.contact_id
INNER JOIN db..contact AS C2
ON G.member=C2.contact_id
go
This pulls the first portion:
The group name, then the first name of a member in that group, and then the last name of a member in that group.
However, I am having trouble getting the supervisor portion. This portion uses the table db.contact under the column supervisor_id as a foreign key. The supervisor_id uses the same unique id as the normal contact_id, but in the same table. Some contact_ids have supervisor_id's that are other contact_id's from the same table, hence the foreign key.
How can I make it so I can get the contact_id that is equal to the supervisor_id of the contact_id that is equal to the group_id?
Taking a quick stab at this while we wait for details
You know you need groups and I'm assuming you don't care about Groups that have no members. Thus Groups INNER JOINed to Contact. This generates your direct group membership. To get the supervisor, you then need to factor in the Supervisor on the specific Contact row.
You might not have a boss, or your boss might be yourself. It's always interesting to see how various HR systems record this. In my example, I'm assuming the head reports to no one instead of themselves.
SELECT
G.group_name
, C.first_name
, C.last_name
-- this may produce nulls depending on outer vs inner join below
, CS.first_name AS supervisor_first_name
, CS.last_name AS supervisor_last_name
FROM
dbo.Groups AS G
INNER JOIN
dbo.Contact AS C
ON C.contact_id = G.member
LEFT OUTER JOIN
dbo.Contact AS CS
ON CS.contact_id = C.supervisor_id;
Depending on how exactly you wanted that data reported, there are various tricks we could use to report that data. In particular, GROUPING SETS might come in handy.
SQLFiddle