Mysql scenario - Get all tasks even if there is no entry? - mysql

I have three tables
Tasks with columns Taskid, Taskname
TaskAllocations with columns Taskid, EmpNum
TaskEntries with columns TaskId, EmpNum, WorkedDate, Hoursspent
Now I want to get all the task entries along a particular week. Here my problem is even if there is no Taskentry for a particular task I should get atleast a row with that TaskId, and Taskname with Hoursspent as Null in the query's resultset. I have been trying to get this with the below query.
SELECT A.TaskId,
B.TaskName,
SUM( C.HoursSpent ) as TotalHours ,
C.WorkedDate, C.Comments
FROM TaskAllocations A
LEFT OUTER JOIN TaskEntries C
ON A.TaskId = C.TaskId
AND A.EmpNum = C.EmpNum
INNER JOIN Tasks B
ON A.TaskId = B.TaskId
WHERE A.EmpNum =123456
AND C.WorkedDate
IN ('2010-01-17','2010-01-18','2010-01-19',
'2010-01-20','2010-01-21','2010-01-22','2010-01-23' )
GROUP BY A.TaskId, C.WorkedDate
ORDER BY A.TaskId,C.WorkedDate ASC ';
What I am getting for this SQL piece is if and only if there is an entry for a particular task id, then only i am getting a row for that. but what I want is to get atleast a row for each and every task that is available to a EmpNum. Even if I get one row for each TaskId and WorkedDate combination no issues. Please help me with this. Actual intention of this is to build a HTML two dimensional table with each task entry against date and task as shown below.
---------------------------------------------------------
TaskId TaskName Sun Mon Tue Wed Thu Fri Sat
---------------------------------------------------------
18 name1 2 3 4:30 3:30
19 name2
20 name3 4 2:30
22 name4 2:30
23 name5
24 name6 1:30 6
---------------------------------------------------------
So that this can be updated by the user for each year week. First I thought of group_concat but because of performance I am using normal group by query.
Note: for a particular taskid and workeddate there will be only one entry of hoursspent.
I have almost built the frontend. Please help me to get all task ids as above even if there is no entry. Do I need to use subquery.

don't user an inner join, use a left or right join, depending which values from which tables you want.
so:
SELECT *
FROM tasks t
LEFT JOIN taskentries te
ON t.id = te.id
which is the same statement as:
SELECT *
FROM tasksentries te
RIGHT JOIN tasks t
ON te.id = t.id
will get you all tasks, even if there is no taskentry
an inner join will only select rows when there are rows in both tables, left join selects all rows from the left (first) table and matching from the other row (if there is no such row, null will be the value of all columns). right join will do the oposite: select all rows from right (second) table and matching from left.
a LEFT JOIN b is the same as b RIGHT JOIN a

After rigorous testing of different options I came up with the below solution which will give the required results for me.
SELECT Final.TaskId,
Final.TaskName,
Tmp.HoursSpent AS TotalHours,
Tmp.WorkedDate
FROM (
SELECT A.TaskId, B.TaskName, A.EmpNum
FROM TaskAllocations A
INNER JOIN
Tasks B
ON ( A.TaskId = B.TaskId )
WHERE a.empnum = "333"
)Final
LEFT OUTER JOIN (
SELECT New.TaskId, New.EmpNum, New.WorkedDate, New.HoursSpent
FROM TaskEntries New
WHERE New.WorkedDate
IN
('2010-01-17','2010-01-18','2010-01-19',
'2010-01-20','2010-01-21','2010-01-22','2010-01-23' )
OR New.WorkedDate IS NULL
AND New.EmpNum = "333"
)Tmp
ON Tmp.TaskId = Final.TaskId
AND Tmp.EmpNum = Final.EmpNum
ORDER BY Final.TaskId, Tmp.WorkedDate ASC ;
The first query of mine in the question was not working as I was putting a condition on right table's column while doing Left Outer Join. Thanks to all for the support.

Related

MYSQL - show data between 2 dates if every date counter matches the condition

