Using an alias in a select statement in a join query - oracle

I am very new at using Oracle (in the class now). I have a problem with the query I am trying to run. I have researched a lot of other answers on this site and none of them seem to apply directly to my problem so the solutions aren't working.
I need to find the total amount spent on lunches by each employee.
Show first_name, last_name, credit_limit, and total_price_spent in your results.
Order the answer by total_price_spent in descending order. Only show the employees who spent more than their credit limit.
I figured out how to do everything but the part about showing only the employees who spent more than their credit limit. I tried to use a select statement at the end but discovered that I can't use an alias in a select statement so I don't really know where to go from here. Any help would be appreciated. This is what I have so far.
select a.first_name, a.last_name, credit_limit, sum(c.quantity * d.price) as total_price_spent
from l_employees a join l_lunches b on a.employee_id = b.employee_id join l_lunch_items c on b.lunch_id = c.lunch_id join l_foods d on c.supplier_id = d.supplier_id and c.product_code = d.product_code
group by a.first_name, a.last_name, a.credit_limit
order by total_price_spent desc;

As Mike said : Add HAVING
select a.first_name, a.last_name, credit_limit, sum(c.quantity * d.price) as total_price_spent
from l_employees a join l_lunches b on a.employee_id = b.employee_id join l_lunch_items c on b.lunch_id = c.lunch_id join l_foods d on c.supplier_id = d.supplier_id and c.product_code = d.product_code
group by a.first_name, a.last_name, a.credit_limit
having sum(c.quantity * d.price) > credit_limit
order by total_price_spent desc;

I think what you're looking for is a HAVING clause. It's like a WHERE, but is used when you're using group by. You want to drop it in between the group by and order by. Something like 'HAVING total > a.credit_limit' should work. If using the alias 'total' doesn't work (haven't tested this), you might have to do 'sum(c.quantity * d.price)' again in the HAVING clause instead of using total, so HAVING sum(c.quantity * d.price) > a.credit_limit.

Related

Optimizing Left Join With Group By and Order By (MariaDb)

I am attempting to optimize a query in MariaDb that is really bogged down by its ORDER BY clause. I can run it in under a tenth of a second without the ORDER BY clause, but it takes over 25 seconds with it. Here is the gist of the query:
SELECT u.id, u.display_name, u.cell_phone, u.email,
uv.year, uv.make, uv.model, uv.id AS user_vehicle_id
FROM users u
LEFT JOIN user_vehicles uv ON uv.user_id = u.id AND uv.current_owner=1
WHERE u.is_deleted = 0
GROUP BY u.id
ORDER BY u.display_name
LIMIT 0, 10;
I need it to be a left join because I want to include users that aren't linked to a vehicle.
I need the group by because I want only 1 result per user (and display_name is not guaranteed to be unique).
users table has about 130K rows, while user_vehicles has about 230K rows.
Here is the EXPLAIN of the query:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE u index dms_cust_idx PRIMARY 4 null 124825 Using where; Using temporary; Using filesort
1 SIMPLE uv ref user_idx user_idx 4 awscheduler.u.id 1 Using where
I have tried these two indices to speed things up, but they don't seem to do much.
CREATE INDEX idx_display_speedy ON users(display_name);
CREATE INDEX idx_display_speedy2 ON users(id, display_name, is_deleted, dms_cust_id);
I am looking for ideas on how to speed this up. I attempted using nested queries, but since the order by is the bottleneck & order within the nested query is ignored, I believe that attempt was in vain.
how about:
WITH a AS (
SELECT u.id, u.display_name, u.cell_phone, u.email
FROM users u
WHERE u.is_deleted = 0
GROUP BY u.id
LIMIT 0, 10
)
SELECT a.id, a.display_name, a.cell_phone, a.email,
uv.year, uv.make, uv.model, uv.id AS user_vehicle_id
FROM a LEFT JOIN user_vehicles uv ON uv.user_id = a.id AND uv.current_owner=1
ORDER BY a.display_name;
The intention is we take a subset of users before joining it with user_vehicles.
Disclaimer: I haven't verified if its faster or not, but have similar experience in the past where this helps.
with a as (
SELECT u.id, u.display_name, u.cell_phone, u.email,
uv.year, uv.make, uv.model, uv.id AS user_vehicle_id
FROM users u
LEFT JOIN user_vehicles uv ON uv.user_id = u.id AND uv.current_owner=1
WHERE u.is_deleted = 0
GROUP BY u.id
)
select * from a
ORDER BY u.display_name;
)
I suspect it's not actually the ordering that is causing the problem... If you remove the limit, I bet the ordered and un-ordered versions will end up performing pretty close to the same.
Depending on if your actual query is as simple as the one you posted, you may be able to get good performance in a single query by using RowNum() as described here:
SELECT u.id, u.display_name, u.cell_phone, u.email,
uv.year, uv.make, uv.model, uv.id AS user_vehicle_id
FROM (
SELECT iu.id, iu.display_name, iu.cell_phone, iu.email
FROM users iu
WHERE iu.is_deleted = 0
ORDER BY iu.display_name) as u
LEFT JOIN user_vehicles uv ON uv.user_id = u.id AND uv.current_owner=1
WHERE ROWNUM() < 10
GROUP BY u.id
ORDER BY u.display_name
If that doesn't work, you probably need to select the users in one select and then select their vehicles in a second Select

