I have 3 tables a, b, and c.
Table a contains the ids of the stores, their earning date, and count of sale (flight tickets).
Table b contains the id, sale date, and count of clothing orders.
Table c contains the id, date and total count.
SQL> select * from a;
STOREID EARNINGDATE COUNT_FLIGHT_TICKETS
-------------------- ----------- ----------------
store01 14980000 10
store01 14980001 32
store02 14980000 134
SQL> select * from b;
STOREID EARNINGDATE CLOTHES_SALE_COUNT
-------------------- ----------- ---------------
store01 14980000 6
store02 14980000 6
SQL> select * from c;
STOREID EARNINGDATE TOTAL_SALE_COUNT
-------------------- ----------- -------------
store01 14980001 32
store01 14980000 16
store02 14980000 134
Given above the tables, I have to print all the stores ids, with their date of earning for total sale, flight sale, and clothing sale.
|StoreId | EarningDate | FlightCount | ClothingCount | TotalCount |
I have used below query, but failing to get the above.
select b.storeId , sum(a.COUNT_FLIGHT_TICKETS),
sum(b.CLOTHES_SALE_COUNT), sum (c.TOTAL_SALE_COUNT)
from a
full outer join b on a.storeId = b.storeId
and a.EarningDate = b.earningdate
full outer join c on a.storeId = c.storeId
and a.earningDate = b.earningDate group by a.storeId;
This query does not give all the rows and having some bug.
STOREID flight clothing total
------ --------- --------- --------------------
store02 134 6 134
store01 52 12 48
Can someone help me to correct this query to get the expected output?
One option would be to take the UNION of the thee tables and then aggregate by store:
SELECT
t.STOREID,
t.EARNINGDATE,
SUM(t.COUNT_FLIGHT_TICKETS) AS FlightCount,
SUM(t.CLOTHES_SALE_COUNT) AS ClothingCount,
SUM(t.TOTAL_SALE_COUNT) AS TotalCount
FROM
(
SELECT
STOREID,
EARNINGDATE,
COUNT_FLIGHT_TICKETS,
0 AS CLOTHES_SALE_COUNT,
0 AS TOTAL_SALE_COUNT
FROM a
UNION ALL
SELECT STOREID, EARNINGDATE, 0, CLOTHES_SALE_COUNT, 0
FROM b
UNION ALL
SELECT STOREID, EARNINGDATE, 0, 0, TOTAL_SALE_COUNT
FROM c
) t
GROUP BY
t.STOREID,
t.EARNINGDATE
This gets around the join problem you correctly pointed out, which might require a full outer join. Full outer join in MySQL is a hassle, and in any case it usually should not be necessary with good design.
Demo here:
Rextester
Assuming that for each table, (storeId, earningdate) is unique or compound keys, group by will be unnecessary.
You can try this query.
select
IF(isnull(a.STOREID), IF(isnull(b.STOREID), c.STOREID, b.STOREID),a.STOREID) as StoreId,
IF(isnull(a.EARNINGDATE), IF(isnull(b.EARNINGDATE), c.EARNINGDATE, b.EARNINGDATE),a.EARNINGDATE) as EarningDate,
IF(isnull(COUNT_FLIGHT_TICKETS),0,COUNT_FLIGHT_TICKETS) as FlightCount,
IF(isnull(CLOTHES_SALE_COUNT),0,CLOTHES_SALE_COUNT) as ClothingCount,
IF(isnull(TOTAL_SALE_COUNT),0,TOTAL_SALE_COUNT) as TotalCount
from a full outer join b
on a.storeId = b.storeId and a.EarningDate = b.earningdate
full outer join c
on a.storeId = c.storeId and a.earningDate = c.earningDate;
Result was:
STOREID EarningDate flight clothing total
------ ------------- --------- --------- --------------------
store01 14980000 10 6 16
store02 14980000 134 6 134
store01 14980001 32 0 32
Is this your expected result?
I think you forgot the earning date.
Given above the tables, I have to print all the stores ids, with their
date of earning for total sale, flight sale, and clothing sale.
|StoreId | EarningDate | FlightCount | ClothingCount | TotalCount |
Related
This is my sql query to get the following table below :
select c.name, s.company, p.qty, p.qty * p.price as Total
from client c, purchase p, stock s
where c.clno = p.clno AND s.company = p.company
group by c.name, s.company, p.qty, p.qty * p.price
order by sum(p.qty) desc
The output of the above query looks like this :
Name | Company | Qty | Total
John ABC 12 100
Bob XYZ 10 150
John ABC 5 50
Bob XYZ 20 250
Bob XYZ 2 20
Nav QRS 10 150
John ABC 10 150
I want to have the query to get the output as the following :
Name | Company | Qty | Total
John ABC 27 300
Bob XYZ 32 420
Nav QRS 10 150
As of now your query uses GROUP BY but does not actually aggregates data. You want to GROUP BY name and company, and SUM the quantities and amounts, like :
select c.name, s.company, SUM(p.qty), SUM(p.qty * p.price) as Total
from client c
inner join purchase p on c.clno = p.clno
inner join stock s on s.company = p.company
group by c.name, s.company
order by Total desc
Other remarks regarding your query :
always use explicit joins instead of implicit ones
you can use column aliases in the ORDER BY clause (here, Total ; this can make the query easier to read
I have 2 tables in MySQL(InnoDB). The first is an employee table. The other table is the expense table. For simplicity, the employee table contains just id and first_name. The expense table contains id, employee_id(foreign key), amount_spent, budget, and created_time. What I would like is a query that returns the percentage of their budget spent for the most recent X number of expense they've registered.
So given the employee table:
| id | first_name
-------------------
1 alice
2 bob
3 mike
4 sally
and the expense table:
| id | employee_id | amount_spent | budget | created_time
----------------------------------------------------------
1 1 10 100 10/18
2 1 50 100 10/19
3 1 0 40 10/20
4 2 5 20 10/22
5 2 10 70 10/23
6 2 75 100 10/24
7 3 50 50 10/25
The query for the last 3 trips would return
|employee_id| first_name | percentage_spent |
--------------------------------------------
1 alice .2500 <----------(60/240)
2 bob .4736 <----------(90/190)
3 mike 1.000 <----------(50/50)
The query for the last 2 trips would return
|employee_id| first_name | percentage_spent |
--------------------------------------------
1 alice .3571 <----------(50/140)
2 bob .5000 <----------(85/170)
3 mike 1.000 <----------(50/50)
It would be nice if the query, as noted above, did not return any employees who have not registered any expenses (sally). Thanks in advance!!
I'll advise you to convert datatype of created_time as DATETIME in order to get accurate results.
As of now, I've assumed that most recent id indicates most recent spents as it's what sample data suggests.
Following query should work (didn't tested though):
select t2.employee_id,t1.first_name,
sum(t2.amount_spent)/sum(t2.budget) as percentage_spent
from employee t1
inner join
(select temp.* from
(select e.*,#num := if(#type = employee_id, #num + 1, 1) as row_number,
#type := employee_id as dummy
from expense e
order by employee_id,id desc) temp where temp.row_number <= 3 //write value of **n** here.
) t2
on t1.id = t2.employee_id
group by t2.employee_id
;
Click here for DEMO
Feel free to ask doubt(s), if you've any.
Hope it helps!
If you are using mysql 8.0.2 and higher you might use window function for it.
SELECT employee_id, first_name, sliding_sum_spent/sliding_sum_budget
FROM
(
SELECT employee_id, first_name,
SUM(amount_spent) OVER (PARTITION BY employee_id
ORDER BY created_time
RANGE BETWEEN 3 PRECEDING AND 0 FOLLOWING) AS sliding_sum_spent,
SUM(budget) OVER (PARTITION BY employee_id
ORDER BY created_time
RANGE BETWEEN 3 PRECEDING AND 0 FOLLOWING) AS sliding_sum_budget,
COUNT(*) OVER (PARTITION BY employee_id
ORDER BY created_time DESC) rn
FROM expense
JOIN employee On expense.employee_id = employee.id
) t
WHERE t.rn = 1
As mentioned by Harshil, order of row according to the created_time may be a problem, therefore, it would be better to use date date type.
I'm a bit lost in this MySQL query problem which involves counting each instance of cust.category grouped by the branch which delivered the customer's order. If a customer has multiple orders in the same month, the cust.category is added according to how many orders they might have made. Is it possible to limit the counting of cust.category only to unique users?
Sample table
Customer table
id name category nearestbranch created
------ ------ --------- ------------- -------------------
1 John Engineer 3 2014-09-10 18:10:10
2 Mary Developer 4 2014-09-10 18:10:10
-------------------------------------------------------------
Orders table
id delivery_date customer_id
------ ------------- -----------
1 2014-09-01 1
2 2014-09-02 2
3 2014-09-03 1
-----------------------------------
My query:
SELECT cust.nearestbranch,
SUM(IF(cust.category = 'Engineer', 1, 0)) eng,
SUM(IF(cust.category = 'Developer', 1, 0)) dev
FROM customers cust
LEFT JOIN orders ON cust.id = orders.customer_id
WHERE DATE_FORMAT(orders.delivery_date, '%Y-%m') = '2014-09'
GROUP BY cust.nearestbranch
But instead of getting this (eng = 1)
nearestbranch eng dev
------------- ------ ------
3 1 0
4 0 1
I get this (eng = 2)
nearestbranch eng dev
------------- ------ ------
3 2 0
4 0 1
Use count(distinct) with case so for every category you will have count of distinct users
SELECT cust.nearestbranch,
COUNT(distinct case when cust.category = 'Engineer' then customer_id end) eng,
COUNT(distinct case when cust.category = 'Developer'then customer_id end) dev
FROM customers cust
LEFT JOIN orders ON cust.id = orders.customer_id
WHERE DATE_FORMAT(orders.delivery_date, '%Y-%m') = '2014-09'
GROUP BY cust.nearestbranch
Demo
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.
I have three tables as :
ab, a, and b
Table a and b should have multiple occurrences for the same touid.
SELECT * FROM ab
tourid tourname
------ ------------
100 hundred
110 one ten
120 one twenty
select * from a;
imageid tourid filesize
------- ------ ----------
1 100 10
2 100 20
SELECT * FROM b
uid tourid filesize
------ ------ ----------
5 100 5
sql query :
SELECT
a.tourid,
SUM(a.filesize) AS a_sum,
SUM(b.filesize) AS b_sum
FROM ab
LEFT JOIN a ON a.tourid=ab.tourid
LEFT JOIN b ON b.tourid=ab.tourid
WHERE ab.tourid=100
gives the result as:
tourid a_sum b_sum
------ ------ --------
100 30 10
But result should be as :
tourid a_sum b_sum
------ ------ --------
100 30 5
result of b_sum column is wrong
In the first table, you have one row :
tourid tourname
100 hundred
Joining the a table will produce 2 rows :
tourid tourname imaageid filesize
100 hundred 1 10
100 hundred 2 20
Joining the b table will keep 2 rows :
tourid tourname imaageid filesize uid filesize
100 hundred 1 10 5 5
100 hundred 2 20 5 5
the good query is :
select tourid, sum_a, sum_b
from ab
left join (select tourid, sum(filesize) as sum_a from a group by tourid) a on ab.tourid = a.tourid
left join (select tourid, sum(filesize) as sum_b from b group by tourid) b on ab.tourid = b.tourid
You need to add GROUP BY ab.tourid to overcome duplicates, there are repeated results therefore you are getting the sum as twice
SELECT
a.tourid,
SUM(a.filesize) AS a_sum,
SUM(b.filesize) AS b_sum
FROM ab
LEFT JOIN a ON a.tourid=ab.tourid
LEFT JOIN b ON b.tourid=ab.tourid
WHERE ab.tourid=100
GROUP BY ab.tourid
The counts are correct: since you are joining to both tables at once, the results before aggregation will look like this:
tourid a.filesize b.filesize
------ ---------- ----------
100 10 5
100 20 5
You get the same row of b for each joined row in a.
When you total up a.filesize, you get 30; when you total up b.filesize, you get 10, explaining the results.
You can get the results that you wish without joins or aggregations - simple subqueries would be sufficient:
SELECT
100
, (SELECT SUM(filesize) FROM a WHERE tourid=100) as sum_a
, (SELECT SUM(filesize) FROM b WHERE tourid=100) as sum_a