Why outer join query not working? - sql

Hallo,
my objective is to generate a table that shows the total of each CODE that belong to the owner, take note that each owner must have a CODE tied to it no matter the TOTAL value is zero. So there will be APP, REJ, CAN tied to each of the APPROVAL_ID.
APPROVAL_ID CODE TOTAL
----------- ---- -----
101 APP 2
101 REJ 1
101 CAN 3
102 APP 2
102 REJ 4
102 CAN 0
103 APP 0
103 REJ 0
103 CAN 4
Thus, here is the source code:
select approval_id, code, total
from (
select 'APP' code, '1' seq from dual
union all
select 'REJ' code, '2' seq from dual
union all
select 'CAN' code, '3' seq from dual
)
left outer join (
select m.approval_id, own.name, m.decision, count(*) total,
case own.channel
when 'CH1' then 'CH1'
when 'CH2' then 'CH2'
else 'Others Channel'
end the_channel
from tableM m, owner own
where m.decision in ('REJ', 'APP', 'CAN')
and own.id=m.approval_id
group by m.approval_id, own.name, m.decision, own.channel
order by m.approval_id
)
on code=decision
group by approval_id, code, total
order by approval_id;
The output from the above query is like below:
APPROVAL_ID CODE TOTAL
----------- ---- -----
101 APP 2
101 REJ 1
101 CAN 3
102 APP 2
102 REJ 4
103 CAN 4
The output of the inner query is like below:
APPROVAL_ID CODE TOTAL
----------- ---- -----
101 APP 2
101 REJ 1
101 CAN 3
102 APP 2
102 REJ 4
103 CAN 4
Something was not right to the query because I know that some of the row is having total value of zero, it should print something like (null) value in it. But why does it hidden from the view? Is there anything wrong to my query?
THanks #!

First, you need to do a cross join between your owner and your code table.
Then you do the left join.
I have modelised 3 table : Type for your 3 lines APP, REJ and CAN, then a user table, equivalent to your owner table, and a third table decision, equivalent to your tableM.
The query looks like this :
SELECT c.user_id, c.type_code, COUNT(d.id)
FROM
(
SELECT t.ID as type_id, u.id as user_id, t.CODE as type_code
FROM Type t, Userr u
) c
LEFT OUTER JOIN Decision d
ON d.user_id = c.user_id
AND d.type_id = c.type_id
GROUP BY c.user_id, c.type_code
Not tested but for yours set of table :
select a.id_own, a.code, count(m.approval_id)
from
(
select code, own.id as id_own
from (
select 'APP' code, '1' seq from dual
union all
select 'REJ' code, '2' seq from dual
union all
select 'CAN' code, '3' seq from dual
) , owner own
) a
left outer join tableM m
on a.code = m.decision
and a.id_own = m.approval_id
group by a.id_own, a.code
order by a.id_own
Note that the count(m.approval_id) will give you the number of approval_id that appear not null in the left join.

Your outer join is on code=decision. That means you get one row for each codes which don't occur as decision on the right side. Obviously you want to do your left join with a cross join of 3 codes and all distinct APPROVAL_IDs giving all the combinations of code and APPROVAL_ID on the left side.

My first guess is that
group by approval_id, code, total
should be
group by approval_id, code

Related

Oracle SQL - join two tables + show unmatched results

I have two tables:
POSITION_TABLE
Account
Security
Pos_Quantity
1
A
100
2
B
200
TRADE_TABLE
Account
Security
Trade_Quantity
1
A
50
2
C
10
I want to join them in a way that matching rows are displayed as one row, but unmatching rows are also displayed, so standard LEFT JOIN wouldnt work.
Expected output:
Account
Security
Pos_Quantity
Trade_Quantity
1
A
100
50
2
B
200
0
2
C
0
10
How do I do that?
A full outer join would work nicely here:
with position_table as (select 1 account, 'A' security, 100 pos_quantity from dual union all
select 2 account, 'B' security, 200 pos_quantity from dual),
trade_table as (select 1 account, 'A' security, 50 trade_quantity from dual union all
select 2 account, 'C' security, 10 trade_quantity from dual)
select coalesce(pt.account, tt.account) account,
coalesce(pt.security, tt.security) security,
coalesce(pt.pos_quantity, 0) pos_quantity,
coalesce(tt.trade_quantity, 0) trade_quantity
from position_table pt
full outer join trade_table tt on pt.account = tt.account
and pt.security = tt.security
order by account,
security;
db<>fiddle - note how you can see that the full outer join works just fine with subqueries defined in a where clause!

SQL help i need to find the inventory remaining in my office