Difference in the following using Hive Left Outer Join

I had an issue with the LEft outer join and my query was little bit wierd. Tried searching for it , but did not get any clues.
This is happening due to were clause has been added.
The query below is not working as expected.
SELECT count(*) from
BN_DATA_TEMP.EDS_DELIVERYITEM_GBQ_SAMPLE_DATA A
LEFT OUTER JOIN BN_DATA_TEMP.TBL_EDS_DELIVERYITEMS_DATES B
on (
to_Date(substr(A.LASTUPDATEDTIME,1,10)) = '2020-05-31'
and to_Date(substr(B.LASTUPDATEDTIME,1,10)) = '2020-05-31'
and A.deliveryid = B.deliveryid )
where B.deliveryid is null;
Trying to get Records in A but not in B. But the count is very different and it is wrong.
I need to consider only the dates with 2020-05-31 from both the tables.
Modified like the below and it gave the correct result.
select count(*) from
(SELECT * from
BN_DATA_TEMP.EDS_DELIVERYITEM_GBQ_SAMPLE_DATA where to_Date(substr(LASTUPDATEDTIME,1,10)) = '2020-05-31' ) A
LEFT OUTER JOIN
(select * from
BN_DATA_TEMP.TBL_EDS_DELIVERYITEMS_DATES where to_Date(substr(LASTUPDATEDTIME,1,10)) = '2020-05-31' ) B
on A.deliveryid = B.deliveryid
where B.deliveryid is null;
I have never use much of Left outer joins for Data validation. So thought of posting over here and learn the same.
Thanks in Advance.

need help in re-writing this query, which uses same data set multiple times, as per explain plan

We have a query run by our development team that's heavy in resources and looking at the explain plan, it looks like its uses the same data set multiple times. Is there anyway we can re-write this query.
Now, i tried to replace the co-related query with direct join but still the multiple co-related queries look the same apart from one minor difference.
select tb2.mktg_id, mktg_cd , count(distinct tb2.conf_id)
from
(select conf_id, count(distinct c.mktg_id) as num_cpg
from acc_latst c, off_latst ot
where c.mktg_id = ot.mktg_id and c.bus_eff_dt > '2019-01-01' and to_date(strt_tms) = '2019-01-10'
group by conf_id
having count(distinct c.mktg_id) >1
)tb1,
(select distinct conf_id, c.mktg_id, mktg_cd
from acc_latst c, off_latst ot
where c.mktg_id = ot.mktg_id and c.bus_eff_dt > '2019-01-01' and to_date(strt_tms) = '2019-01-10'
)tb2
where tb1.conf_id = tb2.conf_id group by tb2.mktg_id, mktg_cd
One way is using CTE -
with res1 as
(
select distinct conf_id, c.mktg_id, mktg_cd
from acc_latst c, off_latst ot
where c.mktg_id = ot.mktg_id and c.bus_eff_dt > '2019-01-01' and to_date(strt_tms) = '2019-01-10'
)
,res2 as
(
select conf_id, count(distinct c.mktg_id) as num_cpg
from res1 group by conf_id having count(distinct c.mktg_id) > 1
)
select res1.mktg_id, mktg_cd, count(distinct res1.conf_id) from res1 t1 inner join res2 t2 on t1.conf_id=t2.conf_id group by res1.mktg_id, mktg_cd;
If the query is still slow, could you provide table and partition details.