I am trying to show different plans to customers if the are available for customers by dates.
e.g if a customer choose date between 2 to 4 august and want to see the plan
my issue is while running mysql query and when there are plan sold out for 4 august query still showing results.
From 02-04 august
Plans available
On 02 -> 2
On 03 -> 0
My aim is to get 0 rows in returns when customer want to buy 1 plan between 2 to 4th august.
MySQL query:
SELECT bs.`sales_available` sale_to,hp.`package_name`
FROM `tbl_booking_categories` bc
LEFT JOIN `tbl_booking_suite` bs ON bc.`id`=bs.`booking_cat_id`
LEFT JOIN `tbl_holiday_packages` hp ON hp.`id`=bc.`package_id`
LEFT JOIN `tbl_room_type` rt ON rt.`id`=bc.`room_type_id`
WHERE bc.`room_type_id`='5' AND bs.booked_date IN ('2017-08-10','2017-08-11') AND bs.`sales_available`>=1
GROUP BY bc.`package_id`
Trying to assume your table structure (in a minimal way). Is this correct?
If my assumption is right, "INNER-ing" JOIN tables should solve your problems? Isn't it?
SELECT bs.`sales_available` sale_to
,hp.`package_name`
FROM `tbl_booking_categories` bc
INNER JOIN `tbl_booking_suite` bs ON bc.`id` = bs.`booking_cat_id`
INNER JOIN `tbl_holiday_packages` hp ON hp.`id` = bc.`package_id`
INNER JOIN `tbl_room_type` rt ON rt.`id` = bc.`room_type_id`
WHERE bc.`room_type_id` = '5'
AND bs.booked_date BETWEEN '2017-08-10' AND '2017-08-11'
AND bs.`sales_available` >= 1
GROUP BY bc.`package_id`

MySql Fetch rows that have latest date

Here's the code:
SELECT tblitem.strItemCode, tblitem.strItemName, tblitemunit.strItemUnitName, tblvendor.strVendName, MAX(tblitemprice.dtmItemPasOf) AS Expr1,
tblitemprice.dblItemPAmount
FROM tblclassification INNER JOIN
tblitem ON tblclassification.strClasCode = tblitem.strItemClasCode INNER JOIN
tblitemprice ON tblitem.strItemCode = tblitemprice.strItemPItemCode INNER JOIN
tblitemunit ON tblitemprice.strItemPItemUnitCode = tblitemunit.strItemUnitCode INNER JOIN
tblvendor ON tblclassification.strClasCode = tblvendor.strVendClasCode AND tblitemprice.strItemPVendCode = tblvendor.strVendCode AND tblitem.deleted_at IS NULL
GROUP BY tblitem.strItemCode, tblitem.strItemName, tblitemunit.strItemUnitName, tblvendor.strVendName, tblitemprice.dblItemPAmount
And Here's the Result:
CODE NAME UNIT VENDOR DATE PRICE
ITEM101-Fudgee Bar-Piece-Imus Palengke 10/9/20165:03:32AM - 6.5
ITEM102-Yum Burger-Box-Jollibee Lumina Mall-10/9/2016 6:13:27 AM - 2500
ITEM102-Yum Burger-Piece-Jollibee Lumina Mall-10/9/2016 4:42:28 AM - 30
ITEM102-Yum Burger-Piece-Jollibee Lumina Mall-10/13/2016 12:37:31 PM- 35
ITEM102-Yum Burger Piece Jollibee Lumina Mall 10/14/2016 10:05:44 PM 40
What I want to happen is to fetch only the row with the latest price. Can someone help me please.
I want to fetch the Item101 and only the last row to ITEM102 since it is the latest.
If I understand correctly, you're looking for the last updated row price. It's easy to do so:
Order your data by their relevant timestamp in reversed order (ORDER BY <FIELD_NAME> DESC). From what I gather from your query and results, Expr1 is the latest date for a given price.
Only pick a single element (LIMIT 1). Since your data is already ordered in reverse chronological order, you're sure to pick the latest one.
The SQL for that would be
SELECT tblitem.strItemCode, tblitem.strItemName, tblitemunit.strItemUnitName, tblvendor.strVendName, MAX(tblitemprice.dtmItemPasOf) AS Expr1,
tblitemprice.dblItemPAmount
FROM tblclassification INNER JOIN
tblitem ON tblclassification.strClasCode = tblitem.strItemClasCode INNER JOIN
tblitemprice ON tblitem.strItemCode = tblitemprice.strItemPItemCode INNER JOIN
tblitemunit ON tblitemprice.strItemPItemUnitCode = tblitemunit.strItemUnitCode INNER JOIN
tblvendor ON tblclassification.strClasCode = tblvendor.strVendClasCode AND tblitemprice.strItemPVendCode = tblvendor.strVendCode AND tblitem.deleted_at IS NULL
GROUP BY tblitem.strItemCode, tblitem.strItemName, tblitemunit.strItemUnitName, tblvendor.strVendName, tblitemprice.dblItemPAmount
ORDER BY Expr1 DESC
LIMIT 1
Try it out !

