oracle outer join with condition in where clause - sql

I have a case table and case_status which is a child table. Earlier case_status was a mandatory field but not anymore.
So i had to change from join to left outer join. After adding this join condition the records are not getting picked up when case_status table doesnt have a record for a case.
case_status has a lookup to lkp_case_status hence having an outer join even there.
My query looks like this
select c.* from cases c
left outer join (SELECT case_id,
case_status_id,
COUNT(DECODE(case_status_id, 16, 1, NULL)) OVER (PARTITION BY case_id) AS IS_CLOSED
FROM case_status) cs ON cs.case_id = c.case_id
left outer join lkp_case_status lkp_cs
on lkp_cs.id = cs.case_status_id
where c.case_type = 'P'
and c.delete_date is null
AND cs.is_closed = 0;
I think its the cs.is_closed = 0 is not letting the record to get pulled up.
So i added AND cs.is_closed = 0; within the join condition as below,
select c.* from cases c
left outer join (SELECT case_id,
case_status_id,
COUNT(DECODE(case_status_id, 16, 1, NULL)) OVER (PARTITION BY case_id) AS IS_CLOSED
FROM case_status) cs ON cs.case_id = c.case_id AND cs.is_closed = 0
left outer join lkp_case_status lkp_cs
on lkp_cs.id = cs.case_status_id
where c.case_type = 'P'
and c.delete_date is null;
But this query is pulling all the records irrespective of the decode condition in the 3rd line.
3rd line - if case_status = 16, which means case is closed and dont pull the record.
Any inputs on how to make it work. Basically it has to pull all the records from cases table , if caseStatus table has a value of case_status = 16 dont pull the case record too.
case
id name
1 AAA
2 BBB
3 CCC
case_status
1 16
2 1
Output should show as follows:
Result:
id name
2 BBB
3 CCC
But what am getting is
id name
2 BBB
My query is not picking case with id = 3.
Any help is highly appreciated. Thank you.

If you want to add a filtering predicate for an outer-joined table or table expression you'll need to account for their columns to show up as nulls. You can do:
select c.* from cases c
left outer join (SELECT case_id,
case_status_id,
COUNT(DECODE(case_status_id, 16, 1, NULL))
OVER (PARTITION BY case_id) AS IS_CLOSED
FROM case_status) cs ON cs.case_id = c.case_id
left outer join lkp_case_status lkp_cs
on lkp_cs.id = cs.case_status_id
where c.case_type = 'P'
and c.delete_date is null
AND (cs.is_closed = 0 or cs.is_closed is null); -- changed here

Related

LEFT OUTER JOIN WITH inner join with case in SQL DBMS

I have the following query
SELECT
A.WORKNR AS "WORK-nr",
UCASE(A.NAME) AS "NAME" ,
UCASE(A.ADRESS) AS "adress",
A.ZIPNR,
UCASE(A.ZIPADR) AS "zipadress",
A.NNR,
CASE
WHEN AO.ORG_REF IS NULL THEN 'OK'
ELSE 'NO '
END AS RESULT OF ORG ,
CASE
WHEN B.B = 1 THEN 'OK'
ELSE 'COME WITH ANOTHER NNR'
END AS COMPARE_RESULT
FROM
WORK.WORK_ADRESS A
LEFT JOIN
(SELECT
POSTNR, UCASE(STREET) AS STREET,
COUNT(DISTINCT NNR) AS B
FROM
WORK.WORK_ADRESS
GROUP BY
ZIPNR, UCASE(STREET)) B ON B.STREET = UCASE(A.STREET)
AND B.ZIPNR = ZIPNR
LEFT JOIN
(SELECT *
FROM WORK.ORGANISATION2) AO ON AO.WORK_REF = A.WORK_REF
AND AO.TILL >= CURRENT DATE
what I want is to add a JOIN to a third table ORG3 that contains the name for org genom
SELECT WORK.ORGANISATION2
INNER JOIN STYR.ORG2 ORG3 ON AO.ORG_REF3 = ORG3.ORG_REF
and get the column OR3.NAME as a separate column.
Can anyone help me? I am thankful

2 SQL query with Union Building

