two left outer join not working Oracle sql - sql

There are four table in the query
Table a contains a_id , a_name
Table a_tl contains a_tl_id , a_id , language_id , a_disp_name
Table b contains b_id , a_id , b_name
Table b_tl contains b_tl_id , b_id , language_id , b_disp_name
I want to do a left outer join on a and a_tl , a leftouter join on b and b_tl
and a inner join on the resultant tables .I wrote the following query
SELECT case a.a_disp_name
WHEN null THEN a.a_name
else a.a_disp_name
end AS a_name ,
case b.b_disp_name
WHEN null THEN b.b_name
else b.b_disp_name
end AS b_name ,
a_id ,
b_id
FROM a ,
a_tl ,
b ,
b_tl
WHERE a.a_id = a_tl.a_id (+)
AND b.b_id = b_tl.b_id (+)
AND a_tl.language_id = 2
AND b_tl.language_id = 2
AND a.a_id= b.b_id
This query is working of the language_id is present in the database if for a particular value it is not present it will not work i.e. left outer join is not working

Looks like the problem is, that you are not using (+) for your language_id checks.
Your table is outer-joined, so language_id is NULL when no record is found, but then you check for language_id = 2, but ? language_id is NULL.
I also don't see where you use results from a_tl or b_tl, guess that's just a problem of your post, not your original query?
However, please use explicit joins instead of the old syntax. Once you are used to it, it is a lot easier to read and understand.
Your query could also benefit from using COALESCE (or NVL if you like):
SELECT COALESCE( a_tl.a_disp_name, a.a_name ) AS a_name,
COALESCE( b_tl.b_disp_name, b.b_name ) AS b_name,
a.a_id,
b.b_id
FROM a
JOIN b ON ( b.b_id = a.a_id )
LEFT JOIN a_tl ON ( a_tl.a_id = a.a_id AND a_tl.language_id = 2 )
LEFT JOIN b_tl ON ( b_tl.b_id = b.b_id AND b_tl.language_id = 2 )
Hope I got your question right, please ask if it does not work.

If you want a_tl.language_id = 2 you force an inner join(a_tl.language_id would never be null).
If you want to left join only with rows from a_tl with a_tl.language_id = 2, then write like this:
FROM a
join b on (a.a_id= b.b_id)
left join a_tl on ( a.a_id = a_tl.a_id and a_tl.language_id = 2)
left join b_tl on ( b.b_id = b_tl.b_id and b_tl.language_id = 2 )

Related

Select an ID where there is only one row and that row is a specific value

I have this query. There's a lot of joins because I am checking if an ID is linked to any of those tables.
Currently, this query shows me any ID's that are not linked to any of those tables. I would like to add to it so that it also shows any IDs that are linked to the d table, but only if there is only 1 row in the D table and the type in the D field is 'member'.
SELECT
c.ID,
c.location,
c.pb,
c.name,
c.surname
FROM c
LEFT JOIN l on c.rowno = l.rowno
LEFT JOIN d on c.rowno = d.rowno
LEFT JOIN t on c.rowno = t.rowno
LEFT JOIN cj ON (c.rowno = cj.rowno OR c.rowno = cj.rowno2)
LEFT JOIN dj ON c.rowno = d.rowno
LEFT JOIN lg ON c.rowno = lg.rowno
LEFT JOIN tj ON c.rowno = tj.rowno
WHERE
c.status != 'closed'
AND l.rowno IS NULL
AND d.rowno IS NULL
AND t.rowno IS NULL
AND cj.rowno IS NULL
AND dj.rowno IS NULL
AND lg.rowno IS NULL
AND tj.rowno IS NULL
My first thought is to just add
WHERE D.type = 'member'
But that gives me all IDs that have a row with D.type = member (they could have 10 rows with all different types, but as long as 1 of those has type = member it shows up). I want to see ID's that ONLY have d.type = member
I'm sorry if I'm wording this badly, I'm having trouble getting this straight in my head. Any help is appreciated!
I would use exists for all conditions except the one on the D table:
SELECT c.*
FROM c JOIN
(SELECT d.rownum, COUNT(*) as cnt,
SUM(CASE WHEN d.type = 'Member' THEN 1 ELSE 0 END) as num_members
FROM t
GROUP BY d.rownum
) d
ON c.rownum = d.rownum
WHERE c.status <> 'closed' AND
NOT EXISTS (SELECT 1 FROM t WHERE c.rowno = t.rowno) AND
NOT EXISTS (SELECT 1 FROM l WHERE c.rowno = l.rowno) AND
. . .
I find NOT EXISTS is easier to follow logically. I don't think there is a big performance difference between the two methods in SQL Server.

