How to include Null records in Oracle query - oracle

I have this query that returns what you would expect
SELECT
e.event_date,
e.venue_id,
e.description AS event,
t.price,
t.ticket_id,
bt.buyer_type_code,
bt.description AS buyer_type,
btg.description AS ticket_category,
SUM(sci.actual_amount) AS tax,
t.price + SUM(sci.actual_amount) AS revenue,
e.event_id,
coupon.coupon_code
FROM
event e
INNER JOIN event_seat es ON e.event_id = es.event_id
INNER JOIN ticket t ON es.ticket_id = t.ticket_id
LEFT JOIN service_charge_item sci ON sci.ticket_id = t.ticket_id
INNER JOIN buyer_type bt ON t.buyer_type_id = bt.buyer_type_id
INNER JOIN buyer_type_group btg ON bt.buyer_type_group_id = btg.buyer_type_group_id
INNER JOIN coupon ON t.coupon_id = coupon.coupon_id
WHERE
e.event_date > '1-JAN-2022'
AND e.description LIKE '%Hm%'
AND e.description NOT LIKE '%Mg%'
AND e.description NOT LIKE '%Gt%'
AND e.event_status_code = 'SAL'
GROUP BY
e.event_date,
e.venue_id,
e.description,
t.price,
t.ticket_id,
bt.buyer_type_code,
bt.description,
btg.description,
e.event_id,
coupon.coupon_code
if i leave out the coupon_code references i get significantly more records. so it seems it is only returning records that have input in the coupon_code field. how do i return everything whether it has input or no input ?

You simply need a LEFT JOIN instead of INNER OIN -
SELECT
e.event_date,
e.venue_id,
e.description AS event,
t.price,
t.ticket_id,
bt.buyer_type_code,
bt.description AS buyer_type,
btg.description AS ticket_category,
SUM(sci.actual_amount) AS tax,
t.price + SUM(sci.actual_amount) AS revenue,
e.event_id,
coupon.coupon_code
FROM
event e
INNER JOIN event_seat es ON e.event_id = es.event_id
INNER JOIN ticket t ON es.ticket_id = t.ticket_id
LEFT JOIN service_charge_item sci ON sci.ticket_id = t.ticket_id
INNER JOIN buyer_type bt ON t.buyer_type_id = bt.buyer_type_id
INNER JOIN buyer_type_group btg ON bt.buyer_type_group_id = btg.buyer_type_group_id
LEFT JOIN coupon ON t.coupon_id = coupon.coupon_id -- Change this to LEFT JOIN from INNER JOIN
WHERE
e.event_date > '1-JAN-2022'
AND e.description LIKE '%Hm%'
AND e.description NOT LIKE '%Mg%'
AND e.description NOT LIKE '%Gt%'
AND e.event_status_code = 'SAL'
GROUP BY
e.event_date,
e.venue_id,
e.description,
t.price,
t.ticket_id,
bt.buyer_type_code,
bt.description,
btg.description,
e.event_id,
coupon.coupon_code

Related

How can I solve the ORA-01799 when creating a VIEW with a LEFT OUTER JOIN and a MAX value?

