How to select data from 2 tables by date - mysql

I have two tables:
In this table I have all employees:
Employe (id_employe, name, tel)
In this table I have all employees who are present:
Present (id_present, date, #id_empoye)
Now, I want to select all employees who are absent (all of employees who are not in the table present) with the date of absence.
I'm sorry for by bad English, and I want a help please!
Here's my sql query:
select id_employe, date from employe, absence where id_employe not in
(select id_personnel from absence group by date) group by id_employe
order by date asc;

What you first want to do is use a subquery to get all of your employees with a record for every day that was a work day. I am assuming that if any one person showed up for work that day, it was a work day. So, I select every distinct work date and join it onto every employee. I say ON 1=1 because that condition is always true and will give me a record for every employee and the work day.
Then, I take that temp table and I join on the Present table. If an employee was present on a work day, he will have a record joined on from the Present table, so I can look for only records where there was no join, i.e. p.id_present IS NULL.
WITH WorkDateTable AS (
SELECT e.id_employe, p.work_date
FROM Employe e LEFT JOIN (SELECT DISTINCT work_date FROM Present) p ON 1=1)
SELECT wd.id_employe, wd.work_date AS AbsenceDate
FROM WorkDateTable wd
LEFT JOIN Present p ON p.work_date = wd.work_date AND p.id_employe = wd.id_employe
WHERE p.id_present IS NULL
ORDER BY AbsenceDate, wd.id_employe
Note that date is a reserved word and you cannot use that for a field, that is why I changed it to work_date. Your final result is a list of work dates and the ID of who was absent on that day.
If you want to know who was absent on a particular date or range of dates, you just need to add a clause to the WHERE statement at the end.
EDIT:
MySQL does not support the use of the WITH clause. Therefore, move the statement in the WITH to inside the FROM statement like this:
SELECT wd.id_employe, wd.work_date AS AbsenceDate
FROM (SELECT e.id_employe, p.work_date
FROM Employe e LEFT JOIN (SELECT DISTINCT work_date FROM Present) p ON 1=1) wd
LEFT JOIN Present p ON p.work_date = wd.work_date AND p.id_employe = wd.id_employe
WHERE p.id_present IS NULL
ORDER BY AbsenceDate, wd.id_employe
This query should work for either MySQL or Oracle searches.

Assuming you only want the employees who are not present on a particular day, you can use the following query:
SELECT e.*
FROM employee e
WHERE NOT EXISTS (
SELECT id_present FROM present WHERE id_employee = e.id_employee AND date = <date>
);
This would give you employees who are absent on <date> date. As we are querying for one date only, no sorting is needed.

Related

SQL query for all grouped records in joined table

Let's consider a toy example. There is a table employees and table tasks, where each task is assigned to one employee. One employee can have multiple tasks.
I want to query employees filtering them by some columns in their tasks. I group the employees to display each one only once. The query would be something like this:
SELECT *
FROM employees emp JOIN tasks tsk on emp.id = tsk.assigned_emp
WHERE tsk.deadline = today
GROUP BY employees
This works fine, but now let's say that I'd like to select the employees that don't have any tasks which deadline is due today. My first try was:
tsk.deadline != today
but then is finds employees with at least one task that is not due today. How to write a query to include all possible tasks for a given employee?
There are several different solutions for this antijoin task. One of them is using NOT EXISTS
SELECT *
FROM employee e
WHERE NOT EXISTS(
SELECT 1
FROM tasks t
WHERE e.id = t.assigned_emp and t.deadline = today
)
You want the LEFT JOIN ... IS NULL pattern for this.
SELECT *
FROM employees emp
LEFT JOIN tasks tsk ON emp.id = tsk.assigned_emp
AND tsk.deadline = today
WHERE tsk.deadline IS NULL
GROUP BY employees
JOIN removes rows from its first table that don't match your ON condition. By contrast, LEFT JOIN leaves them in place putting NULL values into the result columns from the second table.

How to get distinct count of rows according to dates

Actually, i did counted distinct empid rows according to dates. But the problem is i get only one empid record of that specific dates.Please let me know how to get all empid records. Here is my sql query.
$sql = "
SELECT COUNT(DISTINCT subcount.empid) AS CountOf
, subcount.name
, subcount.date
, subcount.empid
, calendar.cdate
FROM subcount
, calendar
WHERE subcount.date = calendar.cdate
GROUP
BY subcount.date
";
Here is sql database.
For example, When you look at 2020-11-10 there are two empid with 10 and 7.
When i tried to get both records i get only empid 10 record or 7 record, though i need both record counts:
Here is the output:
Please help me on this.
I think what you are asking is to get list of employees with count of their submissions on a given date, this could show do it:
SELECT cnt.empid AS EmpId
, sc.Name
, cnt.`date` AS Timestamp
, cnt.CountOf AS SubmissionCount
FROM subcount AS sc
INNER JOIN
(
SELECT subcount.empid
subcount.`date`,
count(*) AS CountOf
FROM subcount
INNER JOIN calendar
ON subcount.`date` = calendar.cdate
GROUP BY
subcount.`date`, subcount.empid
) AS cnt
ON sc.empid == cnt.empid
It uses nested SELECT with GROUP BY to calculate count per employee (empid) and date (not only employee). Outer SELECT join nested SELECT to get subcount.Name piece of data which isn't retrieved in nested SELECT so it needs to be retrieved using outer SELECT.
GROUP BY ___ means result rows per ___. If you group by employee ID, you get one row per employee ID. If you want one row per employee ID and date, group by employee ID and date.
SELECT any_value(s.name), s.`date`, s.empid, c.cdate, count(*)
FROM subcount s
JOIN calendar c on c.cdate = s.`date`
GROUP BY s.`date`, s.empid
ORDER BY s.`date`, s.empid;
I expect a calendar table to have one row per date, so there is exactly one cdate for a result row. The name, however, can be different from row to row, so we must tell the DBMS, which to pick. With ANY_VALUE I tell it that I don't care which.

Use SELECT through three table

I tried to write a query, but unfortunately I didn't succeed.
I want to know how many packages delivered over a given period by a person.
So I want to know how many packages were delivered by John (user_id = 1) between 01-02-18 and 28-02-18. John drives another car (another plate_id) every day.
(orders_drivers.user_id, plates.plate_name, orders.delivery_date, orders.package_amount)
I have 3 table:
orders with plate_id delivery_date package_amount
plates with plate_id plate_name
orders_drivers with plate_id plate_date user_id
I tried some solutions but didn't get the expected result. Thanks!
Try using JOINS as shown below:
SELECT SUM(o.package_amount)
FROM orders o INNER JOIN orders_drivers od
ON o.plate_id=od.plate_id
WHERE od.user_id=<the_user_id>;
See MySQL Join Made Easy for insight.
You can also use a subquery:
SELECT SUM(o.package_amount)
FROM orders o
WHERE EXISTS (SELECT 1
FROM orders_drivers od
WHERE user_id=<user_id> AND o.plate_id=od.plate_id);
SELECT sum(orders.package_amount) AS amount
FROM orders
LEFT JOIN plates ON orders.plate_id = orders_drivers.plate_id
LEFT JOIN orders_driver ON orders.plate_id = orders_drivers.plate_id
WHERE orders.delivery_date > date1 AND orders.delivery_date < date2 AND orders_driver.user_id = userid
GROUP BY orders_drivers.user_id
But seriously, you need to ask questions that makes more sense.
sum is a function to add all values that has been grouped by GROUP BY.
LEFT JOIN connects all tables by id = id. Any other join can do this in this case, as all ids are unique (at least I hope).
WHERE, where you give the dates and user.
And GROUP BY userid, so if there are more records of the same id, they are returned as one (and summed by their pack amount.)
With the AS, your result is returned under the name 'amount',
If you want the total of packageamount by user in a period, you can use this query:
UPDATE: add a where clause on user_id, to retrieve John related data
SELECT od.user_id
, p.plate_name
, SUM(o.package_amount) AS TotalPackageAmount
FROM orders_drivers od
JOIN plates p
ON o.plate_id = od.plate_id
JOIN orders o
ON o.plate_id = od.plate_id
WHERE o.delivery_date BETWEEN convert(datetime,01/02/2018,103) AND convert(datetime,28/02/2018,103)
AND od.user_id = 1
GROUP BY od.user_id
, p.plate_name
It groups rows on user_id and plate_name, filter a period of delivery_date(s) and then calculate the sum of packageamount for the group

How do I query a table to give the number of instances of an ID using SQL?

I have two tables Doctor and Appointment. I need to produce a list of doctor IDs with the number of appointments made for each doctor with one or more appointments.
So far I have but I can't figure out how to get it to do what I want :
SELECT
doctor.doctor_id,
appointment.doctor_id
FROM doctor,
appointment
WHERE doctor.doctor_id = appointment.doctor_id;
SELECT
COUNT(DISTINCT doctor_id) AS NumberOfAppointments
FROM appointment
where doctor_id="50";
Any help would be appreciated, thanks.
JOIN TWO TABLES AND USE GROUP BY
SELECT doctor.doctor_id,
appointment.doctor_id,
COUNT(doctor_id) AS NumberOfAppointments
FROM doctor
LEFT JOIN appointment ON doctor.doctor_id = appointment.doctor_id GROUP BY doctor_id;
To get a list of doctor_id value that appear at least one time in the appointments table, with a count of rows with that doctor_id that appear in the appointments table, you can use a query like this:
SELECT a.doctor_id AS doctor_id
, SUM(1) AS count_appointments
FROM appointments a
GROUP BY a.doctor_id
The key to getting the result is the addition of the GROUP BY doctor_id clause, which effectively collapses all of the rows for a given value of doctor_id into a single row, so we get back a distinct list of doctor_id.
The aggregate function SUM(1) basically returns a literal value of 1 for each row, and then adds up all the those values from each row with the same doctor_id, effectively giving us a count of the number of rows with that doctor_id.
Note: you could also use a COUNT(1) or COUNT(*) in place of SUM(1), those will return an equivalent result. You could use a COUNT(expr), where expr is any expression that evaluates to a non-NULL value for every row to be included in the "count".
To also return doctor_id that have zero appointments, we'd use the doctors table as the driving table for an outer join operation (assuming doctor_id is unique in the doctors table.)
Something like this:
SELECT d.doctor_id AS doctor_id
, COUNT(a.doctor_id) AS count_appointments
FROM doctors d
LEFT
JOIN appointments a
ON a.doctor_id = d.doctor_id
GROUP BY d.doctor_id
Again, the GROUP BY collapses all of the rows that have the same value for doctor_id into a single row, and the COUNT() aggregate operates on the group. The expression we use in the COUNT() function needs to be an expression that is not-NULL for each matching row in the appointments table, and is NULL for rows from doctors that didn't have a matching row.
SELECT
doctor.doctor_id,
count(appointment.doctor_id)
FROM doctor,
appointment
WHERE doctor.doctor_id = appointment.doctor_id group by doctor.doctor_id;
You can use group by clause...

join on sub query returns fails

Trying to join a table "fab_qouta.qoutatype" to at value inside a sub query "fab_status_members.statustype" but it returns nothing.
If I join the 2 tables directly in a query the result is correct.
Like this:
select statustype, takst
from
fab_status_members AS sm
join fab_quota as fq
ON fq.quotatype = sm.statustype
So I must be doing something wrong, here the sub query code, any help appreciated
select
ju.id,
name,
statustype,
takst
from jos_users AS ju
join
( SELECT sm.Members AS MemberId, MaxDate , st.statustype
FROM fab_status_type AS st
JOIN fab_status_members AS sm
ON (st.id = sm.statustype) -- tabels are joined
JOIN
( SELECT members, MAX(pr_dato) AS MaxDate -- choose members and Maxdate from
FROM fab_status_members
WHERE pr_dato <= '2011-07-01'
GROUP BY members
)
AS sq
ON (sm.members = sq.members AND sm.pr_dato = sq.MaxDate)
) as TT
ON ju.id = TT.Memberid
join fab_quota as fq
ON fq.quotatype = TT.statustype
GROUP BY id
Guess the problem is in the line: join fab_quota as fq ON fq.quotatype = TT.statustype
But I can't seem to look through it :-(
Best regards
Thomas
It looks like you are joining down to the lowest combination of per member with their respective maximum pr_dato value for given date. I would pull THIS to the FIRST query position instead of being buried, then re-join it to the rest...
select STRAIGHT_JOIN
ju.id,
ju.name,
fst.statustype,
takst
from
( SELECT
members,
MAX(pr_dato) AS MaxDate
FROM
fab_status_members
WHERE
pr_dato <= '2011-07-01'
GROUP BY
members ) MaxDatePerMember
JOIN jos_users ju
on MaxDatePerMember.members = ju.ID
JOIN fab_status_members fsm
on MaxDatePerMember.members = fsm.members
AND MaxDatePerMember.MaxDate = fsm.pr_dato
JOIN fab_status_type fst
on fsm.statustype = fst.id
JOIN fab_quota as fq
on fst.statusType = fq.quotaType
I THINK I have all of what you want, and let me reiterate in simple words what I think you want. Each member can have multiple status entries (via Fab_Status_Members). You are looking for all members and what their MOST RECENT Status is as of a particular date. This is the first query.
From that, whatever users qualify, I'm joining to the user table to get their name info (first join).
Now, back to the complex part. From the first query that determined the most recent date status activity, re-join back to that same table (fab_status_members) and get the actual status code SPECIFIC to the last status date for that member (second join).
From the result of getting the correct STATUS per Member on the max date, you need to get the TYPE of status that code represented (third join to fab_status_type).
And finally, from knowing the fab_status_type, what is its quota type.
You shouldn't need the group by since the first query is grouped by the members ID and will return a single entry per person (UNLESS... its possible to have multiple status types in the same day in the fab_status_members table... unless that is a full date/time field, then you are ok)
Not sure of the "takst" column which table that comes from, but I try to completely qualify the table names (or aliases) they are coming from, buy my guess is its coming from the QuotaType table.
... EDIT from comment...
Sorry, yeah, FQ for the last join. As for it not returning any rows, I would try them one at a time and see where the break is... I would start one at a time... how many from the maxdate query, then add the join to users to make sure same record count returned. Then add the FSM (re-join) for specific member / date activity, THEN into the status type... somewhere along the chain its missing, and the only thing I can think of is a miss on the status type as any member status would have to be associated with one of the users, and it should find back to itself as that's where the max date originated from. I'm GUESSING its somewhere on the join to the status type or the quota.