avoid repeating condition in select query

I have the following query to be executed on postgresql
SELECT COUNT(DISTINCT id_client) FROM contract c
INNER JOIN bundle b ON c.bundle_id = b.id
INNER JOIN payment_method pm ON pm.id = c.payment_method_id
WHERE country_id=1 AND b.platform_id=1 AND pm.name <> 'RIB'
AND CONDITION_1
AND id_client NOT IN (
SELECT id_client FROM contract c1
INNER JOIN bundle b1 ON (c1.bundle_id = b1.id)
INNER JOIN payment_method pm1 ON pm1.id = c1.payment_method_id
WHERE c1.country_id=1 AND b1.platform_id=1 AND pm1.name <> 'RIB'
AND CONDITION_2);
I don't like it because it's the same query repeated twice except of CONDITION_1 and CONDITION_2 (and I have another example where it's repeated 3 times).
It's also very slow as well.
I tried to rewrite it as the following:
WITH
filter_cpm AS (
SELECT * FROM contract c
INNER JOIN bundle b ON b.id = c.bundle_id
INNER JOIN payment_method pm ON pm.id = c.payment_method_id
WHERE c.country_id = 1 AND b.platform_id = 1 AND pm.name <> 'RIB'
)
SELECT COUNT(DISTINCT id_client) FROM filter_cpm
WHERE CONDITION_1
AND id_client NOT IN (
SELECT id_client FROM filter_cpm
WHERE CONDITION_2);
Now it's DRY but it's two times slower.
How can I re-write the query to have the same (or better) performance?
EDIT: I cannot join two conditions with AND. For example if CONDITION_1 and CONDITION_2 are VIP, then I want to select clients who were re-qualified from NOT VIP to VIP.
You can select from the common table expression twice, using an outer join:
WITH filter_cpm AS (SELECT *
FROM CONTRACT c
INNER JOIN BUNDLE b
ON b.ID = c.BUNDLE_ID
INNER JOIN PAYMENT_METHOD pm
ON pm.ID = c.PAYMENT_METHOD_ID
WHERE c.COUNTRY_ID = 1 AND
b.PLATFORM_ID = 1 AND
pm.NAME <> 'RIB')
SELECT COUNT(DISTINCT fc1.ID_CLIENT)
FROM filter_cpm fc1
LEFT OUTER JOIN filter_cpm fc2
ON fc2.ID_CLIENT = fc1.ID_CLIENT AND
CONDITION_2
WHERE fc1.CONDITION_1 AND
fc2.ID_CLIENT IS NULL
Best of luck.

sql: join two tables and display records even if they are in first table and not in the second table

I don't know if the title justifies the question but
i have 2 tables table A a and table B b. I am inner joining these two tables with
on (a.msisdn = b.frmsisdn or a.msisdn = b.tomsisdn)
in table A i have all the details of the users like names, msisdn, regdate, status, address etc
in table B i have user's transaction reports like timestamp, frmsisdn, tomsisdn, reference, amounts etc
Both tables have MSISDN as common but in Table A it is named as MSISDN and in Table B this can either be in FRMSISDN or TOMSISDN or will not be in any of the fields(usually users with no transactions).
But when i try to do like this, the report i am getting is not displaying the users who are in Table A with no entries in Table B, basically registered in the database but did not do any transactions as all.
Can someone tell me how do i include the MSISDN from Table A in the report even if they dont have an entry in Table B
Here is the query i am trying
select mai.msisdn, mai.firstname, mai.lastname, type as account_type,
alias as nickname, mai.regdate, mai.status,
amount as balance, count(trai.referenceid) as number_of_transactions,
sum(trai.amount) as sum_of_transactions from tableA mai
inner join tableC stk
on mai.msisdn = stk.msisdn
inner join tableB trai
on (mai.msisdn = trai.tomsisdn or mai.msisdn = trai.frmsisdn)
where trai.status = 0
and stk.walletid = 0
group by mai.msisdn, mai.firstname, mai.lastname, mai.type,
mai.alias, mai.regdate, mai.status,
stk.amount;
If you need all the entry of table A also when there si no enty in table B the use LEFT join for table B
select
mai.msisdn
, mai.firstname
, mai.lastname
, type as account_type
, alias as nickname
, mai.regdate
, mai.status
, amount as balance
, count(trai.referenceid) as number_of_transactions
, sum(trai.amount) as sum_of_transactions
from tableA mai
left join tableB trai on (mai.msisdn = trai.tomsisdn or mai.msisdn = trai.frmsisdn)
inner join tableC stk on mai.msisdn = stk.msisdn
where trai.status = 0
and stk.walletid = 0
group by
mai.msisdn
, mai.firstname
, mai.lastname
, mai.type
, mai.alias
, mai.regdate
, mai.status
, stk.amount;
or if you problem is limited but the inner join for table C
try using inner join for this table too
select
mai.msisdn
, mai.firstname
, mai.lastname
, type as account_type
, alias as nickname
, mai.regdate
, mai.status
, amount as balance
, count(trai.referenceid) as number_of_transactions
, sum(trai.amount) as sum_of_transactions
from tableA mai
left join tableB trai on (mai.msisdn = trai.tomsisdn or mai.msisdn = trai.frmsisdn)
left join tableC stk on mai.msisdn = stk.msisdn
where trai.status = 0
and stk.walletid = 0
group by
mai.msisdn
, mai.firstname
, mai.lastname
, mai.type
, mai.alias
, mai.regdate
, mai.status
, stk.amount;
Try
select mai.msisdn, mai.firstname, mai.lastname, type as account_type,
alias as nickname, mai.regdate, mai.status,
amount as balance, count(trai.referenceid) as number_of_transactions,
sum(trai.amount) as sum_of_transactions
from tableA mai
inner join tableC stk
on mai.msisdn = stk.msisdn
and stk.walletid = 0
left join tableB trai
on (mai.msisdn = trai.tomsisdn or mai.msisdn = trai.frmsisdn)
and trai.status = 0
group by mai.msisdn, mai.firstname, mai.lastname, mai.type,
mai.alias, mai.regdate, mai.status,
stk.amount;
See the difference how (at what stage) predicate is applied in LEFT JOIN .. ON and in WHERE.

SQL Left Outer Join acting like Inner Join

I am trying to do a left outer join on two tables (well, an inline view and a table).
What I want to happen is to list all the grads (I know there are 3815 DISTINCT Grads) with any of their enrolments (there could be 0 or n enrolments). What I'm getting is only a list of the grads that have enrolments (3649 DISTINCT students). I'm not sure where I'm going wrong with not getting all the rows from the grad 'view' (I don't have create view privs so this is my workaround).
This is my code:
SELECT C.*, D.FREEZE_EVENT, D.ACADEMIC_PERIOD, D.CAMPUS, D.COLLEGE, D.COLLEGE_DESC,D.MAJOR, D.MAJOR_DESC , D.STUDENT_RATE
FROM
(SELECT A.STUDENT_LEVEL_DESC, A.CAMPUS, A.CAMPUS_DESC, A.COLLEGE, A.COLLEGE_DESC, A.MAJOR_DESC, A.MAJOR, A.DEGREE_DESC, A.PERSON_UID, A.ID, A.NAME,
A.OUTCOME_GRADUATION_DATE, A.STATUS, A.GRAD_YEAR, A.TRAINING_LOCATION, B.CITIZENSHIP_TYPE
FROM ACAD_OUTOCME A, PERSON_DETAIL B
WHERE A.STUDENT_LEVEL IN ('02','03') AND A.GRAD_YEAR = '2015' AND A.FREEZE_EVENT = '10TH_SEP2016' AND B.FREEZE_EVENT = '10TH_SEP2016'
AND A.ID = B.ID) C
LEFT OUTER JOIN ACAD_STUDY D ON
C.CAMPUS = D.CAMPUS
AND C.COLLEGE = D.COLLEGE
AND C.MAJOR = D.MAJOR
AND C.PERSON_UID = D.PERSON_UID
WHERE D.FREEZE_EVENT = '10TH_SEP2016'
ORDER BY C.NAME
Any suggestions? I'm using Toad Data Point. I'm also the loan developer at work, so I don't have anyone I can ask to help out with this, and google has failed me.
Thanks!
Move your WHERE condition to the ON condition:
Select C.*
, D.FREEZE_EVENT
, D.ACADEMIC_PERIOD
, D.CAMPUS
, D.COLLEGE
, D.COLLEGE_DESC
, D.MAJOR
, D.MAJOR_DESC
, D.STUDENT_RATE
From (Select A.STUDENT_LEVEL_DESC
, A.CAMPUS
, A.CAMPUS_DESC
, A.COLLEGE
, A.COLLEGE_DESC
, A.MAJOR_DESC
, A.MAJOR
, A.DEGREE_DESC
, A.PERSON_UID
, A.ID
, A.NAME
, A.OUTCOME_GRADUATION_DATE
, A.STATUS
, A.GRAD_YEAR
, A.TRAINING_LOCATION
, B.CITIZENSHIP_TYPE
From ACAD_OUTOCME A
Join PERSON_DETAIL B On A.ID = B.ID
Where A.STUDENT_LEVEL In ('02', '03')
And A.GRAD_YEAR = '2015'
And A.FREEZE_EVENT = '10TH_SEP2016'
And B.FREEZE_EVENT = '10TH_SEP2016'
) C
Left Outer Join ACAD_STUDY D
On C.CAMPUS = D.CAMPUS
And C.COLLEGE = D.COLLEGE
And C.MAJOR = D.MAJOR
And C.PERSON_UID = D.PERSON_UID
And D.FREEZE_EVENT = '10TH_SEP2016'
Order By C.NAME;
The WHERE clause is evaluated after the OUTER JOIN, which would cause it to filter out the NULL records from the LEFT JOIN. So, having the right-hand table of a LEFT JOIN in the WHERE clause will effectively transform the OUTER JOIN into an INNER JOIN.