In sql help i have 3 tables, table one is asset table which is as follow
id
asset_code
asset_name
asset_group
asset_quantity
1
A001
demo asset
4
5
2
A002
demo asset 2
6
3
and another table is asset_allocation
id
asset_id
allocated_quantity
allocated_location
1
1
2
IT office
2
1
1
main hall
the last table is asset_liquidated which will present assets that are no longer going to be used
id
asset_id
liquidated_quantity
1
1
2
2
1
1
lets say i have 5 computers and i have allocated 3 computers and 1 is no longer going to be used so i should be remaining with 1 computer so now how do i make sql auto generate this math for me
You need to use aggregation and the join your tables -
SELECT id, asset_code, asset_name, asset_group, asset_quantity,
asset_quantity - COALESCE(AA.allocated_quantity, 0) - COALESCE(AL.liquidated_quantity, 0) available_quantity
FROM asset A
LEFT JOIN (SELECT asset_id, SUM(allocated_quantity) allocated_quantity
FROM asset_allocation
GROUP BY asset_id) AA ON A.id = AA.asset_id
LEFT JOIN (SELECT asset_id, SUM(liquidated_quantity) liquidated_quantity
FROM asset_liquidated
GROUP BY asset_id) AL ON A.id = AL.asset_id
This query will give you -1 as available_quantity for asset_id 1 as you have only 5 available, 3 of them are allotted and 3 are liquidated as per your sample data.
Please see if this helps
SELECT
asset_quantity AS Total_Assets
,ISNULL(allocated_quantity, 0) allocated_quantity
,ISNULL(liquidated_quantity, 0) liquidated_quantity
FROM asset
LEFT OUTER JOIN (
SELECT
asset_id, SUM(allocated_quantity) AS allocated_quantity
FROM asset_allocation
GROUP BY asset_id
) asset_allocation2
ON asset_allocation2.asset_id = asset.id
LEFT OUTER JOIN (
SELECT
asset_id, SUM(liquidated_quantity) AS liquidated_quantity
FROM asset_liquidated
GROUP BY asset_id
) asset_liquidated 2
ON asset_liquidated 2.asset_id = asset.id

Oracle SQL: Retrieving a record more than once

I'm using Oracle 11 and would like to be able to retrieve a record more than one in a query, which would be a good convenience saving for the next part of my code.
Let's consider this SQL statement:
SELECT ID, NAME FROM PEOPLE WHERE NAME IN ('Alice', 'Bob', 'Alice');
It returns this data:
| 1 | Alice |
| 2 | Bob |
What I'd really like to do is to un-uniquify that list and return the records with duplicates, in the order given. So the above statement would be:
| 1 | Alice |
| 2 | Bob |
| 1 | Alice |
I appreciate that Oracle is optimized to remove repetition like this, and I could re-use the data afterwards, keep it in a store object and retrieve by name etc. I was just wondering if there was a way to make this happen on the database itself.
Oracle has a couple of handy built-in functions that return lists of arguments that you can then transform to a table and join on it. In your case, odcivarchar2list can be used to return a list of varchar2s:
SELECT p.*
FROM TABLE(sys.odcivarchar2list('Alice', 'Bob', 'Alice')) dups
JOIN people p ON p.name = dups.column_value*
query below for record with duplicate
select x.id,x.name from (
select a.id,a.name from people a where a.name in ('Alice')
union all
select a.id,a.name from people a where a.name in ('Bob')
union all
select a.id,a.name from people a where a.name in ('Alice')
) x
Late to the party but just wanted to add you can use a traditional table expression:
select p.id, p.name
from (
select 'Alice' as name from dual
union all select 'Bob' from dual
union all select 'Alice' from dual
) searched s
join people p on p.name = s.name;
Here's another idea:
WITH cteNumbers as (SELECT LEVEL AS N
FROM DUAL
CONNECT BY LEVEL <= 2),
PEOPLE AS (SELECT 'Bob' AS NAME, 111 AS EMPID FROM DUAL UNION ALL
SELECT 'Carol' AS NAME, 222 AS EMPID FROM DUAL UNION ALL
SELECT 'Ted' AS NAME, 333 AS EMPID FROM DUAL UNION ALL
SELECT 'Alice' AS NAME, 444 AS EMPID FROM DUAL)
SELECT *
FROM PEOPLE p
CROSS JOIN cteNumbers
WHERE 1 = CASE
WHEN NAME = 'Alice' THEN 1
WHEN NAME = 'Bob' AND N = 1 THEN 1
WHEN NAME = 'Ted' AND N < 4 THEN 1
WHEN NAME = 'Carol' AND N = 3 THEN 1
ELSE 0
END
ORDER BY NAME, N
Basically, use cteNumbers to generate a list of number (in this case, from 1 to 2 - adjust the CONNECT BY LEVEL condition to control how many numbers are generated), then use the CASE expression in the WHERE clause to control the circumstances under which a particular record's repetitions are selected.
SQLFiddle here

Get separate count for each condition within group

