In mysql how to select columns in a union join - mysql

How to select join table's columns when I'm using union. Specifically how to select b.id(booking id) in the following query? Also is it the right way to do it? Can you please tell me any other way to do it? I also want to select available time for a selected date if any available. I have three categories of timing.
cat1(category 1): availability on particular days of week(Example:Mon, Tue, Wed, etc of any month)
cat2(category 2): availability on First monday, second saturday, etc of any months.
cat3(category 3): availability on last saturday, last friday, etc of
any month.
I'm using day_offset, and cat columns in my doctor_schedule table to calculate date and checking it to #selected_date. I am also filtering off days with the help of my off_day table (off_days table stores information for any holidays, or if doctor taking leave for any personal reasons).
mysql> select * from doctor;
+----+-------------+
| id | name |
+----+-------------+
| 1 | John Doe |
| 2 | Larry Jones |
+----+-------------+
mysql> select * from doctor_schedule;
+----+-----------+-----+------------+----------+---------------+--------+------------+-----+
| id | doctor_id | day | start_time | end_time | booking_limit | active | day_offset | cat |
+----+-----------+-----+------------+----------+---------------+--------+------------+-----+
| 1 | 2 | 5 | 10:00:00 | 12:00:00 | 1 | 1 | 3 | 2 |
| 2 | 2 | 5 | 19:00:00 | 22:00:00 | 1 | 1 | 3 | 2 |
| 3 | 2 | 6 | 19:00:00 | 22:00:00 | 1 | 1 | 0 | 3 |
+----+-----------+-----+------------+----------+---------------+--------+------------+-----+
mysql> select * from booking;
+----+---------+-------------+--------------+------+
| id | user_id | schedule_id | booking_date | paid |
+----+---------+-------------+--------------+------+
| 1 | 1 | 3 | 2017-06-26 | 1 |
+----+---------+-------------+--------------+------+
mysql> select * from off_day;
+----+-----------+-------------+------------+
| id | doctor_id | schedule_id | date |
+----+-----------+-------------+------------+
| 2 | 1 | 3 | 2017-06-26 |
+----+-----------+-------------+------------+
set #selected_date := "2017-06-26";
set #doctor_id := 2;
SET #first_day = DATE_SUB(#selected_date, INTERVAL DAYOFMONTH(#selected_date) - 1 DAY);
select s.*
from
(select
s1.id
from doctor_schedule s1
where
s1.cat = 1
and
s1.day = weekday(#selected_date)
UNION
select
s2.id
from doctor_schedule s2
where
s2.cat = 2
and
DATE_ADD(#first_day, INTERVAL (s2.day - WEEKDAY(#first_day)) + (s2.other*7) DAY) = #selected_date
UNION
select
s3.id
from doctor_schedule s3
where
s3.cat = 3
and
date_sub(LAST_DAY(#selected_date), INTERVAL ((7 + WEEKDAY(LAST_DAY(#selected_date)) - s3.other) % 7) DAY) = #selected_date
) as s
right join
booking b
on
s.id = b.schedule_id
and
b.booking_date >= #selected_date
and
b.paid = 1
left join
off_day o
on
s.id = o.schedule_id
and
o.date = #selected_date
and
o.doctor_id = #doctor_id
where
o.schedule_id is null
group by
s.id

Mr. Tim Biegeleisen pointed that there is no booking_id in my query. Thanks to him that I found the solution. It should be b.id.
set #selected_date := "2017-06-26";
set #doctor_id := 2;
SET #first_day = DATE_SUB(#selected_date, INTERVAL
DAYOFMONTH(#selected_date) - 1 DAY);
select s.*, b.id
from
(select
s1.id
from doctor_schedule s1
where
s1.cat = 1
and
s1.day = weekday(#selected_date)
UNION
select
s2.id
from doctor_schedule s2
where
s2.cat = 2
and
DATE_ADD(#first_day, INTERVAL (s2.day - WEEKDAY(#first_day)) + (s2.other*7) DAY) = #selected_date
UNION
select
s3.id
from doctor_schedule s3
where
s3.cat = 3
and
date_sub(LAST_DAY(#selected_date), INTERVAL ((7 + WEEKDAY(LAST_DAY(#selected_date)) - s3.other) % 7) DAY) = #selected_date
) as s
right join
booking b
on
s.id = b.schedule_id
and
b.booking_date >= #selected_date
and
b.paid = 1
left join
off_day o
on
s.id = o.schedule_id
and
o.date = #selected_date
and
o.doctor_id = #doctor_id
where
o.schedule_id is null
group by
s.id

Related

How to write this query MYSQL

I have this database:
| id | name | email | control_number | created | | | | | |
|:--:|-------|-----------------|----------------|------------|---|---|---|---|---|
| 1 | john | john#gmail.com | 1 | 14/09/2016 | | | | | |
| 2 | carl | carl#gmail.com | 1 | 13/08/2016 | | | | | |
| 3 | frank | frank#gmail.com | 2 | 12/08/2016 | | | | | |
And i want to get the COUNT in the last 12 months by the control_number.
basicly is a COUNT where control_number = 1 but by month.
So if the query is done today, its september, it should start from september to October 2015 and display the count of records for each month.
Result should be:
09/2016 = 50
08/2016 = 35
07/2016 = 20
06/2016 = 50
05/2016 = 21
04/2016 = 33
03/2016 = 60
02/2016 = 36
01/2016 = 11
12/2015 = 0
11/2015 = 0
10/2015 = 0
Hmmm. Getting the 0 values can be tricky. Assuming that you have some data each month (even if not for "1"), th en you can do:
select extract(year_month from created) as yyyymm,
sum(control_number = 1)
from t
where created >= date_sub(curdate(), interval 12 month)
group by extract(year_month from created)
order by yyyymm;
If you don't have at least one record for each month, then you'll need a left join and a table with one row per month.
Try this:
select CONCAT(SUBSTRING(ym, 5, 2), '/', SUBSTRING(ym, 1, 4)) Month, Count from (
select EXTRACT(YEAR_MONTH FROM created) ym, count(*) Count
from mytable
where EXTRACT(YEAR_MONTH FROM created) > (EXTRACT(YEAR_MONTH FROM SUBDATE(NOW(), INTERVAL 1 YEAR))
group by 1
order by 1 desc) x
Try:
select concat(month(created),'/',year(created)) as period, count(*) as cnt
from mytable
where control_number=1 and TIMESTAMPDIFF(year, created, now())=0
group by (month(created));

Left Join not filtering results

Using MySQL.
Below is my table structure.
batch_admissions
------------------------+
batchId | studentId |
----------------------- +
1 | 1 |
1 | 2 |
1 | 3 |
2 | 1 |
2 | 2 |
------------------------+
attendance_master
----------------------------+
attendance | studentId |
----------------------------+
P | 1 |
P | 2 |
P | 3 |
----------------------------+
desire result if batchId=2 as below as attendance_master only contain record of batchId=1
----------------------------+
attendance | studentId |
----------------------------+
| 1 |
| 2 |
----------------------------+
But currently I am getting all record back from attendance_master irrespective of change in batchId.
What wrong in my query? I think left join should do the job. but not working
SELECT
a.attendanceId,
a.attendanceDate,
a.attendance,
a.Remarks,
CONCAT(b.studentFirstName, ' ', COALESCE(b.studentMiddleName,'') , ' ', b.studentLastName) as studentName ,
c.classRollNum,
SUBSTRING_INDEX(SUBSTRING_INDEX( a.attendanceDate , '-', 3 ),'-',-1) AS attDay,
CASE WHEN DAYNAME(a.attendanceDate) = 'Monday' THEN 'Mon'
WHEN DAYNAME(a.attendanceDate) = 'Tuesday' THEN 'Tue'
WHEN DAYNAME(a.attendanceDate) = 'Wednesday' THEN 'Wed'
WHEN DAYNAME(a.attendanceDate) = 'Thursday' THEN 'Thu'
WHEN DAYNAME(a.attendanceDate) = 'Friday' THEN 'Fri'
WHEN DAYNAME(a.attendanceDate) = 'Saturday' THEN 'Sat'
WHEN DAYNAME(a.attendanceDate) = 'Sunday' THEN 'Sun'
END as attDayName
,CONCAT(SUBSTRING_INDEX(SUBSTRING_INDEX( a.attendanceDate , '-', 3 ),'-',-1),'.',c.classRollNum) as Idx
FROM attendance_master a
LEFT JOIN student_master b ON a.studentId = b.studentId
LEFT JOIN batch_admissions c ON c.studentId = a.studentId AND c.batchId=1
WHERE a.attendanceDate BETWEEN '2016-03-01' AND '2016-03-31'
ORDER BY c.classRollNum ASC
-------------
Basically I trying to avoid triggering two queries and want result in single query.
batch_admissions table holds series of batch with N numbers student in it.
attendance_master table holds attendance of students for all batch.
On web page I am displaying table grid report, per batch wise.
What I am trying to achieve,
case 1 : when attendance_master NOT contain attendance for batchId for specific period. Still want list of student for that batch
-------------------------------------------------------
BatchId |studentId | Mon | Tue | Wed | Thus |
------------------------------------------------------
1 | 11 | | | | |
1 | 12 | | | | |
.. | .. | | | | |
Case 2: when attendance_master contain attendance for batchId for specific period.
-------------------------------------------------------
BatchId |studentId | Mon | Tue | Wed | Thus |
------------------------------------------------------
2 | 1 | P | P | P | P |
2 | 2 | P | A | P | P |
.. | .. | P | P | P | P |
Alternate I can trigger two queries to achieve this logically. One for get of student for batch, and then getting attendance detail for all those student.
ok... so return all records from batch admissions and the related student_master data (which there will always be records) and the associated attendance master data...
FROM batch_admissions c
INNER JOIN student_master b
ON a.studentId = c.studentId
LEFT JOIN attendance_master a
ON c.studentId = a.studentId
and a.attendanceDate BETWEEN '2016-03-01' AND '2016-03-31'
WHERE c.batchId=1
ORDER BY c.classRollNum ASC

MySQL Use Virtual Columns as Join Points?

I have the three following tables:
monthly:
| u_id | memb_type | run_day |
------------------------------------
| 1 | 1 | 410 |
| 2 | 1 | 410 |
| 1 | 2 | 510 |
| 2 | 1 | 510 |
| 1 | 2 | 610 |
| 2 | 1 | 610 |
memb_types:
| id | name |
----------------------------
| 1 | member |
| 2 | active_member |
user:
| id | join_date |
----------------------------
| 1 | 2015-03-01 |
| 2 | 2015-04-04 |
The query I'm attempting to write will show what memb_type the user was the first two months of them joining. The tricky part is monthly.run_day is not a valid datetime, and it basically requires creating multiple virtual columns I have something along the lines of this right now and I'm stuck:
SELECT
user.id,
user.join_date,
MONTH(user.join_date + INTERVAL 1 MONTH) AS `first_month`,
"???" AS `first_month_memb_type`,
MONTH(user.join_date + INTERVAL 2 MONTH) AS `second_month`,
"???" AS `second_month_memb_type`
FROM
user
INNER JOIN
monthly
ON
user.id = monthly.u_id
INNER JOIN
memb_types
ON
monthly.memb_type = memb_types.id
I'm not exactly sure how to use the data generated from MONTH(user.join_date + INTERVAL 1 MONTH) and join that back to the monthly table joined on the run_day (SUBSTRING(monthly.run_day, 1, 1) Does what we need to match the value from the month to the run_day) But I don't know how to join that back to the monthly table twice for the first_month_memb_type and the second_month_memb_type.
Based on the needs the desired output would be as follows (Abbreviated for brevity):
| id | join_date | fm | fm_type | sm | sm_type |
-------------------------------------------------------
| 1 | 2015-03-01 | 4 | member | 5 | active_member |
| 2 | 2015-04-04 | 5 | member | 6 | member |
Any ideas how to accomplish this?
It's okay to use those functions as the join predicates, which means we just need to join a couple of extra times to get your first and second months all in a row, and only once - instead of two rows per u_id.
select u.id,
u.join_date,
month(u.join_date + interval 1 month) fm,
mt1.name fm_type,
month(u.join_date + interval 2 month) 2m,
mt2.name sm_type
from user u
inner join monthly m1
on month(u.join_date + interval 1 month) = substring(m1.run_day, 1, 1)
and m1.u_id = u.id
inner join monthly m2
on month(u.join_date + interval 2 month) = substring(m2.run_day, 1, 1)
and m2.u_id = u.id
inner join memb_types mt1
on m1.memb_type = mt1.id
inner join memb_types mt2
on m2.memb_type = mt2.id
demo fiddle
Note: if your table gets particularly large - it's not going to be much fun joining on the results of functions, in which case you may wish to consider making those 'virtual' columns, not so virtual, and then indexing them.
edit
As pointed out in the comments, it may make a slight difference if run_day is a numeric field, to use math rather than substring:
select u.id,
u.join_date,
month(u.join_date + interval 1 month) fm,
mt1.name fm_type,
month(u.join_date + interval 2 month) 2m,
mt2.name sm_type
from user u
inner join monthly m1
on month(u.join_date + interval 1 month) = floor(m1.run_day / 100)
and m1.u_id = u.id
inner join monthly m2
on month(u.join_date + interval 2 month) = floor(m2.run_day / 100)
and m2.u_id = u.id
inner join memb_types mt1
on m1.memb_type = mt1.id
inner join memb_types mt2
on m2.memb_type = mt2.id

SQL select orders depending on status and date

I have a table order_history that is similar to the following:
+-------------------------------------------------------------+
| order_history_id | order_id | order_status_id | date_addded |
+-------------------------------------------------------------+
| 1 | 1 | 1 | 2014-03-20 |
| 2 | 1 | 2 | 2014-03-21 |
| 3 | 1 | 3 | 2014-03-29 |
| 4 | 2 | 1 | 2014-03-20 |
| 5 | 2 | 2 | 2014-03-21 |
| 6 | 2 | 3 | 2014-04-02 |
| 7 | 3 | 1 | 2014-04-20 |
| 8 | 3 | 2 | 2014-04-21 |
| 9 | 3 | 3 | 2014-04-22 |
+-------------------------------------------------------------+
The order_status represents the status of an order
+-------------------------------+
| order_status_id | name |
+-------------------------------+
| 1 | received |
| 2 | processed |
| 3 | shipped |
+-------------------------------+
what i want to do is to pull out all the orders that have been received before 2014-04-01 but not shipped until after 2014-04-01.
So in this case the query would just return order_id 2 as this is the only order that was received before 2014-04-01 yet shipped after.
I can't even seem to get started... Any help, hints, or pointers much appreciated.
You can do so ,by joining your tables and count the statues shipped for each order by using expression in sum i.e SUM(os.name ='shipped') shipped
SELECT o.*
,SUM(os.name ='shipped') shipped
FROM
orders o
LEFT JOIN orders_status os USING(order_status_id)
WHERE o.date_addded < '2014-04-01'
GROUP BY o.order_id
HAVING shipped =0
Fiddle Demo
You can use INNER JOIN with this, if I get what you really want you can try this:
SELECT DISTINCT order_id
FROM order_history A
INNER JOIN order_status B
ON A.order_status_id = B.order_status_id
WHERE (A.order_Status_id = '1' AND A.date_added < #date) AND (A.order_status_id = '3' AND A.date_added < #date)
SELECT h1.order_id
FROM order_history h1
JOIN order_status s1
ON s1.order_status_id = h1.order_status_id
JOIN order_history h2
ON h2.order_id = h1.order_id
JOIN order_status s2
ON s2.order_status_id = h2.order_status_id
WHERE h1.date_addded < '2014-04-01'
AND s1.name = 'received'
AND h2.date_addded >= '2014-04-01'
AND s2.name = 'shipped';
Note: Too many 'd's in addded
SELECT r.order_id
FROM (
SELECT DISTINCT oh.order_id
FROM order_history AS oh
JOIN order_status AS os ON(oh.order_status_id = os.order_status_id)
WHERE os.name = 'received'
AND oh.date_addded < '2014-04-01'
) AS r
JOIN (
SELECT DISTINCT oh.order_id
FROM order_history AS oh
JOIN order_status AS os ON(oh.order_status_id = os.order_status_id)
WHERE os.name = 'shipped'
AND oh.date_addded > '2014-04-01'
) AS s ON (s.order_id = r.order_id)
demo
What about this simple and light query:
SELECT DISTINCT order_id
FROM order_history o1
JOIN order_history o2
ON o1.order_id = o2.order_id
AND o1.order_status_id=1 AND o1.date_added<'2014-04-01'
AND o2.order_status_id=3 AND o2.date_added>'2014-04-01';
Not tested, but try this:
SELECT A.ORDER_ID
FROM ORDER_HISTORY A, ORDER_HISTORY B
WHERE A.ORDER_ID = B.ORDER_ID
AND A.order_status_id = 1
AND A.date_addded < TO_DATETO_DATE ('2014-04-01', 'YYYY-MM-DD')
AND B.order_status_id = 3
AND B.date_addded > TO_DATETO_DATE ('2014-04-01', 'YYYY-MM-DD');

Joining tables and making count operation - MySQL

I have those tables:
Members
---------------------------
MemberID | Name |.....
1
2
3
4
---------------------------
RentedMovies
---------------------------
MemberID | MovieID | DateOfLease | ReturnDate | .....
1 | 1 | 2012-12-20 | 2013-01-05
1 | 2 | 2012-12-15 | 2012-12-30
1 | 3 | 2012-12-16 | 2013-01-06
2 | 1 | 2012-12-17 | 2012-12-18
2 | 4 | 2012-12-18 | 2013-01-05
3 | 1 | 2012-12-19 | 2013-01-04
I need to get this:
--------------------------------------------------------
MemberID | NumberOfRentedMovies | ReturnData < curdate())
1 | 3 | 1
2 | 2 | 1
3 | 1 | 0
4 | 0 | 0
---------------------------------------------------------
And i used next code:
SELECT Members.MemberID,
COUNT(rented.MemberID) AS NumberOfRentedMovies,
COUNT(notTakenBackOnTime.idClana) AS NumberOfMoviesLate
FROM Members
left JOIN RentedMovies as rented ON rented.MemberID = Members.MemberID
left JOIN RentedMovies as notTakenBackOnTime ON notTakenBackOnTime.MemberID
= Members.MemberID AND notTakenBackOnTime.ReturnDate< CURDATE()
group by Members.MemberID
But it doesnt work corrextly!
And I also tried with this:
SELECT MemberID,my,my2
FROM Members as mem
JOIN (SELECT COUNT(* )AS my FROM RentedMovies) b
ON b.MemberID = mem.MemberID
JOIN (SELECT COUNT(* )AS my2 FROM RentedMovies WHERE ReturnDate< CURDATE()) c
ON c.MemberID = mem.MemberID
But i got some errors!
So the question is how to accomplish right solution?
You were close. Try this:
SELECT M.MemberID,
COUNT(RM.MemberID) NumberOfRentedMovies,
SUM(CASE WHEN RM.ReturnDate < CURDATE() THEN 1 ELSE 0 END) ReturnData
FROM Members M
LEFT JOIN RentedMovies RM
ON M.MemberID = RM.MemberID
GROUP BY M.MemberID
The desired result you showed can be accomplished by:
SELECT MemberID,
COALESCE(COUNT(MovieID), 0) AS NumberOfRentedMovies,
COALESCE(SUM(ReturnDate < CURDATE()), 0) AS NotYetReturned
FROM Members
LEFT JOIN RentedMovies USING (MemberID)
GROUP BY MemberID
See it in action: http://sqlfiddle.com/#!2/a192c/1