Simple outer join between two tables not working - sql

I have a very simple query , and yet I can't get it to work like I want it to.
I have 2 tables, A and B which are very similar and look like this :
A :
+------+----------+---------+
| a_id | a_cnt_id | a_value |
+------+----------+---------+
| 1 | 848 | 0.5 |
| 2 | 848 | 3 |
| 3 | 848 | 4 |
| 4 | 848 | 65 |
+------+----------+---------+
B :
+------+----------+---------+
| b_id | b_cnt_id | b_value |
+------+----------+---------+
| 1 | 849 | 36 |
| 2 | 849 | 42 |
| 3 | 849 | 8 |
+------+----------+---------+
B has more records than A for a given set of {a_cnt_id, b_cnt_id}.
I would like my query to return this :
+------+------+---------+---------+
| a_id | b_id | a_value | b_value |
+------+------+---------+---------+
| 1 | 1 | 0.5 | 36 |
| 2 | 2 | 3 | 42 |
| 3 | 3 | 4 | 8 |
| 4 | NULL | 65 | NULL |
+----+--------+---------+---------+
My (not working) query, because it returns only the first 3 rows :
select distinct a.a_id, b.b_id, a.a_value, b.b_value
from b
full join a on b.b_id = a.a_id
where a.a_cnt_id = 849
and b.b_cnt_id = 848;

Adding a WHERE clause will filter the results to what the where suggests. So if you have where a.a_cnt_id = 849 it will get only these rows, not the ones with the null. Move your filters to the join:
select distinct a.a_id, b.b_id, a.a_value, b.b_value
from b
full join a on b.b_id = a.a_id
and a.a_cnt_id = 849
and b.b_cnt_id = 848;

Move the cnt_id checks to the ON clause instead to preserve the OUTER JOIN, like so:
select distinct a.a_id, b.b_id, a.a_value, b.b_value
from b
full join a on b.b_id = a.a_id
and a.a_cnt_id = 849
and b.b_cnt_id = 848;

I don't remember where I found this but here you go:
EDIT:
The link of the image belongs to Visual-Representation-of-SQL-Joins. Thanks #jyparask

Related

Group-by if all field results are true

I have this query:
SELECT
tbl_ord.ord.table,
tbl_ord.ord.n_ord,
player.confirm
FROM
tbl_ord
INNER JOIN
tbl_players ON tbl_ord.player_id = tbl_players.player_ids
WHERE
id_shop = 3
ORDER BY
n_ord ASC
result:
+-----------+-------+---------+
| ord_table | n_ord | confirm |
+-----------+-------+---------+
| 10 | 2 | 1 |
| 10 | 2 | 0 |
| 8 | 3 | 1 |
| 8 | 3 | 1 |
| 4 | 5 | 1 |
| 4 | 5 | 1 |
+-----------+-------+---------+
I'd like get only result with all confirmed users by group-by on ord_table
+-----------+-------+---------+
| ord_table | n_ord | confirm |
+-----------+-------+---------+
| 4 | 5 | 1 |
| 8 | 3 | 1 |
+-----------+-------+---------+
Thanks!
You can probably use not exists:
SELECT o.ord_table, o.ord.n_ord,
1 as confirm
FROM tbl_ord o
WHERE NOT EXISTS (SELECT 1
FROM tbl_players p
WHERE o.player_id = p.player_ids AND
?.id_shop = 3 AND -- not sure what table this comes from
p.confirm = 0
)
ORDER BY o.n_ord ASC;
The advantage of this approach is that it avoids aggregating at the outer level. This, in turn, means that it can make better use of indexes, including using an index to potentially avoid sorting.
Use HAVING:
SELECT
tbl_ord.ord.table,
tbl_ord.ord.n_ord,
MIN(player.confirm)
FROM tbl_ord
INNER JOIN tbl_players ON tbl_ord.player_id = tbl_players.player_ids
WHERE id_shop = 3
GROUP BY tbl_ord.ord.table, tbl_ord.ord.n_ord,
HAVING MIN(player.confirm) = 1
ORDER BY n_ord ASC

SQL Question Looking Up Value in Same Table