I am trying to get a view of table information from and Oracle 10g table that lists the counts of specific values of a column in their own columns with each row being the group value.
for example:
The first select is :
SELECT processed_by, count(priority) as P2
FROM agreement_activity
WHERE priority = '2'
GROUP BY processed_by
Which outputs:
PROCESSED_BY P2
------------------------------ ----------
Alicia 2
Christine 2
The second select is:
SELECT processed_by, count(priority) as P1
FROM agreement_activity
WHERE priority = '1'
GROUP BY processed_by
Which outputs:
PROCESSED_BY P1
------------------------------ ----------
Bonita 2
Alicia 6
Christine 2
What I am looking for is to output those values as the following:
PROCESSED_BY P1 P2
------------------------------ ---------- ----------
Bonita 2
Alicia 6 2
Christine 2 2
Is that possible?
You can use sum with the case expression to get conditional count:
select processed_by
, sum(case when priority = 1 then 1 else 0 end) as P1
, sum(case when priority = 2 then 1 else 0 end) as P2
from agreement_activity
group by processed_by
P.S. If you don't care that P1 or P2 maybe null instead of 0 you can omit else in both expressions.
this is how I implement the sql. I'm using firebird code but I think you can convert the code into your sql
SELECT
a.equipmentid,
a.name equipname,
w1.countwarranty1 ,
w2.countwarranty2
FROM TBL_EQUIPMENTMST a
inner JOIN
(select c.equipmentid, count(c.WARRANTYID) countwarranty1 from tbl_equipwarranty c where c.serviceproduct='1' group by c.equipmentid) w1
ON w1.equipmentid = a.equipmentid
inner JOIN
(select d.equipmentid, count(d.WARRANTYID) countwarranty2 from tbl_equipwarranty d where d.serviceproduct='2' group by d.equipmentid) w2
ON w2.equipmentid = a.equipmentid
inner JOIN
(select e.equipmentid, count(e.equiplocationid) countlocation from tbl_equiplocation e group by e.equipmentid) w3
ON w3.equipmentid = a.equipmentid
this is the output
My warranty table has only 2 warranty for equipment each that's why its only showing 2.
you can also inner join to the same table with different count.
as you can see my warranty has multiple warranty where serviceproduct is different on each table
if I edit your code it will be like this
SELECT a.processed_by, b.priority as p2, c.priority as p1
FROM agreement_activity a
inner join
(
SELECT w1.processed_by, count(w1.priority) as P2 FROM agreement_activity w1
WHERE w1.priority = '2' GROUP BY w1.processed_by
) b
on a.processed_by = b.processed_by
inner join
(
SELECT w2.processed_by, count(w2.priority) as P2 FROM w2.agreement_activity
WHERE w2.priority = '1' GROUP BY w2.processed_by
) c
on a.processed_by = c.processed_by
test it

Join a table with one table if condition1 is true and with another table if condition1 is false?

I have the following tables :
User_Group
id group_id group_type
------------------------
1 100 A
1 100 B
2 101 B
2 102 A
Group_A
id name
---------
100 A
101 B
102 C
Group_B
id name
---------
100 D
101 E
102 F
I want the group names of all users (using array.agg()). We have to get the group name from group A if the user's group type = A and from group B if the user's group type = B. The result should be :
userid groups
--------------
1 A,D
2 E,C
I have created a fiddle for this, and given a solution using union of 2 separate queries. Can it be done without the union, something in which I can decide on which table to pick the group name from with a single join of user_groups, group_A and group_B ?
select ug.id, array_agg(
case ug.group_type
when 'A' then g_a.name
when 'B' then g_b.name
else 'N/A'
end)
from user_groups ug
left outer join group_A g_a on ug.group_id = g_a.id
left outer join group_B g_b on ug.group_id = g_b.id
group by ug.id
SQL Fiddle Example
You can do this without union using left joins (I'd advise using explicit joins anyway since implicit joins are 20 years out of date Aaron Bertrand has written a good blog as to why). The Group_Type can become a join condition meaning the table is only joined when the right group type is found:
SELECT ug.ID, ARRAY_AGG(COALESCE(a.Name, b.Name))
FROM User_Groups ug
LEFT JOIN group_A a
ON a.ID = ug.group_ID
AND ug.Group_Type = 'A'
LEFT JOIN group_B b
ON b.ID = ug.group_ID
AND ug.Group_Type = 'B'
WHERE COALESCE(a.ID, b.ID) IS NOT NULL -- ENSURE AT LEAST ONE GROUP IS MATCHED
GROUP BY ug.ID;
However I would be inclined to use a UNION Still, but move it as follows:
SELECT ug.ID, ARRAY_AGG(Name)
FROM User_Groups ug
INNER JOIN
( SELECT 'A' AS GroupType, ID, Name
FROM Group_A
UNION ALL
SELECT 'B' AS GroupType, ID, Name
FROM Group_B
) G
ON g.GroupType = ug.Group_Type
AND g.ID = ug.Group_ID
GROUP BY ug.ID;
Your Fiddle with my queries added