Joining tables and making count operation - MySQL - 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

Related

In mysql how to select columns in a union join

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

Select values if doesn't match in other table by left join

I am having some problem with getting the values from two tables of my database .
I have two tables in my database 1 is mem and 2nd is payment
mem stores name and drawid of users
payment table stores draw and instalment of the user
User pays us every month .
so if a user with a draw id 1 pays us in feb the values in two tables are
mem drawid=1 and name = something
payment draw = 1 and instalment=2
drawid in mem is same as draw in payment
so the tables has many to many relation.
Now I need to find the list of all the members who have not paid even 1 instalment before 4th month.
I am using this query
SELECT drawid,contact,dnd,mem.name, count(*) as numPayments FROM mem
LEFT JOIN payment ON (mem.drawid = payment.draw) GROUP BY
drawid HAVING numPayments < 4
it's working all good no issues only the issue is that I also need to show the instalments the user has paid so I need to fetch all the instalment from table payments and then show by while loop.
This query is perfect but it gives me repeated results !!!
SELECT drawid,contact,dnd,mem.name, count(*) as numPayments,NULL numPaidPayments ,NULL PAID_CONTACT,NULL NAME_PAID FROM mem
LEFT JOIN payment ON (mem.drawid = payment.draw) GROUP BY
drawid HAVING numPayments < 4
UNION
SELECT NULL drawid,NULL contact, NULL dnd, NULL name,NULL numPayments,COUNT(*) as numPaidPayments ,contact PAID_CONTACT,mem.name NAME_PAID FROM mem
INNER JOIN payment ON (mem.drawid = payment.draw) GROUP BY
drawid HAVING numPaidPayments >= 4
SELECT drawid,contact,dnd,mem.name, count(*) as numPayments,NULL numPaidPayments ,NULL PAID_CONTACT,NULL NAME_PAID FROM mem
LEFT JOIN payment ON (mem.drawid = payment.draw) GROUP BY
drawid HAVING numPayments < 4
UNION
SELECT NULL drawid,NULL contact, NULL dnd, NULL name,NULL numPayments,COUNT(*) as numPaidPayments ,contact PAID_CONTACT,mem.name NAME_PAID FROM mem
INNER JOIN payment ON (mem.drawid = payment.draw) GROUP BY
drawid HAVING numPaidPayments >= 4
Try above query.
Given this
MariaDB [sandbox]> select * from member;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
| 4 |
+------+
4 rows in set (0.00 sec)
MariaDB [sandbox]> select * from payment;
+--------+------+
| mem_id | mth |
+--------+------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 2 | 1 |
| 2 | 3 |
| 3 | 2 |
| 3 | 4 |
+--------+------+
8 rows in set (0.00 sec)
This
select m.id, group_concat(p.mth order by p.mth) mthspaid,
sum(case when p.mem_id is not null then 1 else 0 end) NoofMthsPaid,
4 - sum(case when p.mem_id is not null then 1 else 0 end) NoofMthsMissed
from member m
left join payment p on p.mem_id = m.id
group by m.id
Gives this
+------+----------+--------------+----------------+
| id | mthspaid | NoofMthsPaid | NoofMthsMissed |
+------+----------+--------------+----------------+
| 1 | 1,2,3,4 | 4 | 0 |
| 2 | 1,3 | 2 | 2 |
| 3 | 2,4 | 2 | 2 |
| 4 | NULL | 0 | 4 |
+------+----------+--------------+----------------+
4 rows in set (0.00 sec)
And if you add in code to work out the months due
select paid.*, due.mthsmissed
from
(
select m.id, group_concat(p.mth order by p.mth) mthspaid,
sum(case when p.mem_id is not null then 1 else 0 end) NoofMthsPaid,
4 - sum(case when p.mem_id is not null then 1 else 0 end) NoofMthsMissed
from member m
left join payment p on p.mem_id = m.id
group by m.id
) paid
left join
(
select due.id, group_concat(due.mth order by due.mth) MthsMIssed
from
(
select m.id,d.mth
from member m,(select 1 mth union select 2 union select 3 union select 4) d
) due
left join payment p on p.mem_id = due.id and p.mth = due.mth
where p.mth is null
group by due.id
) due on paid.id = due.id
You get this
+------+----------+--------------+----------------+------------+
| id | mthspaid | NoofMthsPaid | NoofMthsMissed | mthsmissed |
+------+----------+--------------+----------------+------------+
| 1 | 1,2,3,4 | 4 | 0 | NULL |
| 2 | 1,3 | 2 | 2 | 2,4 |
| 3 | 2,4 | 2 | 2 | 1,3 |
| 4 | NULL | 0 | 4 | 1,2,3,4 |
+------+----------+--------------+----------------+------------+
4 rows in set (0.04 sec)

