MySQL - Joining three tables to get the SUM of a column - mysql

I have a database that has 3 tables. 1st table hosts the users id and position. The 2nd table hosts the boss/squire id. The 3rd hosts the squire id and its money. Suppose I am table1.id=1. I am a pos1 with 2 squire, table1.id=2 with pos1 and table1.id=3 with pos1, where in this 2 has squires under them. table1.id=4 with pos2 and table1.id=5 with pos3 is both under table1.id=2. Same with table1.id=3.
table1.id=1 ------> table1.id=2 ------> table1.id=4
| |
| |
| ------> table1.id=5
|
------> table1.id=3 ------> table1.id=6
|
|
------> table1.id=7
I want the result be
sumTotal = 1000
CREATE TABLE Table1 (`id` INT, `pos` VARCHAR(255))
CREATE TABLE Table2 (`id` INT, `id_boss` INT, `id_squire` INT)
CREATE TABLE Table3 (`id` INT, `id_squire` INT, `money` INT)
INSERT INTO Table1 (`id`, `pos`)
VALUES
(1, 'pos1'),
(2, 'pos1'),
(3, 'pos1'),
(4, 'pos2'),
(5, 'pos2'),
(6, 'pos3'),
(7, 'pos3');
INSERT INTO Table2 (`id`, `id_boss`, `id_squire`)
VALUES
(1, 1, 2),
(2, 1, 3),
(4, 2, 4),
(5, 2, 5),
(6, 3, 6),
(7, 3, 7);
INSERT INTO Table3 (`id`, `id_squire`, `money`)
VALUES
(1, 4, 100),
(2, 5, 200),
(3, 6, 300),
(4, 7, 400);
I will be using table1.id=1
to help understand pls click: http://sqlfiddle.com/#!9/e8924/4/0

You first need a self join to Table2 so that you get the descendant nodes:
SELECT SUM(money)
FROM Table2 AS t21
JOIN Table2 AS t22 ON t21.id_squire = t22.id_boss
JOIN Table3 AS t3 ON t22.id_squire = t3.id_squire
WHERE t21.id_boss = 1
Demo here
Edit:
If you want to also include Table1 in the query then you can put it at the beginning of the JOIN chain:
SELECT SUM(t3.money)
FROM Table1 AS t1
JOIN Table2 AS t21 ON t1.id = t21.id_boss
JOIN Table2 AS t22 ON t21.id_squire = t22.id_boss
JOIN Table3 AS t3 ON t22.id_squire = t3.id_squire
WHERE t1.id = 1

Related

mySQL - SUM and COUNT and JOIN to display all records [duplicate]