i have 2 SQL query, i want to make both in one SQL query and get all the elements from each query only in one row.
I want to have 6 columns for the result("kunde", "GesamtdateiGrosse", "anzahlObjGesamt", "GesamtdateiGrossefertig", "anzahlObjFertig" "RestGross") and 2 lines. RestGross would then be the difference between GesamtdateiGrosse and GesamtdateiGrossefertig
SELECT
kunde.nummer AS kunde,
SUM(datei.groesse) as "GesamtdateiGrosse",
COUNT (DISTINCT objekt.id) as anzahlObjGesamt
FROM datei
INNER JOIN objekt ON datei.id_objekt = objekt.id
INNER JOIN auftrag ON objekt.id_auftrag = auftrag.id
INNER JOIN kunde ON auftrag.id_kunde = kunde.id
WHERE kunde.nummer = 777
GROUP BY kunde.nummer
UNION
SELECT
kunde.nummer AS kunde,
SUM(datei.groesse) as "GesamtdateiGrossefertig",
COUNT (DISTINCT objekt.id) as anzahlObjFertig
FROM datei
INNER JOIN objekt ON datei.id_objekt = objekt.id
INNER JOIN auftrag ON objekt.id_auftrag = auftrag.id
INNER JOIN status ON objekt.id_restorestatus = status.id
INNER JOIN kunde ON auftrag.id_kunde = kunde.id
WHERE status.name = 'fertig' AND kunde.nummer = 777
GROUP BY kunde.nummer
I expect the output 3 rown 3 column (Kunde, "GesamtdateiGrosse",anzahlObjGesamt)
This should get you close to what you want. The idea is to add another join in the first to bring in the status table, and the do conditional aggregation.
This requires that there is a 1:1 relationship between objekt and status (meaning that, for a given record in objekt, there is always one and only one record in status that satisfies condition objekt.id_restorestatus = status.id).
Consider:
SELECT
kunde.nummer AS kunde,
SUM(datei.groesse) as "GesamtdateiGrosse",
COUNT(DISTINCT objekt.id) as anzahlObjGesamt,
SUM(CASE WHEN status.name = 'fertig' THEN datei.groesse ELSE 0 END) as "GesamtdateiGrossefertig",
COUNT (DISTINCT (CASE WHEN status.name = 'fertig' THEN objekt.id END)) as anzahlObjFertig
FROM datei
INNER JOIN objekt ON datei.id_objekt = objekt.id
INNER JOIN auftrag ON objekt.id_auftrag = auftrag.id
INNER JOIN kunde ON auftrag.id_kunde = kunde.id
INNER JOIN status ON objekt.id_restorestatus = status.id
WHERE kunde.nummer = 777
GROUP BY kunde.nummer

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.

Joining onto NULL if Record does not exist SQL