Sql Query with 1 Join and 2 Where Clauses not returning all records

So guys, trying to write a query to get the count of statuses where project_id = ? and statuses in 'New' from a couple of tables so let me break it down.
I have these three tables
Case_Status
id case_status
1 New
2 Failed
3. Accepted
Referral
id case_status_id project_id application_id
1 1 1 20
2 2 1 21
Project
id name
1 project1
2 project2
So this is my query
SELECT COUNT(referrals.id) AS count_all, case_statuses.case_status AS counted
FROM "case_statuses" LEFT OUTER JOIN "referrals" ON "referrals"."case_status_id" = "case_statuses"."id"
WHERE "case_statuses"."deleted_at" IS NULL AND (case_statuses.case_status IN ('New') AND referrals.project_id = 1)
GROUP BY case_statuses.case_status;
This is my result
count_all counted
1 New
1 Failed
But I am expecting this result instead
count_all counted
1 New
1 Failed
0 Accepted
Does anyone know what's wrong with my query that isnt showing count for all the case_statuses?
Thanks
Conditions on the second table (in a left join) should be in the on clause:
SELECT COUNT(r.id) AS count_all, cs.case_status AS counted
FROM case_statuses cs LEFT OUTER JOIN
referrals r
ON r.case_status_id = cs.id AND r.project_id = 1
WHERE cs.deleted_at IS NULL AND cs.case_status NOT IN ('New')
GROUP BY cs.case_status;
Otherwise, the WHERE clause turns the outer join into an inner join.
change your query like this
SELECT COUNT(referrals.id) AS count_all, case_statuses.case_status AS counted
FROM "case_statuses" LEFT JOIN "referrals" ON "referrals"."case_status_id" = "case_statuses"."id" AND referrals.project_id = 1
WHERE "case_statuses"."deleted_at" IS NULL AND case_statuses.case_status NOT IN ('New')
GROUP BY case_statuses.case_status;
Given your data and the expected result you just need to loose the WHERE clause.
SELECT COUNT(referrals.id) AS count_all, case_statuses.case_status AS counted
FROM case_statuses
LEFT OUTER JOIN referrals ON referrals.case_status_id = case_statuses.id
GROUP BY case_statuses.case_status;
See this fiddle for details.

Generate empty row for unused slots

I designed a db schema to hold patient appointments. Every hour is divided into equal 15 slots and the last two slots are always kept unused for emergency cases. When printing the appointment list, I need to have all the booked appointments along with empty row for 14 & 15th slot for each hour so receptionist can fill-in the unscheduled appointments by hand in the printed sheet.
appt_schedules:
id
slot_id
hour
scheduled_on
slots table - contains exactly 15 rows all the time.
id
start_time
end_time
The following query gives me the expected result except the empty row for 14 & 15th slot.
SELECT `appt_schedules`.* FROM `appt_schedules`
LEFT JOIN `slots` ON `appt_schedules`.`slot_id` = `slots`.`id`
WHERE `scheduled_on`='2015-04-17' LIMIT 10,10
I am also open to schema changes that can result in simple and efficient queries.
SELECT `appt_schedules`.*, `slots`.`slot_id` FROM `appt_schedules`
LEFT JOIN `slots` ON `appt_schedules`.`slot_id` = `slots`.`id`
WHERE `scheduled_on`='2015-04-17' and `slots`.`slot_id` between 1 and 15
Assuming of cours slots.slot_id holds the 15 equal alots as an int 1 to 15
Another way to write it a bit more optimized is:
SELECT `appt_schedules`.*, `slots`.`slot_id` FROM `appt_schedules`
LEFT JOIN `slots` ON `appt_schedules`.`slot_id` = `slots`.`id`
WHERE `scheduled_on`='2015-04-17' and `slots`.`slot_id` IS NOT NULL
Or even better:
SELECT `appt_schedules`.*, `slots`.`slot_id` FROM `appt_schedules`
JOIN `slots` ON `appt_schedules`.`slot_id` = `slots`.`id`
WHERE `scheduled_on`='2015-04-17'
****BEGIN REVISED EDIT****
If I understand correctly please try:
SELECT s.slot_id, aps.* FROM slots s
LEFT JOIN
(SELECT *, ROW_NUMBER()
over(PARTITION BY aps.slot_id
ORDER BY aps.slot_id
,aps.scheduled_on) AS RowId
FROM appt_schedules aps) aps
ON aps.slot_id = s.id
AND s.slot_id = aps.RowId
WHERE scheduled_on = '2015-04-17'
ORDER BY s.slot_id
Once again thanks for removing slos.slot_id....
select *
from slots s
left join appt_schedules as
on s.id = as.slot_id
where IFNULL(as.scheduled_on, '') in ('', '2015-04-17')

