Listing rows not only when criteria are met - mysql

I have three tables:
1 dates - contains column date
2 book - contains columns room, date and surname
3 people - contains column surname
I am able to get dates where there were people in rooms on certain days within the range of dates.
SELECT date, IFNULL(surname, 'ghost')
FROM book JOIN dates ON book.date=dates.date JOIN people ON people.surname=book.surname
WHERE room = 113
AND date BETWEEN '2015-01-01' AND '2015-01-07'
+------------+-----------+
| Date | Surname |
+------------+-----------+
| 2015-01-02 | Surname 1 |
| 2015-01-05 | Surname 2 |
+------------+-----------+
How do I return a full list of dates, including those where there was nobody in a room so every date from given range is printed and if no people in the specified room, then 'ghost' is printed instead?

You need to use a LEFT JOIN
SELECT dates.date, IFNULL(book.surname, 'ghost')
FROM dates
LEFT JOIN book
ON book.date=dates.date
AND book.room = 113
-- LEFT JOIN people ON people.surname=book.surname
WHERE dates.date BETWEEN '2015-01-01' AND '2015-01-07'
Note that book.room = 113 needs to be in the ON clause. Using it in the WHERE clause would convert the LEFT JOIN to INNER JOIN.

Related

SQL scenario to group a dimension