This question already has answers here:
How can I do a FULL OUTER JOIN in MySQL?
(15 answers)
Closed 4 years ago.
This is a following question to this one Join two tables with SUM and COUNT.
What I try to do is to have all values displayed as some are in history table and not in rota table or vice-versa (999 and 777)
So my tables are:
create table history (
code int(10) primary key,
PN varchar(10) not null,
Qty int(10) not null,
LOC_ID int(10));
insert into history values (1, 'T1', 1, 1);
insert into history values (2, 'A1', 2,2);
insert into history values (3, 'J1', 3,3);
insert into history values (4, 'A2', 1,4);
insert into history values (5, 'J2', 2,1);
insert into history values (6, 'A3', 3,2);
insert into history values (7, 'J3', 4,3);
insert into history values (8, 'T1', 5,4);
insert into history values (9, 'A1', 1,1);
insert into history values (10, '999', 3,2);
insert into history values (11, 'J2', 4,3);
insert into history values (12, 'A1', 3,4);
insert into history values (13, 'J2', 5,1);
create table rota (
code int(10) primary key,
PN varchar(10) not null,
SN varchar(10) not null,
LOC_ID int(10));
insert into rota values (1, 'T1', 't1a',1);
insert into rota values (2, 'A1', 'a1a',2);
insert into rota values (3, 'J1', 'j1a',3);
insert into rota values (4, 'A2', 'a2a',4);
insert into rota values (5, 'J2', 'j2a',1);
insert into rota values (6, 'A3', 'a3a',2);
insert into rota values (7, 'J3', 'j3a',3);
insert into rota values (8, '777', 't1b',4);
insert into rota values (9, 'A1', 'a1b',1);
insert into rota values (10, 'J2', 'j2b',2);
insert into rota values (11, 'J2', 'j2c',3);
insert into rota values (12, 'A1', 'a1c',4);
insert into rota values (13, 'J2', 'j2d',1);
insert into rota values (14, 'J2', 'j2e',2);
insert into rota values (15, 'J2', 'j2f',3);
create table loca (
code1 int(10) primary key,
LOC varchar(10) not null);
insert into loca values (1, 'AAA');
insert into loca values (2, 'BBB');
insert into loca values (3, 'CCC');
insert into loca values (4, 'DDD');
The code I have got is
select CASE WHEN a.pn IS NULL THEN b.pn ELSE a.pn END AS PN
, a.q
, b.c
, a.LOC_ID
, b.LOC_ID
from
(select
h.pn
, sum(qty) q
, h.LOC_ID
from
history h
group by h.pn, h.LOC_ID) a
RIGHT JOIN
(select
r.pn
, count(sn) c
, r.LOC_ID
from
rota r
group by r.pn, r.LOC_ID) b
on a.pn = b.pn WHERE a.LOC_ID = b.LOC_ID
order by a.pn;
The above code works great for all PN that are in both tables. The problem is for values that are specific to one of the tables. I can remove the WHERE clause from JOIN but it is not corect. The question is - how to get all PNs from history and rota where some of them are present i just one table. I had some luck with RIGHT JOIN but that did not cover unique values from the other table. Any one came across solution before?
Results shoud look like the following table
PN LOC_ID Count Qty
T1 1 1 1
A1 2 1 2
J1 3 1 3
A2 4 1 1
J2 1 2 2
A3 2 1 3
J3 3 1 4
777 4 1 NULL
A1 1 1 1
J2 2 2 NULL
J2 3 2 4
A1 4 1 3
J2 1 2 2
J2 2 2 NULL
J2 3 2 4
999 2 NULL 3
use another join and that is left and make them union
select t.PN,t.q,t.c,t.LOC_ID,t.LOC_ID_b from
(
select CASE WHEN a.pn IS NULL THEN b.pn ELSE a.pn END AS PN
, a.q
, b.c
, a.LOC_ID
, b.LOC_ID as LOC_ID_b
from
(select
h.pn
, sum(qty) q
, h.LOC_ID
from
history h
group by h.pn, h.LOC_ID) a
RIGHT JOIN
(select
r.pn
, count(sn) c
, r.LOC_ID
from
rota r
group by r.pn, r.LOC_ID) b
on a.pn = b.pn and a.LOC_ID = b.LOC_ID
) as t
union
select t2.PN,t2.q,t2.c,t2.LOC_ID,t2.LOC_ID_b from
(
select CASE WHEN a.pn IS NULL THEN b.pn ELSE a.pn END AS PN
, a.q
, b.c
, a.LOC_ID
, b.LOC_ID as LOC_ID_b
from
(select
h.pn
, sum(qty) q
, h.LOC_ID
from
history h
group by h.pn, h.LOC_ID) a
left JOIN
(select
r.pn
, count(sn) c
, r.LOC_ID
from
rota r
group by r.pn, r.LOC_ID
) b
on a.pn = b.pn and a.LOC_ID = b.LOC_ID
) t2
http://sqlfiddle.com/#!9/c20c81/20

MySQL: Select first row with value in interval