How to left join or inner join a table itself

I have this data in a table, for instance,
id name parent parent_id
1 add self 100
2 manage null 100
3 add 10 200
4 manage null 200
5 add 20 300
6 manage null 300
How can I left join or inner join this table itself so I get this result below?
id name parent
2 manage self
4 manage 10
6 manage 20
As you can I that I just want to query the row with the keyword of 'manage' but I want the column parent's data in add's row as the as in manage's row in the result.
Is it possible?
EDIT:
the simplified version of my actual table - system,
system_id parent_id type function_name name main_parent make_accessible sort
31 30 left main Main NULL 0 1
32 31 left page_main_add Add self 0 1
33 31 left page_main_manage Manage NULL 0 2
my actual query and it is quite messy already...
SELECT
a.system_id,
a.main_parent,
b.name,
b.make_accessible,
b.sort
FROM system AS a
INNER JOIN -- self --
(
SELECT system_id, name, make_accessible, sort
FROM system AS s2
LEFT JOIN -- search --
(
SELECT system_id AS parent_id
FROM system AS s1
WHERE s1.function_name = 'page'
) AS s1
ON s1.parent_id = s2.parent_id
WHERE s2.parent_id = s1.parent_id
AND s2.system_id != s1.parent_id
ORDER BY s2.sort ASC
) b
ON b.system_id = a.parent_id
WHERE a.function_name LIKE '%manage%'
ORDER BY b.sort ASC
result I get currently,
system_id main_parent name make_accessible sort
33 NULL Main 0 1
but I am after this,
system_id main_parent name make_accessible sort
33 self Main 0 1
You just need to reference the table twice:
select t1.id, t1.name, t2.id, t2.name
from TableA t1
inner join TableA t2
on t1.parent_id = t2.Id
Replace inner with left join if you want to see roots in the list.
UPDATE:
I misread your question. It seems to me that you always have two rows, manage one and add one. To get to "Add" from manage:
select system.*, (select parent
from system s2
where s2.parent_id = system.parent_id
and s2.name = 'add')
AS parent
from system
where name = 'manage'
Or, you might split the table into two derived tables and join them by parent_id:
select *
from system
inner join
(
select * from system where name = 'add'
) s2
on system.parent_id = s2.parent_id
where system.name = 'manage'
This will allow you to use all the columns from s2.
Your data does not abide to a child-parent hierarchical structure. For example, your column parent holds the value 10, which is not the value of any id, so a child-parent association is not possible.
In other words, there's nothing that relates the record 2,manage,null to the record 1,add,self, or the record 4,manage,null to 3,add,10, as you intend to do in your query.
To represent hierarchical data, you usually need a table that has a foreign key referencing it's own primary key. So your column parent must reference the column id, then you can express a child-parent relationship between manage and add. Currently, that's not possible.
UPDATED: Joining by parent_id, try:
select m.id, m.name, a.parent
from myTable m
join myTable a on m.parent_id = a.parent_id and a.name = 'add'
where m.name = 'manage'
Change the inner join to a left join if there may not be a corresponding add row.