Related
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
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
So...this is a little confusing. I have 2 tables, one is basically a list of Codes and Names of people and topics and then a value, for example:
The second table is just a list of topics, with a value and a "result" which is just a numerical value too:
Now, what I want to do is do a LEFT OUTER JOIN on the first table, matching on topic and value, to get the "Result" field from the second table. This is simple in the majority of cases because they will almost always be an exact match, however there will be some cases there won't be, and in those cases the problem will be that the "Value" in table 1 is lower than all the Values in table 2. In this case, I would like to simply do the JOIN as though the Value in table 1 equalled the lowest value for that topic in table 2.
To highlight - the LEFT OUTER JOIN will return nothing for Row 2 if I match on topic and value, because there's no Geography row in table 2 with the Value 30. In that case, I'd like it to just pick the row where the value is 35, and return the Result field from there in the JOIN instead.
Does that make sense? And, is it possible?
Much appreciated.
You can use Cross Apply here. There may be a better solution performance wise.
declare #people table(
Code int,
Name varchar(30),
Topic varchar(30),
Value int
)
declare #topics table(
[Subject] varchar(30),
Value int,
Result int
)
INSERT INTO #people values (1, 'Doe,John', 'History', 25),
(2, 'Doe,John', 'Geography', 30),
(3, 'Doe,John', 'Mathematics', 45),
(4, 'Doe,John', 'Brad Pitt Studies', 100)
INSERT INTO #topics values ('History', 25, 95),
('History', 30, 84),
('History', 35, 75),
('Geography', 35, 51),
('Geography', 40, 84),
('Geography', 45, 65),
('Mathematics', 45, 32),
('Mathematics', 50, 38),
('Mathematics', 55, 15),
('Brad Pitt Studies', 100, 92),
('Brad Pitt Studies', 90, 90)
SELECT p.Code, p.Name,
case when p.Value < mTopic.minValue THEN mTopic.minValue
else p.Value
END, mTopic.minValue
FROM #people p
CROSS APPLY
(
SELECT [Subject],
MIN(value) as minValue
FROM #topics t
WHERE p.Topic = t.Subject
GROUP BY [Subject]
) mTopic
I am also assuming that:
This is simple in the majority of cases because they will almost always be an exact match, however there will be some cases there won't be, and in those cases the problem will be that the "Value" in table 1 is lower than all the Values in table 2.
is correct. If there is a time when Value is not equal to any topic values AND is not less than the minimum, it will currently return the people.value even though it is not a 'valid' value (assuming topics is a list of valid values, but I can't tell from your description.)
Also technically you only need that case statement in the select statement, not the following mTopic.minValue but I thought the example showed the effect better with it.
Another method of performing this is by using a temporary table to hold the different values.
First insert the exact matches, then insert the non-exact matches that where not found in the initial select and finally grab all the results from the temp table. This solution is more code than the other, so just adding it as an alternative.
Example (SqlFiddle):
Schema first
create table students
( code integer,
name varchar(50),
topic varchar(50),
value integer );
create table subjects
( subject varchar(50),
value varchar(50),
result integer );
insert students
( code, name, topic, value )
values
( 1, 'Doe, John', 'History', 25),
( 2, 'Doe, John', 'Geography', 30),
( 3, 'Doe, Jane', 'Mathematics', 45),
( 4, 'Doe, Jane', 'Brad Pitt Studies', 100);
insert subjects
( subject, value, result )
values
( 'History', 25, 95 ),
( 'History', 30, 84 ),
( 'History', 35, 75 ),
( 'Geography', 35, 51 ),
( 'Geography', 40, 84 ),
( 'Geography', 45, 65 ),
( 'Mathematics', 45, 32 ),
( 'Mathematics', 50, 38 ),
( 'Mathematics', 55, 15 ),
( 'Brad Pitt Studies', 100, 92 ),
( 'Brad Pitt Studies', 90, 90 );
The actual SQL query:
-- Temp table to hold our results
create temporary table tempresult
( code integer,
name varchar(50),
topic varchar(50),
studentvalue integer,
subjectvalue integer,
result integer );
-- Get the exact results
insert tempresult
( code,
name,
topic,
studentvalue,
subjectvalue,
result )
select stu.code,
stu.name,
stu.topic,
stu.value as 'student_value',
sub.value as 'subject_value',
sub.result
from students stu
join
subjects sub on sub.subject = stu.topic
and sub.value = stu.value;
-- Get the non-exact results, excluding the 'students' that we already
-- got in the first insert
insert tempresult
( code,
name,
topic,
studentvalue,
subjectvalue,
result )
select stu.code,
stu.name,
stu.topic,
stu.value as 'student_value',
sub.value as 'subject_value',
sub.result
from students stu
join
subjects sub on sub.subject = stu.topic
-- Business logic here: Take lowest subject value that is just above the student's value
and sub.value = (select min(sub2.value)
from subjects sub2
where sub2.subject = stu.topic
and sub2.value > stu.value)
where not exists (select 1
from tempresult tmp
where tmp.code = stu.code
and tmp.name = stu.name
and tmp.topic = stu.topic)
-- Get our resultset
select code,
name,
topic,
studentvalue,
subjectvalue,
result
from tempresult
order by code,
name,
topic,
studentvalue,
subjectvalue,
result
In this case I would make two joins instead of one. Something like this:
select *
from Table1 T1
LEFT JOIN Table2 T2 on T1.Topic=T2.subject and T1.Value=T2.VALUE
LEFT JOIN Table2 as T3 on T1.Topic=T3.Subject and T1.Value<T2.Value
The do a case to choose the table to take values from. If T2.value is null then use T3.Value ELSE T2.Value. Hope this helps you
A left join is not called for in the requirements. You want to join when T1.Subject = T2.Topic and then either when T1.Value = T2.Value or when T1.Value < T2.Value and T2.Value is the smallest value. Just write it out that way:
select p.*, t.Result
from #People p
join #Topics t
on t.Subject = p.Topic
and( t.Value = p.Value
or( p.Value < t.value
and t.Value =(
select Min( Value )
from #Topics
where Subject = t.Subject )));
Which generates:
Code Name Topic Value Result
---- -------- ----------------- ----- ------
1 Doe,John History 25 95
2 Doe,John Geography 30 51
3 Doe,John Mathematics 45 32
4 Doe,John Brad Pitt Studies 100 92
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;
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