SELECT Date with max date in Oracle

I have expression with db_link to MS SQL:
select b."Str" as "State" ,a."_Fld9059" as "Date" from
"_InfoRg9050"#SQLSERVER.UISLAB.COM a INNER JOIN
"EnumTexts"#SQLSERVER.UISLAB.COM b
on a."_Fld9052RRef" = b."_IDRRef"
where a."_Fld10998" = '1104000009' and
a."_Fld10998" = to_date(max(a."_Fld9059"),'dd.mm.yyyy')
order by a."_Fld9059" desc;
I want to upload value with maximum date. Can anybody help me ?
When I run this query I get ORA-00934 error.
The immediate cause of the error you are getting is that MAX() appears in the WHERE clause. One possible workaround, which might be what you intended, would be to use a subquery in the WHERE clause to identify the maximum date:
SELECT b.Str AS State,
a._Fld9059 AS Date
FROM _InfoRg9050 a
INNER JOIN EnumTexts b
ON a._Fld9052RRef = b._IDRRef
WHERE a._Fld10998 = '1104000009' AND
a._Fld10998 = (SELECT MAX(TO_DATE(_Fld9059, 'dd.mm.yyyy')) FROM _InfoRg9050)
ORDER BY a._Fld9059 DESC
However, it is not clear why you are comparing _InfoRg9050._Fld10998 to both the string '1104000009' and a date. You will need to resolve this on your own I believe to get a meaningful result.
Thank you for your help. I got it.
SELECT b."Str" AS "State"
FROM "_InfoRg9050"#SQLSERVER.UISLAB.COM a
INNER JOIN "EnumTexts"#SQLSERVER.UISLAB.COM b
ON a."_Fld9052RRef" = b."_IDRRef"
WHERE a."_Fld10998" = '1104000009' AND
a."_Fld9059" = (select MAX(a."_Fld9059") from "_InfoRg9050"#SQLSERVER.UISLAB.COM a
INNER JOIN "EnumTexts"#SQLSERVER.UISLAB.COM b
on a."_Fld9052RRef" = b."_IDRRef"
where a."_Fld10998" = '1104000009')
ORDER BY a."_Fld9059" DESC

Oracle: using WHERE ROWNUM = 1

chaps and chapettes
Just a quick question. I need to return only one row from a stored proc., but no matter where I place the WHERE clause, I get errors. Can somebody take a look at the (cut-down due to sheer length) code and let me know where it should go, please?
SELECT **values**
INTO **variables**
FROM **table**
_WHERE ROWNUM = 1_
INNER JOIN **other table**
ON **join target**
ORDER BY **sort criteria**;
_WHERE ROWNUM = 1_
Thanks
I believe this is the way to structure rownum queries
SELECT * FROM
INTO **Variables * *
( SELECT * FROM X
WHERE Y
ORDER BY Z
)
WHERE ROWNUM = 1;
You were almost correct. You put the WHERE clause after the JOINs, but before the ORDER BY.
SELECT **values**
INTO **variables**
FROM **table**
INNER JOIN **other table**
ON **join target**
_WHERE ROWNUM = 1_
ORDER BY **sort criteria**;
However, this won't do what you might think - the ORDER BY is evaluated AFTER the where clause; which means this will just pick the first record it finds (that satisfies the join criteria), and will then sort that row (which obviously is a no-op).
The other answers (e.g. IvoTops') give ideas of how to get the first record according to the sort criteria.
SELECT **values**
INTO **variables**
FROM
( SELECT **values**
, ROW_MUMBER() OVER (ORDER BY **sort criteria**) AS rn
FROM **table**
INNER JOIN **other table**
ON **join target**
) tmp
WHERE rn = 1 ;
Check also this blog post: Oracle: ROW_NUMBER() vs ROWNUM
little bit late, but I got a similar problem and I solved it like this:
SELECT **values**
INTO **variables**
FROM **table**
WHERE **condition**
ORDER BY **sort criteria**
FETCH FIRST 1 ROW ONLY;
Regards

Resources