MySQL count based on condition from a different table

I have the following tables.
Table : types
--------------------
id | type
--------------------
1 | AA
--------------------
2 | BB
--------------------
3 | AA
--------------------
4 | BB
--------------------
Table : users
--------------------
id | username
--------------------
1 | abc
--------------------
2 | bcd
--------------------
3 | cde
--------------------
4 | def
--------------------
Table : methods
---------------------------------
id | user_id | details | type_id
---------------------------------
1 | 1 | detail_1 | 1
---------------------------------
2 | 1 | detail_2 | 3
---------------------------------
3 | 1 | detail_3 | 1
---------------------------------
4 | 1 | detail_4 | 3
---------------------------------
5 | 2 | detail_3 | 1
---------------------------------
6 | 2 | detail_5 | 2
---------------------------------
7 | 2 | detail_6 | 4
---------------------------------
8 | 2 | detail_2 | 3
---------------------------------
9 | 1 | detail_2 | 3
---------------------------------
10 | 1 | detail_2 | 3
---------------------------------
Desired Result :
---------------------------------------------------
UserName | No_of_AA_details | No_of_BB_details |
---------------------------------------------------
abc | 4 | 0 |
---------------------------------------------------
bcd | 2 | 2 |
---------------------------------------------------
I need to get the count of distinct details based on the type from types table.
I have tried this queries but max I am getting is all the counts and not the distinct values.
SELECT u.username,
CASE WHEN t.type = 'AA' THEN count(distinct m.details) END AS No_of_AA_details,
CASE WHEN t.type = 'BB' THEN count(distinct m.details) END AS No_of_BB_details
FROM users as u inner join methods as m on u.id = m.user_id inner join types as t on t.id = m.type_id
GROUP BY m.user_id
SELECT u.username,
SUM(t.type = 'AA') AS No_of_AA_details,
SUM(t.type = 'AA') AS No_of_BB_details
FROM users as u inner join methods as m on u.id = m.user_id inner join types as t on t.id = m.type_id
GROUP BY m.user_id
Any suggestions are welcome.
I can't test it right know but i think you had a good idea, can you try :
SELECT u.username,
m.user_id,
CASE
WHEN t.type = 'AA' THEN 1
ELSE 0
END AS No_of_AA_details,
CASE
WHEN t.type = 'BB' THEN 1
ELSE 0
END AS No_of_BB_details
FROM users as u
INNER JOIN methods as m on u.id = m.user_id
INNER JOIN types as t on t.id = m.type_id
and now you just need to do the sum :
SELECT u.username,
m.user_id,
SUM (CASE
WHEN t.type = 'AA' THEN 1
ELSE 0
END ) AS No_of_AA_details,
SUM (CASE
WHEN t.type = 'BB' THEN 1
ELSE 0
END ) AS No_of_BB_details
FROM users as u
INNER JOIN methods as m on u.id = m.user_id
INNER JOIN types as t on t.id = m.type_id
GROUP BY u.username, m.user_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');

Select monthly counts of data across two tables in mysql