I am using SQL and here is the scenario I am stuck with
Table A
SubscriptionID | Club | Sum_Of_Billings_Of_All_Month | EventID
Table B:
Cost | EventID
Table A left join Table B on EventID (To track the cost of each subscription)
Result
SubscriptionID | Club | Sum_Of_Billings_Of_All_Month | EventID | Cost
I want to break down the Sum_Of_Billings_Of_All_Month into per day billings. i.e. for each subscription, I want the breakdown of billings per day of the month. For that I will group by Sum_Of_Billings_Of_All_Month instead of subscriptionID.
Consequence
If I group by days of billing, upon joining I will get duplicates of subscriptionIDs and the eventIDs will be duplicated in rows multiple time which will then count the cost multiple times as well.
What I want:
In the same query, I want to be able to group by SubscriptionID so I can keep the unique subscriptions per row with their cost.
But one of the other requirement is that I want to know the breakdown of the billings as well to calculate the breakeven and other metrics in the same query
Here is the actual sample data
Table A
idCustomerSubscription | ClubID | EventId | BillingDate| FinalRevenue
33562784 | 56180001| 5y6m600np1fg | 5/31/2017 | 512
Table B
EventId |Cost
5y6m600np1fg |200
Table A join B left join on eventId (Group by SubscriptionID
idCustomerSubscription |ClubID |EventId |BillingDate| FinalRevenue | Cost
33562784 | 56180001 |5y6m600np1fg| 5/31/2017 | 512 |200
It serves the purpose because each subscription has one cost which is unique BUT it on the other hand, this kind of query will not give me breakdown of dates of billings (I need it for the breakeven calculation)
Table A join B left join on eventId (Group by billingdate
idCustomerSubscription| ClubID |EventId | BillingDate| FinalRevenue |Cost
33562784 | 56180001| 5y6m600np1fg |5/30/2017 |510 |200
33562784 | 56180001| 5y6m600np1fg |5/31/2017 |2 |200
This would give me the breakdown of the dates which i need for breakeven (510 on 30th and 2 on 31st) but it will make the cost duplicated (400 is the cost instead of 200)
I want to find out a SQL magic where I can keep the number of unique subcriptions per row and some way to track the billing dates of each subscriptions in the same query (without grouping it by date because it will make the rows duplicated). Is it possible ?
Perhaps some way where when the eventids are joined and its grouped by date, it doesnt duplicate the cost and count only one cost per eventid?
I hope you will get your desired result with below query. Try to modify your group by operation for mn (alias name) table.
select mn.idCustomerSubscription,mn.ClubID,mn.EventId,mn.BillingDate,mn.FinalRevenue,sq.cost
from tableA Mn left join (select idCustomerSubscription,max(cost) cost, min(billingDate) billingDate from tableA a left join tableB b on a.eventid=b.eventid group by idCustomerSubscription) sq on mn.idCustomerSubscription=sq.idCustomerSubscription and mn.billingDate=sq.billingdate
group by mn.idCustomerSubscription,mn.ClubID,mn.EventId,mn.BillingDate

MySQL query to get data between and not between where clause

This MySQL query is to get data from two tables exam_dates and section_strength and join them to get total persons from which sections has completed there exam in march quarter:
select s.section, count(section), r.strength
from exam_dates s
left join section_strength r
on s.section=r.section_name
where s.semester_exam between '2017-01-01' and '2017-03-31'
group by s.section
But this query is showing only those sections name who have completed the exam in march quarter. I want all the name of sections in left side and number of persons completed next column and then the strength of that section.
first table contains=> details of student completed exam on "so and so dates"
example:
id| personal_no| section_name| semester_dates
1 | 777878 | hrm |2017-01-12
2 | 748587 |it |2017-05-10
another table having strength of individual sections:
id|section_name |strength
1 | hrm | 10
2 | it | 15
Now I want my query to show result of all sections name in left side and then total student completed test in march quarter and then total strength of that section.
for eg.
id|section_name|total_completed|strength
1 | hrm | 2 | 10
2 | it | 5 | 15
Your second date is before your first date. Try reversing the order of the dates:
select s.section, count(section), r.strength
from exam_dates s
left join section_strength r on s.section=r.section_name
where s.semester_exam between '2017-03-31' and '2017-01-01'
group by s.section

merging two queries

I have a table of recipes, and I want to show a weekly value for each of them. The values are votes cast for them. My problem is that I want to make an excel-like table with all available fridays on my db, add a column for each recipe, and put it's value for the friday on that column, if any value exists.
Now apparently the easiest join doesn't work so I wrote two queries: one to get all ids for my recipes and one for the values to show. The first (MySql) query is just a select id from recipes, the second is like this:
select d.date,perc from
(SELECT date FROM weekly where YEAR(date)=2014 group by date) as d
left join weekly on d.date = weekly.date and weekly.id_rec= :idrec
Any idea how to merge those two queries? Running two queries makes everything slow down, but when I tried to merge them I didn't get the correct results.
Data:
sql fiddle
The result should be something like:
Dates | Recipe A | Recipe B | ...
Date 1 | 0.005 | 0.11 |
Date 2 | 0 | 0 |
Date 3 | 0 | 0.1 |
Note that Date 2 doesn't exist for Recipe A and B, but for some other do.
You should be able to merge the two queries like this:
SELECT recipes.id, votes.date, votes.perc FROM recipes
RIGHT JOIN
(select weekly.id_rec, d.date, perc from
(SELECT weekly.id_rec, date FROM weekly where YEAR(date) = 2014 group by date) as d left join weekly on d.date = weekly.date) as votes
ON votes.id_rec = recipes.id
SQL Fiddle

select particular items ordered n times

i have two tables, one invoice and the other the details where i need to select products ordered n times by a particular customer within a date range
the tables in part looks like this
Invoice
invid | custid | invdate
----------------------------
101 | 11 | 2014-2-10
102 | 22 | 2014-2-15
103 | 22 | 2014-3-01
104 | 11 | 2014-3-14
Details
invid | item
------------
101 | bread
102 | bread
103 | chips
104 | chips
102 | bread
103 | bread
104 | chips
101 | bread
from the code above, i need to select say all customers who ordered the same items 2 times or more within 2014-2-10 and 2014-3-09, excluding any customer who purchased the same item in the week 2014-3-10 to 2014-3-14
for example
if customer orders bread 2 times between date1 and date2 and did not order the same bread between date3 and date4 then it should be in the output
and date the expected output should be
custid | item | item_count
22 | bread | 2
the custid 11 would have NOT fit the list, because they also purchased in the week 2014-3-10 to 2014-3-14, but it they did not purchased the same item in the passed dates
this is what i tried
SELECT
i.custid, d.ITEM,COUNT(d.ITEM) as orders
From `details` d
LEFT JOIN `invoices` i on i.invid= d.invid
WHERE
i.invdate >= '2014-2-10' AND
i.invdate <= '2014-3-14' AND
i.custid NOT IN
(SELECT custid FROM `invoices` WHERE invdate >= '2014-3-10')
Group By i.invid, d.ITEM
HAVING COUNT(d.ITEM) >= 2
when i run again the full table, i get 1 item instead of 6. I did manually using excel through a number of functions to be sure, in this case none
Typical MySQL error. You mistakenly group by invid instead of custid.
SELECT
i.custid, d.ITEM, COUNT(d.ITEM) as orders
From `details` d
LEFT JOIN `invoices` i on i.invid= d.invid
WHERE
i.invdate >= '2014-2-10' AND
i.invdate <= '2014-3-14' AND
i.custid NOT IN
(SELECT custid FROM `invoices` WHERE invdate >= '2014-3-10')
Group By i.custid, d.ITEM
HAVING COUNT(d.ITEM) >= 2;
EDIT: Okay, here is a closer look at it.
Correct the GROUP BY as already mentioned.
You outer join invoices although there should be no details record without an invoices record. Change this to INNER JOIN.
You are confusing dates. The purchase date shall be between '2014-2-10' and '2014-3-09' and must not be between '2014-3-10' to '2014-3-14'
Then: You don't want to exclude customers who bought something in that latter week. You want to exclude customer-item combinations that occured then.
My suggestion: select from both date ranges and check then if all macthes for a customer-item combination are within the desired week and still have a count of at least two:
select
i.custid,
d.item,
count(d.item) as orders
from invoices i
inner join details d on d.invid = i.invid
where i.invdate between '2014-2-10' and '2014-3-09'
or i.invdate between '2014-3-10' and '2014-3-14'
group by i.custid, d.item
having count(*) >= 2 and max(i.invdate) between '2014-2-10' and '2014-3-09;
SELECT i1.custid, d1.ITEM, COUNT(*) orders
FROM (invoices i1 JOIN details d1 USING (invid))
LEFT JOIN (invoices i2 JOIN details d2 USING (invid))
ON i2.custid = i1.custid
AND d2.ITEM = d1.ITEM
AND i2.invdate BETWEEN '2014-03-10' AND '2014-03-14'
WHERE i1.invdate BETWEEN '2014-02-10' AND '2014-03-09'
AND i2.custid IS NULL
GROUP BY i1.custid, d1.ITEM
HAVING orders >= 2
See it on sqlfiddle.

Summing Records in 2 MySQL fields From Same Table and Use It As a Condition

I am working on a project, and part of it requires me to be able to display records of individuals who have completed repayment. To complete repayment they must have made monthly repayments and a lump sum. So I want to be able to sum the records in an individual's repayment amount field and lumpsum field, indentified by his loanid. Here is the sample table
Repayment table
loanid | repayment_amount | lumpsum | memberid
21 20,000 30,000 45
loan table
loanid | amountdue | memberid
21 40,000 45
Member table
memberid | first_name | middle_name | last_name
21 John Tom Bun
Here is an SQL query I have written in MySQL
SELECT first_name, last_name
FROM repayment,loan, members
WHERE repayment.loanid = loan.loanid AND repayment.memberid = members.memberid
WHERE (SUM(amount)+ SUM(lumpsum)) >= loan.amountdue
I can't seem to get this to display what I want, a list of folks where the sum of their amount and lumpsum is greater than the amount due.
Kindly help
SELECT first_name, last_name
FROM repayment,loan, members
WHERE
repayment.loanid = loan.loanid AND
repayment.memberid = members.memberid
GROUP BY first_name, last_name
HAVING
SUM(repayment.repayment_amount) + SUM(repayment.lumpsum) >= SUM(loan.amountdue)
SELECT m.first_name, m.last_name
FROM repayment r,loan l, members m
WHERE r.loanid = l.loanid AND r.memberid = m.memberid
HAVING (SUM(r.repayment_amount)+ SUM(r.lumpsum)) >= l.amountdue