Basic join and aggregate?

I have table A, table B and table C.
I want to select 2 fields from table A, one field from table B (an inner join between these two I'm assuming) and the Count for each record where the identifier on table B is found on table C.
So I'll have:
OpeartiveId | OperativeNumber | JobLocation | CountOfJobIdInWorkTable
Edit:
Operative
OperativeId
OperativeNumber
Jobs
JobId
JobLocation
Work
JobId
OperativeId
Assuming you also want records from a and b that have no matching records in c, you'll need an outer join:
select a.pk_a, b.pk_b, count(c.pk_c)
from a
inner join b on a.pk_a = b.pk_a
left outer join c on b.pk_b = c.pk_b
group by a.pk_a, b.pk_b;
Translate this to your actual schema and try it:
select a.one, a.two, b.three, count(c.id)
from a
join b on a.id=b.id
left join c on c.bid=b.id
group by a.one, a.two, b.three, b.id
Not sure if this is what you looking for. looking at the filed you provided i came up with this.
select o.operativeId, o.OperativeNumber, j.JobLocation, COUNT(w.jobId)
from dbo.Operative o
Inner join works w
ON o.OperativeID = w.OperativeId
INNER JOIN jobs j
ON w.jobId = j.jobId
GROUP by o.operativeId, o.OperativeNumber, j.JobLocation
select a.a,a.b,b.a,(select count(*) from c where c.b_id = b.id)
from a
join b on a.b_id = b.id