I have two tables, lastfm_scrobbles and lastfm_annotations. Example data:
mysql> select * from lastfm_scrobbles limit 5;
+---------+---------+-----------+---------------------+
| user_id | item_id | artist_id | scrobble_time |
+---------+---------+-----------+---------------------+
| 1469 | 45651 | 1 | 2010-06-30 13:57:42 |
| 1469 | 45651 | 1 | 2011-03-28 15:43:37 |
| 6872 | 45653 | 1 | 2013-08-03 15:07:44 |
| 7044 | 1370 | 1 | 2007-03-26 17:07:26 |
| 7044 | 1370 | 1 | 2007-08-24 18:41:35 |
+---------+---------+-----------+---------------------+
mysql> select * from lastfm_annotations limit 5;
+---------+---------+-----------+--------+------------+
| user_id | item_id | artist_id | tag_id | tag_month |
+---------+---------+-----------+--------+------------+
| 121 | 1330412 | 1330412 | 475 | 2006-12-01 |
| 121 | 1330412 | 1330412 | 517 | 2006-12-01 |
| 121 | 1330412 | 1330412 | 7280 | 2006-12-01 |
| 121 | 1330412 | 1330412 | 21384 | 2006-12-01 |
| 121 | 1330412 | 1330412 | 27872 | 2006-12-01 |
+---------+---------+-----------+--------+------------+
Furthermore, I have a user information table (lastfm_users). The details of this aren't important, but what is relevant is that the query:
select user_id from lastfm_users where scrobbles_recorded==1;
Returns the users I care about for the purposes of this question.
Ok, with that preamble out of the way: I need a query that will get me, for those users, the total number of entries they have in both the scrobbles and annotations tables for each month. In other words, the result should look something like:
user_id y m scrobble_count anno_count
123 2006 3 100 50
456 2008 11 321 10
... and so on
Make sense? I believe the query I want is a combination of the following:
select year(tag_month) as y, month(tag_month) as m, count(*) as anno_count
from lastfm_annotations where user_id in (select user_id from
lastfm_users where scrobbles_recorded=1)
group by user_id, year(tag_month), month(tag_month);
select year(scrobble_time) as y, month(scrobble_time) as m, count(*) as scrobble_count
from lastfm_scrobbles where user_id in (select user_id from
lastfm_users where scrobbles_recorded=1)
group by user_id, year(scrobble_time), month(scrobble_time);
But I'm not certain of the correct way to generate the join query to get the result I want. Suggestions?
You can try
select user_id, y, m,
coalesce(sum(case when source = 1 then total end), 0) anno_count,
coalesce(sum(case when source = 2 then total end), 0) scrobble_count
from
(
select 1 source, a.user_id, year(tag_month) y, month(tag_month) m, count(*) total
from lastfm_annotations a join lastfm_users u
on a.user_id = u.user_id
where u.scrobbles_recorded = 1
group by user_id, year(tag_month), month(tag_month)
union all
select 2 source, s.user_id, year(scrobble_time), month(scrobble_time), count(*)
from lastfm_scrobbles s join lastfm_users u
on s.user_id = u.user_id
where u.scrobbles_recorded = 1
group by user_id, year(scrobble_time), month(scrobble_time)
) q
group by user_id, y, m
or just
select user_id, y, m,
sum(case when source = 1 then 1 else 0 end) anno_count,
sum(case when source = 2 then 1 else 0 end) scrobble_count
from
(
select 1 source, a.user_id, year(tag_month) y, month(tag_month) m
from lastfm_annotations a join lastfm_users u
on a.user_id = u.user_id
where u.scrobbles_recorded = 1
union all
select 2 source, s.user_id, year(scrobble_time), month(scrobble_time)
from lastfm_scrobbles s join lastfm_users u
on s.user_id = u.user_id
where u.scrobbles_recorded = 1
) q
group by user_id, y, m;
Here is SQLFiddle demo