Trying to use a self join in SQL to look up a value in the table and apply it.
Her's what I got:
+-----------------+-----+--------+-----------+
| Acutal Output | | | |
+-----------------+-----+--------+-----------+
| TRKID | Fac | NewFac | BAG_TRKID |
| 449 | 11 | 11 | 999 |
| 473 | 11 | 11 | 737 |
| 477 | 11 | 11 | 737 |
| 482 | 11 | 11 | 737 |
| 737 | 89 | 89 | |
| Desired Out Put | | | |
| TRKID | Fac | NewFac | BAG_TRKID |
| 449 | 11 | 11 | 999 |
| 473 | 11 | 89 | 737 |
| 477 | 11 | 89 | 737 |
| 482 | 11 | 89 | 737 |
| 737 | 89 | 89 | |
+-----------------+-----+--------+-----------+
Here's the code below. I can't seem to get the table that I want. The Bag TrkID's Facility Num is not becoming the TrkID New Facility Num.
Select
TABLEA.TRKID,
TABLEA.FAC,
NVL(TABLEA.FAC, TABLEB.FAC) as NEWFAC,
TABLEA.BAG_TRKID
FROM
(
Select
HSD. TRKID,
HSD.NLPT as FAC,
SBPD.BAG_TRKID
From
HSD
LEFT JOIN
SBPD
ON
SBPD.BAG_TRKID = HSD. TRKID
Where
HSD.SCANDT BETWEEN ‘Yesterday’ and ‘Today’
) TABLEA
LEFT JOIN
(
Select
HSD. TRKID,
HSD.NLPT as FAC,
SBPD.BAG_TRKID
From
HSD
LEFT JOIN
SBPD
ON
SBPD.BAG_TRKID = HSD. TRKID
Where
HSD.SCANDT BETWEEN ‘Yesterday’ and ‘Today’
) TABLEB
ON
TABLEA.TRKID = TABLEB.BAG_TRKID
Perhaps something like
select a.TrkID, a."Facility Number", a.BAG_TRKID, b.TrkID as "NEW Fac"
from tbl a
left join tbl b on (a.TrkID = b.trk_id_reference)
Given the limited information that you've shared, I was able to achieve the expected output with the following query:
SELECT a.TrkID, a.facility_number, a.bag_trkid, b.facility_number as new_facility_number
FROM test_tbl AS a
LEFT JOIN test_tbl AS b ON a.bag_trkid = b.trkid OR (a.bag_trkid IS NULL AND b.trkid = a.trkid);
You want to get the new_facility_number for a row based on its bag_trkid (which can be achieved by this: LEFT JOIN test_tbl AS b ON a.bag_trkid = b.trkid).
BUT the trick is to account for the cases when the Left Table (which I refer as a) does not have a bag_trkid. In this case, we will keep the new_facility_number to be the same as a.facility_number, joining the tables on the trkid solely: OR (a.bag_trkid IS NULL AND b.trkid = a.trkid)

Select / group by multiple columns but count just the values of one column