With the following table:
CREATE TABLE table1 (`id` INT, `num` INT);
INSERT INTO table1 (`id`, `num`) VALUES
(1, 1),
(1, 5),
(1, 7),
(1, 12),
(1, 22),
(1, 23),
(1, 24),
(2, 1),
(2, 6);
How do I select a row for each num interval of 5 (ie. select the first row for [0,5), the first for [5,10), the first for [10,15), etc.), with a given id? Is this possible with a MySQL query, or must I process it in a programming language later?
For reference, the output I'd want for id=1:
(1, 1), (1,5), (1,12), (1,22)
Here is a short query:
select min(num), ceiling((num + 1)/5)
from table1
where id = 1
group by ceiling((num + 1)/5);

How to get combined column values from multiple tables with different columns

I am trying to combine multiple columns from three tables. I could do it using UNION ALL keyword but I am feeling this query what I use is not probably the most efficient
For example:
create table tbl1
(id int, act varchar(50), stk varchar(50), price int, vol int, amt float);
insert into tbl1 values
(1, 'a1', 's1', 10, 5, 50),
(2, 'a1', 's2', 5, 5, 25),
(3, 'a2', 's1', 15, 3, 45),
(4, 'a2', 's2', 20, 2, 40),
(5, 'a2', 's2', 20, 2, 40);
create table tbl2 (id int, tid int, price int, vol int, amt float);
insert into tbl2 values
(1, 1, 5, 3, 15),(2, 1, 5, 1, 5),(3, 1, 15, 1, 15),
(4, 2, 5, 3, 15),(5, 2, 6, 2, 12);
create table tbl3 (id int, act varchar(10), type int, amt float);
insert into tbl3 values
(1, 'a1', 0, 10),(2, 'a1', 1, 15),
(3, 'a2',1, 5),(4, 'a3',0, 5);`
The query I used
SELECT act,stk,amtFROM tbl1
UNION ALL
SELECT
(select act from tbl1 where tbl2.tid = tbl1.id) amt,
(select stk from tbl1 where tbl2.tid = tbl1.id) stk,
amt
from tbl2
Is there a way to get the same without using inner select queries twice? could someone please give me the efficient query?
here is the Fiddle
Expected output (amt from all three tables where act='a1')
ACT STK AMT
a1 s1 50
a1 s2 25
a1 s1 15
a1 s1 5
a1 s1 15
a1 s1 10
a1 s1 15
Just use an explicit join:
SELECT act, stk, amt
FROM tbl1
UNION ALL
SELECT t1.act as amt, t1.stk, t2.amt
from tbl2 join
tbl1
on tbl2.tid = tbl1.id;

How to select only non-zero values from multiple table conditional sum query

I am having a tough time to eliminate the rows having zero values in particular expression, any help here highly appericiated
Here are my two simple tables
create table tbl1
(id int, account varchar(50), stock varchar(50), price int, vol int);
insert into tbl1 values
(1, 'a1', 's1', 10, 5),
(2, 'a1', 's2', 5, 5),
(3, 'a2', 's1', 15, 3),
(4, 'a2', 's2', 20, 2),
(5, 'a2', 's2', 20, 2);
create table tbl2
(id int, tid int, price int, vol int);
insert into tbl2 values
(1, 1, 5, 3),
(2, 1, 5, 1),
(3, 1, 15, 1),
(4, 2, 5, 3),
(5, 2, 6, 2);
My select is as follows, it gives me what I need but it also gives me the rows where (t1.vol - ifnull(Sum(t2.vol), 0)) returns zero
select
t1.id,account,stock,
(t1.vol - ifnull(Sum(t2.vol), 0)) vol
from tbl1 t1
left join tbl2 t2 on t1.id=t2.tid
group by t1.id
Could somebody help me in getting rid of these zero values?
I tried having (t1.vol - ifnull(Sum(t2.vol), 0)) <> 0 ==> it says vol is invalid column
I tried where (t1.vol - ifnull(Sum(t2.vol), 0)) <> 0 ==> it says Invalid use of group function
here is the output I get now with the above query
ID ACCOUNT STOCK VOL
1 a1 s1 0
2 a1 s2 0
3 a2 s1 3
4 a2 s2 2
5 a2 s2 2
SOLUTION:
select
t1.id,account,stock,
(t1.vol - ifnull(Sum(t2.vol), 0)) vol
from tbl1 t1
left join tbl2 t2 on t1.id=t2.tid
group by t1.id
having vol <> 0
You can modify your query like below
select t1.id,
t1.account,
t1.stock,
(t1.vol - coalesce(tab.vol_total,0)) as vol
from tbl1 t1
left join
(
select tid,Sum(vol) as vol_total
from tbl2
group by tid
) tab
on t1.id=tab.tid
where (t1.vol - coalesce(tab.vol_total,0)) > 0

SELECT data based on result of previous row in table

I have a database of students.
CREATE TABLE classlist
(`id` int, `studentid` int, `subjectid` int, `presentid` int)
;
CREATE TABLE student
(`id` int, `name` varchar(4))
;
CREATE TABLE subject
(`id` int, `name` varchar(4))
;
CREATE TABLE classStatus
(`id` int, `name` varchar(8))
;
INSERT INTO classlist
(`id`, `studentid`, `subjectid`, `presentid`)
VALUES
(1, 111, 1, 1),
(2, 222, 3, 0),
(3, 333, 2, 1),
(4, 111, 4, 1),
(5, 111, 1, 0),
(6, 222, 3, 0),
(7, 333, 2, 1),
(8, 111, 4, 1),
(9, 111, 2, 0),
(10, 111, 4, 1),
(11, 111, 1, 1),
(12, 333, 3, 1),
(13, 333, 2, 1),
(14, 333, 3, 1)
;
INSERT INTO student
(`id`, `name`)
VALUES
(111, 'John'),
(222, 'Kate'),
(333, 'Matt')
;
INSERT INTO subject
(`id`, `name`)
VALUES
(1, 'MATH'),
(2, 'ENG'),
(3, 'SCI'),
(4, 'GEO')
;
INSERT INTO classStatus
(`id`, `name`)
VALUES
(0, 'Absent'),
(1, 'Present')
;
And I have a query which shows how many times they have been present or absent.
SELECT
studentid,
students.name AS NAME,
SUM(presentid = 1) AS present,
SUM(presentid = 0) AS absent
FROM classlist
INNER JOIN student as students ON classlist.studentid=students.id
GROUP BY studentid, NAME
See this fiddle below.
http://sqlfiddle.com/#!2/fe0b0/1
There seems to be a trend from looking at this sample data that after someone attends subjectid 4 they are often not coming to the next class. How can I capture this in a query. I want to ONLY show data WHERE last subjectid =4. So in my sample data rows matching my criteria would be.
(5, 111, 1, 0),
(9, 111, 2, 0),
(11, 111, 1, 1),
as these rows are all the next row of a studentid who had a subjectid=4.
My output would be
| STUDENTID | NAME | PRESENT | ABSENT|
| 111 | John | 1 | 2 |
To get the next class for a student, use a correlated subquery:
select cl.*,
(select min(cl2.id) from classlist cl2 where cl2.studentid = cl.studentid and cl2.id > cl.id) as nextcl
from classlist cl
Plugging this into your query example tell you you who is present and absent for the next class:
SELECT students.id, students.name AS NAME,
SUM(cl.presentid = 1) AS present, SUM(cl.presentid = 0) AS absent,
sum(clnext.presentid = 1) as presentnext
FROM (select cl.*,
(select min(cl2.id) from classlist cl2 where cl2.studentid = cl.studentid and cl2.id > cl.id) as nextcl
from classlist cl
) cl INNER JOIN
student as students
ON cl.studentid = students.id left outer join
classlist clnext
on cl.nextcl = clnext.id
GROUP BY students.id, students.NAME
Add a where cl.subjectid = 4 to get the answer for subject 4.
I fixed the query. The SQLFiddle is k.
A quick and dirty solution could be to get the Classlist.Id for all lines where subjectid=4 (let's call them n) then select all the lines where Id = n+1