I am trying to create a view that joins a few tables, one of the joins is using a max value and I think that is generating the following error in our Oracle DB:
ORA-01799: a column may not be outer-joined to a subquery.
Can anyone help me to change the estatement to create the view? The query works fine, the problem only comes when I try to create a view with it.
CREATE or replace VIEW xxxxxx_V (ID, SERVICE_ID, PARENT_ID, PARENT_MATERIALITY, PARENT_END_STATUS,
PARENT_OUTS_TYPE, END_STATUS, MATERIALITY, OUTS_TYPE, CREATED_BY,
SERVICE_DESCRIPTION_SHORT, VERSION, OUTSOURCER_ID, OUTSOURCER_NAME, INSOURCER_ID, INSOURCER_NAME, PLANNED_SERVICE_START) AS
select c.id id, s.id service_id, s.outsourcing_id parent_id, p.materiality parent_materiality, p.end_status parent_end_status,
p.outsourcing_type parent_outs_type, c.end_status end_status, c.materiality materiality, c.outsourcing_type outs_type, c.created_by created_by,
r.service_description_short, r.version, r.outsourcer outsourcer_id, o.name outsourcer_name , r.insourcer insourcer_id,i.name insourcer_name, r.planned_service_start
from OS_OUTSOURCING_CONTRACT c
left join OS_SERVICES s on s.id = c.service_id
left join OS_OUTSOURCING_CONTRACT p on p.id = s.outsourcing_id
left join OS_RISK_ASSESSMENT r on r.outsourcing_id = c.id and r.version = (select max(version) from OS_RISK_ASSESSMENT where outsourcing_id = c.id)
left join OS_COMPANY_INSOURCER i on i.id = r.insourcer
left join OS_COMPANY_OUTSOURCER o on o.id = r.outsourcer
Join the subquery into the query:
CREATE or replace VIEW xxxxxx_V
(ID, SERVICE_ID, PARENT_ID, PARENT_MATERIALITY, PARENT_END_STATUS,
PARENT_OUTS_TYPE, END_STATUS, MATERIALITY, OUTS_TYPE, CREATED_BY,
SERVICE_DESCRIPTION_SHORT, VERSION, OUTSOURCER_ID, OUTSOURCER_NAME,
INSOURCER_ID, INSOURCER_NAME, PLANNED_SERVICE_START) AS
select c.id,
s.id service_id,
s.outsourcing_id parent_id,
p.materiality parent_materiality,
p.end_status parent_end_status,
p.outsourcing_type parent_outs_type,
c.end_status,
c.materiality,
c.outsourcing_type outs_type,
c.created_by,
r.service_description_short,
r.version,
r.outsourcer outsourcer_id,
o.name outsourcer_name,
r.insourcer insourcer_id,
i.name insourcer_name,
r.planned_service_start
from OS_OUTSOURCING_CONTRACT c
LEFT OUTER JOIN (select outsourcing_id,
max(version) AS MAX_VERSION
from OS_RISK_ASSESSMENT
group by outsourcing_id) ora
ON ora.OUTSOURCING_ID = c.id
left join OS_SERVICES s
on s.id = c.service_id
left join OS_OUTSOURCING_CONTRACT p
on p.id = s.outsourcing_id
left join OS_RISK_ASSESSMENT r
on r.outsourcing_id = ora.OUTSOURCING_ID and
r.version = ora.MAX_VERSION
left join OS_COMPANY_INSOURCER i
on i.id = r.insourcer
left join OS_COMPANY_OUTSOURCER o
on o.id = r.outsourcer

spring specification redundant joins