I have a query that selects customers from a table CustomerDetails, and left joins onto another table (CustomerActivity) to get their last login time and finally left joins onto another table (OpenOrderDetails) to get their last open order (if applicable). I also have a big WHERE clause filtering this data
A customer can only have one record in the OpenOrderDetails table at anytime. My query looks like the following:
SELECT CD.*, H.LastCustomerLoginTime, OD.OrderFiledDate, OD.OrderCompletedDate
FROM CustomerDetails CD
LEFT JOIN CustomerActivity H ON H.CustomerID = CD.CustomerID
LEFT JOIN OpenOrderDetails OD ON CD.CustomerID = OD.CustomerID
WHERE CD.OrderStatus IN (1,2,3)
AND (CustomerType = 1 or (CustomerType = 3 and CustomerActive IN (1,2)))
AND (OD.OrderFiledDate IS NULL OR CD.TimeStamp >= OD.OrderFiledDate)
AND (OD.OrderCompletedDate IS NULL OR CD.TimeStamp <= OD.OrderCompletedDate)
My issue is that this query only returns customer records that have a record in the OpenOrderDetails table. How do I return every customer, and OrderFiledDate/OrderCompletedDate if present, and NULL if a record for that customer does not exist in the OpenOrderDetails table?
When doing a LEFT JOIN, referencing ANY of the right side table columns in the WHERE clause will turn it into an INNER JOIN. To get around this, simply remove any and all predicates that reference "right side" columns from the WHERE clause and move them to the LEFT JOIN condition.
Something along these lines...
SELECT
CD.*,
H.LastCustomerLoginTime,
OD.OrderFiledDate,
OD.OrderCompletedDate
FROM
CustomerDetails CD
LEFT JOIN CustomerActivity H
ON H.CustomerID = CD.CustomerID
LEFT JOIN OpenOrderDetails OD
ON CD.CustomerID = DLECS.CustomerID
AND ( OD.OrderFiledDate IS NULL
OR CD.TimeStamp >= OD.OrderFiledDate
)
AND ( OD.OrderCompletedDate IS NULL
OR CD.TimeStamp <= OD.OrderCompletedDate
)
WHERE
CD.OrderStatus IN ( 1, 2, 3 )
AND (
CustomerType = 1
OR
(
CustomerType = 3
AND CustomerActive IN ( 1, 2 )
)
);
Just move some OpenOrderDetails conditions to join clause
SELECT CD.*, H.LastCustomerLoginTime, OD.OrderFiledDate, OD.OrderCompletedDate
FROM CustomerDetails CD
LEFT JOIN CustomerActivity H ON H.CustomerID = CD.CustomerID
LEFT JOIN OpenOrderDetails OD ON CD.CustomerID = DLECS.CustomerID
AND (OD.OrderFiledDate IS NULL OR CD.TimeStamp >= OD.OrderFiledDate)
AND (OD.OrderCompletedDate IS NULL OR CD.TimeStamp <= OD.OrderCompletedDate)
WHERE CD.OrderStatus IN (1,2,3)
AND (CustomerType = 1 or (CustomerType = 3 and CustomerActive IN (1,2)))

Get DISTINCT record using INNER JOIN

I have the follwong Query on multi tables
SELECT DISTINCT b.BoxBarcode as [Box Barcode], (select case when b.ImagesCount IS NULL
then 0
else b.ImagesCount end) as [Total Images], s.StageName as [Current Stage] ,d.DocuementTypeName as [Document Type],
u.UserName as [Start User],uu.UserName as [Finished User]
FROM [dbo].[Operations] o
inner join dbo.LKUP_Stages s on
o.stageid=s.id
inner join dbo.LKUP_Users u on
u.id=o.startuserid
left join dbo.LKUP_Users uu on
uu.id=o.FinishedUserID
inner join boxes b on
b.id=o.boxid
inner join LKUP_DocumentTypes d on
d.ID = b.DocTypeID
where b.IsExportFinished = 0
when i select count from the Boxes table where IsExportFinished = 0
i got the Count 42 records, when i run the above qoury i got 71 records,
i want just the 42 records in Boxes table to retrived.
You are doing a one-to-many join, i.e. at least one of the tables have multiple rows that match the join criteria.
Step one is to find which table(s) that give the "duplicates".
Once you have done that you may be able to fix the problem by adding additional criteria to the join. I'm taking a guess that the same boxid occurs several times in the Operations table. If that is the case you need to decide which Operation row you want to select and then update the SQL accordingly.
Try this one -
SELECT
Box_Barcode = b.BoxBarcode
, Total_Images = ISNULL(b.ImagesCount, 0)
, Current_Stage = s.StageName
, Document_Type = d.DocuementTypeName
, Start_User = u.UserName
, Finished_User = uu.UserName
FROM (
SELECT DISTINCT
o.stageid
, o.boxid
, o.startuserid
, o.FinishedUserID
FROM dbo.[Operations]
) o
JOIN dbo.LKUP_Stages s ON o.stageid = s.id
JOIN dbo.boxes b ON b.id = o.boxid
JOIN dbo.LKUP_DocumentTypes d ON d.id = b.DocTypeID
JOIN dbo.LKUP_Users u ON u.id = o.startuserid
LEFT JOIN dbo.LKUP_Users uu ON uu.id = o.FinishedUserID
WHERE b.IsExportFinished = 0
I guess that if you change your LEFT JOIN into INNER JOIN you will get 42 records as requested.