I have 2 tables.
Table #1: orders
order_id | crit_1 | crit_2 | crit_3 | other
01 | A00 | GER | 49er | x
02 | A00 | GER | 49er | x
03 | A00 | USA | 49er | x
04 | C80 | DEN | 66er | x
05 | B50 | GER | 99er | x
The table orders has 3 important criteria but doesn't have the criterion_4. There is another table with the order_positions which contains multiple criterion_4 entries for each order_id.
Table #2: classifications
crit_1 | crit_2 | crit_3 | crit_4 | class_1 | class_2
A00 | GER | 49er | 4711 | A | 11
A00 | GER | 49er | 4712 | A | 21
A00 | USA | 49er | 4711 | D | 12
A00 | USA | 49er | 4712 | D | 21
B50 | GER | 99er | 4801 | B | 12
B50 | GER | 99er | 4802 | B | 12
B50 | GER | 99er | 4803 | B | 14
C80 | DEN | 66er | 4904 | C | 22
C80 | DEN | 66er | 4905 | C | 21
The table classifications contains classifications for:
orders = class_1 = combination of crit_1, crit_2 & crit_3
order_positions = class_2 = combination of crit_1, crit_2, crit_3
& crit_4
I have a query where I join classifications.class_1 on the table orders to create a list of all orders and their respective classification.
select
orders.order_id,
orders.crit_1,
orders.crit_2,
orders.crit_3,
classifications.class_1
from
orders
left join
classifications
on
orders.crit_1=classifications.crit_1 and
orders.crit_2=classifications.crit_2 and
orders.crit_3=classifications.crit_3
where
orders.others = "..."
group by
orders.order_id,
orders.crit_1,
orders.crit_2,
orders.crit_3,
classifications.class_1
I need a GROUP BY at the end since the table classifications contains multiple entries with the combination of crit_1, crit_2 and crit_3. But this isn't a problem since the needed classification_1 is always the same for each combination of crit_1, crit_2 and crit_3.
Now I want to create another query where I count just the number of each classification_1 for the orders. Something like this:
class_1 | number
A | 12
B | 5
C | 18
. | .
But I don't know how without the whole selection of orders.order_id, orders.crit_1, orders.crit_2, orders.crit_3 and classifications.class_1
I just want to count the class_1 classifications for the query above.
Any suggestions?
edit
I tried it like suggested by Kaushik Nayak:
select
--orders.order_id,
--orders.crit_1,
--orders.crit_2,
--orders.crit_3,
classifications.class_1,
count(*)
from
orders
left join
classifications
on
orders.crit_1=classifications.crit_1 and
orders.crit_2=classifications.crit_2 and
orders.crit_3=classifications.crit_3
where
orders.others = "..."
group by
--orders.order_id,
--orders.crit_1,
--orders.crit_2,
--orders.crit_3,
classifications.class_1
But the results are not correct and I have no idea how to reproduce those numbers.
A few examples:
| class_1 | query w/ | query w/o | query |
| | group by | group by | count(*) |
---------------------------------------------
| A | 654 | 2179 | 1024 |
| B | 371 | 1940 | 667 |
| C | 94 | 238 | 247 |
When I use my query with group by then I get 654 entries for class_1 = A.
When I make my query without group bythen I get 2179 entries for class_1= A.
And when I try the query with Count(*) then I get 1024 entries for class_1 = A.
The last one is definitely not correct.
Just use GROUP BY class_1 for your classifications table and add an EXISTS condition to check if there is an order.
SELECT
c.class_1,
COUNT(c.class_1) "number"
FROM
classifications c
WHERE
EXISTS (
SELECT
1
FROM
orders o
WHERE
o.crit_1 = c.crit_1
AND o.crit_2 = c.crit_2
AND o.crit_3 = c.crit_3
)
GROUP BY
c.class_1
ORDER BY
1;

Using an outerjoin to find where all corresponding values for a tuple are zero

I have the following table data (e0 is the primary key):
+-----+----+----+----+----+
| e0 | e1 | e2 | e3 | e4 |
+-----+----+----+----+----+
| 111 | 2 | 5 | 7 | 0 |
| 222 | 2 | 5 | 7 | 0 |
| 333 | 3 | 6 | 8 | 7 |
| 444 | 1 | 3 | 2 | 2 |
| 555 | 1 | 3 | 2 | 0 |
| 666 | 1 | 3 | 2 | 0 |
| 777 | 6 | 3 | 4 | 0 |
| 888 | 6 | 3 | 4 | 0 |
| 999 | 6 | 3 | 4 | 0 |
+-----+----+----+----+----+
This is part of an exercise where I need to use an outerjoin to find which tuples of (e1,e2,e3) have ALL corresponding values of e4 as 0 (i.e. the query has to return (2,5,7) and (6,3,4)). I've tried a few solutions, but all of them still include (1,3,2) which is not meant to happen.
Does anybody have an idea for an outerjoin that would return (2,5,7) and (6,3,4)?
I would just use NOT EXISTS but to express that using outer joins you can use the below.
SELECT DISTINCT a.e1,
a.e2,
a.e3
FROM data a
LEFT OUTER JOIN data b
ON a.e1 = b.e1
AND a.e2 = b.e2
AND a.e3 = b.e3
AND b.e4 <> 0
WHERE b.e1 IS NULL
SQL Fiddle
And the NOT EXISTS method
SELECT DISTINCT a.e1,
a.e2,
a.e3
FROM data a
WHERE NOT EXISTS (SELECT *
FROM data b
WHERE a.e1 = b.e1
AND a.e2 = b.e2
AND a.e3 = b.e3
AND b.e4 <> 0)
SQL Fiddle
I'm not sure if this actually gives the desired results (semantically); but it doesn't use an OUTER JOIN at all:
SELECT e1, e2, e3 FROM (
SELECT e0, e1, e2, e3, e4, COUNT(*) AS c FROM data
GROUP BY e1, e2, e3
HAVING c > 1
) AS b
WHERE b.e4 = 0
It does give the rows and columns you specify from your data set; but I'm not sure I'm understanding the question quite right.
Why do you need to use an OUTER JOIN? Is this equivalent to Martin Smith's answer?