I'm using spring specification to interact with the database, however I can see that in the generated SQL queries there is redundant joins which make the query very slow, here is the code:
Subquery < Mission > usersAccessSubquery = query.subquery(Mission.class);
Root < Mission > usersAccessSubqueryRoot = usersAccessSubquery.from(Mission.class);
usersAccessSubquery.select(usersAccessSubqueryRoot);
Subquery < Mission > groupesAccessSubquery = query.subquery(Mission.class);
Root < Mission > groupesAccessSubqueryRoot = groupesAccessSubquery.from(Mission.class);
groupesAccessSubquery.select(groupesAccessSubqueryRoot);
Subquery < Mission > groupesAccessSubqueryAdmin = query.subquery(Mission.class);
Root < Mission > groupesAccessSubqueryRootAdmin = groupesAccessSubqueryAdmin.from(Mission.class);
groupesAccessSubqueryAdmin.select(groupesAccessSubqueryRootAdmin);
Predicate usersAccessPredicate = cb.equal(cb.lower(usersAccessSubqueryRoot.join("usersAccess").join("userRef").get("email")), authUserMail.toLowerCase());
Predicate groupesAccessPredicate = cb.equal(cb.lower(groupesAccessSubqueryRoot.join("groupesAccess").join("users").join("userRef").get("email")), authUserMail.toLowerCase());
Predicate groupesAccessPredicateAdmin = cb.equal(cb.lower(groupesAccessSubqueryRootAdmin.join("groupe").get("label")), groupe.getLabel().toLowerCase());
Predicate rootPredicate = cb.conjunction();
usersAccessSubquery.where(usersAccessPredicate);
groupesAccessSubquery.where(groupesAccessPredicate);
groupesAccessSubqueryAdmin.where(groupesAccessPredicateAdmin);
query.where(cb.and(
cb.or(
cb.equal(cb.lower(cb.trim(root.get("createdBy"))), authUserMail.toLowerCase().trim()),
cb.in(root.join("mission").get("id")).value(usersAccessSubquery),
cb.in(root.join("mission").get("id")).value(groupesAccessSubquery),
cb.in(root.join("mission").get("id")).value(groupesAccessSubqueryAdmin)
),
rootPredicate
));
and the generated SQL query:
SELECT DISTINCT user0_.id AS id1_25_,
user0_.created_by AS created_2_25_,
user0_.creation_date AS creation3_25_,
user0_.groupe_id AS groupe_i6_25_,
user0_.role_id AS role_id7_25_,
user0_.update_date AS update_d4_25_,
user0_.updated_by AS updated_5_25_,
user0_.user_ref_id AS user_ref8_25_
FROM users user0_
INNER JOIN user_ref userref1_
ON user0_.user_ref_id = userref1_.id
INNER JOIN user_ref userref2_
ON user0_.user_ref_id = userref2_.id
INNER JOIN user_ref userref3_
ON user0_.user_ref_id = userref3_.id
INNER JOIN groupes groupe4_
ON user0_.groupe_id = groupe4_.id
INNER JOIN mission missions5_
ON groupe4_.id = missions5_.client_entity_id
INNER JOIN groupes groupe6_
ON user0_.groupe_id = groupe6_.id
INNER JOIN mission missions7_
ON groupe6_.id = missions7_.client_entity_id
INNER JOIN groupes groupe8_
ON user0_.groupe_id = groupe8_.id
INNER JOIN mission missions9_
ON groupe8_.id = missions9_.client_entity_id
WHERE ( Lower(userref1_.username) LIKE 'a%'
OR Lower(userref2_.first_name) LIKE 'a%'
OR Lower(userref3_.last_name) LIKE 'a%' )
AND ( missions5_.id IN (SELECT mission10_.id
FROM mission mission10_
INNER JOIN groupes groupe11_
ON mission10_.groupe_id =
groupe11_.id
WHERE Lower(groupe11_.label) =
'tls team')
OR missions7_.id IN (SELECT mission12_.id
FROM mission mission12_
INNER JOIN mission_users_access
usersacces13_
ON mission12_.id =
usersacces13_.mission_id
INNER JOIN users user14_
ON
usersacces13_.users_access_id =
user14_.id
INNER JOIN user_ref userref15_
ON user14_.user_ref_id =
userref15_.id
WHERE Lower(userref15_.departement) =
'test#test.com')
OR missions9_.id IN (SELECT mission16_.id
FROM mission mission16_
INNER JOIN mission_groupes_access
groupesacc17_
ON mission16_.id =
groupesacc17_.mission_id
INNER JOIN groupes groupe18_
ON
groupesacc17_.groupes_access_id =
groupe18_.id
INNER JOIN users users19_
ON groupe18_.id =
users19_.groupe_id
INNER JOIN user_ref userref20_
ON users19_.user_ref_id =
userref20_.id
WHERE Lower(userref20_.departement) =
'test#test.com') )
here's the redundant joins:
INNER JOIN user_ref userref1_
ON user0_.user_ref_id = userref1_.id
INNER JOIN user_ref userref2_
ON user0_.user_ref_id = userref2_.id
INNER JOIN user_ref userref3_
ON user0_.user_ref_id = userref3_.id
INNER JOIN groupes groupe4_
ON user0_.groupe_id = groupe4_.id
INNER JOIN mission missions5_
ON groupe4_.id = missions5_.client_entity_id
INNER JOIN groupes groupe6_
ON user0_.groupe_id = groupe6_.id
INNER JOIN mission missions7_
ON groupe6_.id = missions7_.client_entity_id
INNER JOIN groupes groupe8_
ON user0_.groupe_id = groupe8_.id
INNER JOIN mission missions9_
ON groupe8_.id = missions9_.client_entity_id
That can replaced by this:
INNER JOIN user_ref userref1_
ON user0_.user_ref_id = userref1_.id
INNER JOIN groupes groupe4_
ON user0_.groupe_id = groupe4_.id
INNER JOIN mission missions5_
ON groupe4_.id = missions5_.client_entity_id
Which makes execution time much faster, I think the request in the code is optimized, and I don't know why the generated query looks like that, can anybody explain please?
Try to reuse the path, like
Path<?> path = root.join("mission").get("id"));
query.where(cb.and(
cb.or(
cb.equal(cb.lower(cb.trim(root.get("createdBy"))), authUserMail.toLowerCase().trim()),
cb.in(path).value(usersAccessSubquery),
cb.in(path).value(groupesAccessSubquery),
cb.in(path).value(groupesAccessSubqueryAdmin)
),

LISTAGG inside a LISTAGG

I used some online converter to convert my view in MySQL to oracle and the result was this:
CREATE VIEW actor_info
AS
SELECT
a.actor_id,
a.first_name,
a.last_name,
GROUP_CONCAT(DISTINCT ||(c.name, ': ',
(SELECT GROUP_CONCAT(f.title FROM dual ORDER BY f.title SEPARATOR FROM dual ', ')
FROM film f
INNER JOIN film_category fc
ON f.film_id = fc.film_id
INNER JOIN film_actor fa
ON f.film_id = fa.film_id
WHERE fc.category_id = c.category_id
AND fa.actor_id = a.actor_id
)
)
ORDER BY c.name SEPARATOR '; ')
AS film_info
FROM actor a
LEFT JOIN film_actor fa
ON a.actor_id = fa.actor_id
LEFT JOIN film_category fc
ON fa.film_id = fc.film_id
LEFT JOIN category c
ON fc.category_id = c.category_id
GROUP BY a.actor_id, a.first_name, a.last_name;
The converter didn't convert too well so I had to modify the query and end up with something like this:
CREATE VIEW actor_info
AS
SELECT
a.actor_id,
a.first_name,
LISTAGG(c.name || ': ',
(SELECT LISTAGG(f.title, ', ') within group (order by f.title)
FROM film f
INNER JOIN film_category fc
ON f.film_id = fc.film_id
INNER JOIN film_actor fa
ON f.film_id = fa.film_id
WHERE fc.category_id = c.category_id
AND fa.actor_id = a.actor_id
)
) WITHIN group (order by c.name)
AS film_info
FROM actor a
LEFT JOIN film_actor fa
ON a.actor_id = fa.actor_id
LEFT JOIN film_category fc
ON fa.film_id = fc.film_id
LEFT JOIN category c
ON fc.category_id = c.category_id
GROUP BY a.actor_id, a.first_name, a.last_name, c.name;
But I'm still getting multiple errors like I can't use distinct in the first listagg or that argument should be a constant or a function of expression in GROUP BY. I'm out of ideas to try to solve this, any suggestions on what the error is or another way to do this view?
Oracle's listagg() doesn't support distinct internally, which is a bit of a pain. The other error you're getting, ORA-30497, is because you've accidneally mad ethe second listagg() call the delmiter for the first one.
It's a little hard to tell without sample data and expected resulsts, but it appears you're looking for something like:
SELECT a.actor_id, a.first_name, a.last_name,
LISTAGG(c.name || ': ' || (
SELECT LISTAGG(f.title, ', ') WITHIN GROUP (ORDER BY f.title)
FROM film f
INNER JOIN film_actor fa
ON fa.film_id = f.film_id
INNER JOIN film_category fc
ON f.film_id = fc.film_id
WHERE fc.category_id = c.category_id -- from main query
AND fa.actor_id = a.actor_id -- from main query
), '; ') WITHIN GROUP (ORDER BY c.name)
AS film_info
FROM actor a
LEFT JOIN (
SELECT DISTINCT fa.actor_id, c.category_id, c.name
FROM film_actor fa
LEFT JOIN film_category fc
ON fc.film_id = fa.film_id
LEFT JOIN category c
ON c.category_id = fc.category_id
) c
ON c.actor_id = a.actor_id
GROUP BY a.actor_id, a.first_name, a.last_name;
The 'distinct' part is achieved with the inline view, and the inner listagg then only needs to find all films for that category/actor.

Doing a Left outer join in a linq query

I have the query below that works in sqllite with a left outer join
select * from customer cu
inner join contract cnt on cu.customerId = cnt.customerid
inner join address addy on cu.addressid = addy.addressId
inner join csrAssoc cassc on cu.customerid = cassc.customerId
left outer join CustomerServiceRepresentative csrr on cassc.csrid = csrr.customerservicerepresentativeId
inner join customerServiceManager csmm on cassc.csmid = csmm.customerservicemanagerId
where cu.customernumber = '22222234'
I want to be able to apply a left outer join on this line in the linq query below
join csrr in objCsrCustServRep.AsEnumerable() on cassc.CsrId equals
csrr.CustomerServiceRepresentativeId
VisitRepData = (from cu in objCustomer.AsEnumerable()
join cnt in objContract.AsEnumerable() on cu.customerId equals cnt.customerId
join addy in objAddress.AsEnumerable() on cu.addressId equals addy.addressId
join cassc in objCsrAssoc.AsEnumerable() on cu.customerId equals cassc.CustomerId
join csrr in objCsrCustServRep.AsEnumerable() on cassc.CsrId equals
csrr.CustomerServiceRepresentativeId
join csmm in objCustServMan on cassc.CsmId.ToString() equals csmm.customerServiceManagerId
where cu.CustomerNumber == (customernbr)
How can I do a left outer join in a linq query?
Here is my comment after adjusting and running the code. The other section is also added. All am getting is object is not set to an instance of an object.
var VisitRepData = from cu in objCustomer.AsEnumerable()
join cnt in objContract.AsEnumerable() on cu.customerId equals cnt.customerId
join addy in objAddress.AsEnumerable() on cu.addressId equals addy.addressId
join cassc in objCsrAssoc.AsEnumerable() on cu.customerId equals cassc.CustomerId
join csrr in objCsrCustServRep.AsEnumerable() on cassc.CsrId equals
csrr.CustomerServiceRepresentativeId into temp
from tempItem in temp.DefaultIfEmpty()
join csmm in objCustServMan on cassc.CsmId.ToString() equals csmm.customerServiceManagerId
where cu.CustomerNumber == (customernbr)
select new
{
cu.customerId,
cu.CustomerNumber,
cu.customerName,
cu.dateActive,
cnt.contractExpirationDate,
addy.street,
addy.street2,
addy.city,
addy.state,
addy.zipcode,
cu.EMail,
cu.phoneNo,
cu.faxNumber,
csmm.customerServiceManagerName,
tempItem.CustomerServiceRepresentativeName,
};
foreach (var item in VisitRepData)
{
var one = item.customerId;
var two = item.CustomerNumber;
}
Per documentation:
A left outer join is a join in which each element of the first collection is returned, regardless of whether it has any correlated elements in the second collection. You can use LINQ to perform a left outer join by calling the DefaultIfEmpty method on the results of a group join
(emphasis mine)
Based on MSDN link above, and if I understood your requirements correctly, query should look like this:
VisitRepData = from cu in objCustomer.AsEnumerable()
join cnt in objContract.AsEnumerable() on cu.customerId equals cnt.customerId
join addy in objAddress.AsEnumerable() on cu.addressId equals addy.addressId
join cassc in objCsrAssoc.AsEnumerable() on cu.customerId equals cassc.CustomerId
join csrr in objCsrCustServRep.AsEnumerable() on cassc.CsrId equals
csrr.CustomerServiceRepresentativeId into temp
from tempItem in temp.DefaultIfEmpty()
join csmm in objCustServMan on cassc.CsmId.ToString() equals csmm.customerServiceManagerId
where cu.CustomerNumber == (customernbr)
Specifically, the left outer join is performed with this code:
join csrr in objCsrCustServRep.AsEnumerable()
on cassc.CsrId equals csrr.CustomerServiceRepresentativeId
into temp
from tempItem in temp.DefaultIfEmpty()

Using LAG to Find Previous Value in Oracle

I'm trying to use the LAG function in Oracle to find the previous registration value for donors.
To see my data, I started with this query to find all registrations for the particular donor:
select registration_id, registration_date from registration r where r.person_id=52503290 order by r.registration_date desc;
Then I used the LAG function to return the previous value along with the most recent value:
select registration_id as reg_id, registration_date as reg_date,
lag(registration_date,1) over (order by registration_date) as prev_reg_date
from registration
where person_id=52503290
order by registration_date desc;
And the results are as expected:
So I thought I should be good to place the LAG function within the main query to get the previous value but for some reason, the previous value returns NULL or no value at all.
SELECT
P.Person_Id AS Person_ID,
R.Registration_Date AS Drive_Date,
LAG(R.Registration_Date,1) OVER (ORDER BY R.REGISTRATION_DATE) AS Previous_Drive_Date,
P.Abo AS Blood_Type,
DT.Description AS Donation_Type
FROM
Person P
JOIN Registration R ON P.Person_Id = R.Person_Id AND P.First_Name <> 'Pooled' AND P.First_Name <> 'IMPORT'
LEFT OUTER JOIN Drives DR ON R.Drive_Id = DR.Drive_Id AND DR.Group_Id <> 24999
LEFT OUTER JOIN Branches B ON R.Branch_Id = B.Branch_Id
LEFT OUTER JOIN Donor_Group DG on DR.Group_Id = DG.Group_Id
LEFT OUTER JOIN Donation_Type DT ON R.Donation_Type_Id = DT.DONATION_TYPE_ID
WHERE
TRUNC(R.Registration_Date) = TRUNC(SYSDATE)-1
AND R.Person_Id=52503290
ORDER BY
R.Registration_Date DESC;
Here is the result set:
Any suggestions on what I am missing here? Or why this query isn't returning the values expected?
Based on #Alex Poole's suggestions, I changed the query to look like:
SELECT * FROM (
SELECT
P.Person_Id AS Person_ID,
R.Registration_Date AS Drive_Date,
LAG(R.Registration_Date,1) OVER (partition by p.person_id ORDER BY r.registration_date) AS Previous_Drive_Date,
P.Abo AS Blood_Type,
DT.Description AS Donation_Type
FROM
Person P
JOIN Registration R ON P.Person_Id = R.Person_Id AND P.First_Name <> 'Pooled' AND P.First_Name <> 'IMPORT'
LEFT OUTER JOIN Drives DR ON R.Drive_Id = DR.Drive_Id AND DR.Group_Id <> 24999
LEFT OUTER JOIN Branches B ON R.Branch_Id = B.Branch_Id
LEFT OUTER JOIN Donor_Group DG on DR.Group_Id = DG.Group_Id
LEFT OUTER JOIN Donation_Type DT ON R.Donation_Type_Id = DT.DONATION_TYPE_ID
--WHERE R.Person_Id=52503290
)
WHERE TRUNC(Drive_Date) = TRUNC(SYSDATE)-1
ORDER BY Drive_Date DESC;
It takes about 85 seconds to pull back the first 30 rows:
My original query (before the LAG function was added) took about 2 seconds to pull back approximately 2100 records. But it was nothing but a SELECT with a couple of JOINS and one item in the WHERE clause.
Looking at the record counts, Person has almost 5.5 million records and Registration has 9.1 million records.
The lag is only applied within the rows that match the where clause filter, so you would only see the previous value if that was also yesterday.
You can apply the lag in a subquery, and then filter in an outer query:
SELECT * FROM (
SELECT
P.Person_Id AS Person_ID,
R.Registration_Date AS Drive_Date,
LAG(R.Registration_Date,1) OVER (ORDER BY R.REGISTRATION_DATE) AS Previous_Drive_Date,
P.Abo AS Blood_Type,
DT.Description AS Donation_Type
FROM
Person P
JOIN Registration R ON P.Person_Id = R.Person_Id AND P.First_Name <> 'Pooled' AND P.First_Name <> 'IMPORT'
LEFT OUTER JOIN Drives DR ON R.Drive_Id = DR.Drive_Id AND DR.Group_Id <> 24999
LEFT OUTER JOIN Branches B ON R.Branch_Id = B.Branch_Id
LEFT OUTER JOIN Donor_Group DG on DR.Group_Id = DG.Group_Id
LEFT OUTER JOIN Donation_Type DT ON R.Donation_Type_Id = DT.DONATION_TYPE_ID
WHERE R.Person_Id=52503290
)
WHERE TRUNC(Drive_Date) = TRUNC(SYSDATE)-1
ORDER BY Drive_Date DESC;

Resources