Add Values to Grouping Column

I am having a lot of trouble with a scenario that I think some of you might have come across.
(the whole thing about Business Trips, two tables, one filled with payments done on Business trips, and the other is about the Business Trips, so the first one has more Rows than the other, (there are more Payments that happened than Trips))
I have two tables, Table A and Table B.
Table A looks as follows
| TableA_ID | TableB_ID | PaymentMethod | ValuePayed |
| 52 | 1 | Method1 | 23,2 |
| 21 | 1 | Method2 | 23,2 |
| 33 | 2 | Method3 | 23,2 |
| 42 | 1 | Method2 | 14 |
| 11 | 14 | Method1 | 267 |
| 42 | 1 | Method2 | 14,7 |
| 13 | 32 | Method1 | 100,2 |
Table B looks like this
| TableB_ID | TravelExpenses | OperatingExpense |
| 1 | 23 | 12 |
| 1 | 234 | 24 |
| 2 | 12 | 7 |
| 1 | 432 | 12 |
| 14 | 110 | 12 |
I am trying to create a measure Table (Table C) that looks like this:
| TableC_ID | TypeofCost | Amount |
| 1 | Method1 | 100,2 |
| 2 | Method2 | 52 |
| 3 | TravelExpenses | 7 |
| 4 | OperatingExpense| 12 |
| 5 | Method3 | 12 |
| 6 | OperatingExpense| 7 |
| 7 | Method3 | 12 |
(the Amount results are to be Summed and Columns - Employee, Month, TypeofCost Grouped)
So I pretty much have to group not only by the PaymentMethod which I get from table A,
but also insert new values in the group (TravelExpenses and OperatingExpense)
Can anybody give me any Idea about how this can be done in SQL ?
Here is what I have tried so far
SELECT PaymentMethod as TypeofCost
,Sum(ValuePayed) as Amount
FROM TableA Left Outer Join TableB on TableA.TableB_ID = TableB.TableB_ID
GROUP PaymentMethod
UNION
SELECT 'TravelExpenses' as TypeofCost
,Sum(TableB.TravelExpenses) as Amount
FROM TableA Left Outer Join TableB on TableA.TableB_ID = TableB.TableB_ID
GROUP PaymentMethod
UNION
SELECT 'OperatingExpense' as TypeofCost
,Sum(TableB.OperatingExpense) as Amount
FROM TableA Left Outer Join TableB on TableA.TableB_ID = TableB.TableB_ID
GROUP PaymentMethod
It should be something like this:
Select
row_number() OVER(ORDER BY TableB_ID) as 'TableC_ID',
u.TypeofCost,
u.Amount
from (
Select
a.TableB_ID,
a.PaymentMethod as 'TypeofCost',
SUM(a.ValuePayed) as 'Amount'
from
Table_A as a
group by a.TableB_ID, a.PaymentMethod
union
Select
b1.TableB_ID,
'TravelExpenses' as 'TypeofCost',
SUM(b1.TravelExpenses) as 'Amount'
from
Table_B as b1
group by b1.TableB_ID
union
Select
b2.TableB_ID,
'OperatingExpenses' as 'TypeofCost',
SUM(b2.OperatingExpenses) as 'Amount'
from
Table_B as b2
group by b2.TableB_ID
) as u
EDIT: Generate TableC_ID