Oracle CONNECT BY and MAX - oracle

I am having difficulty integrating a MAX into a query and would greatly appreciate any help.
Basically what I'm trying to achieve is this: list a supervisor's supervisor's supervised employees along with their latest "day out" punch time.
The part I can't get right is the MAX(day out) part.
Here's the part I got so far that works fine:
SELECT EMPLOYEE.NUMBER,
EMPLOYEE.NAME,
S.NAME AS SUPERVISOR,
EMPLOYEE.HIRE_DATE
FROM EMPLOYEE
LEFT JOIN EMPLOYEE_NUMBER ON EMPLOYEE_NUMBER.NUMBER = EMPLOYEE.SUPERVISOR_NUMBER
LEFT JOIN EMPLOYEE S ON S.NUMBER = EMPLOYEE.SUPERVISOR_NUMBER
WHERE LEVEL =2
START WITH EMPLOYEE_NUMBER.USERID = (ID OF SUPERVISOR HERE)
CONNECT BY PRIOR EMPLOYEE.NUMBER = EMPLOYEE.SUPERVISOR_NUMBER
This part works and gives me the basic input that I need. However, I also need to list, for each employee, his/her lastest "day out" date.
Here's what I tried tried that didn't work:
SELECT EMPLOYEE.NUMBER,
EMPLOYEE.NAME,
S.NAME AS SUPERVISOR,
EMPLOYEE.HIRE_DATE,
MAX(EMPLOYEE_TIME.DATE_OUT)
FROM EMPLOYEE
LEFT JOIN EMPLOYEE_NUMBER ON EMPLOYEE_NUMBER.NUMBER = EMPLOYEE.SUPERVISOR_NUMBER
LEFT JOIN EMPLOYEE S ON S.NUMBER = EMPLOYEE.SUPERVISOR_NUMBER
LEFT JOIN EMPLOYEE_TIME ON EMPLOYEE_TIME.EMPLOYEE_NUMBER = EMPLOYEE.EMPLOYEE_NUMBER
WHERE LEVEL =2
START WITH EMPLOYEE_NUMBER.USERID = (ID OF SUPERVISOR HERE)
CONNECT BY PRIOR EMPLOYEE.NUMBER = EMPLOYEE.SUPERVISOR_NUMBER
GROUP BY
EMPLOYEE.NUMBER,
EMPLOYEE.NAME,
S.NAME,
EMPLOYEE.HIRE_DATE
HAVING MAX(EMPLOYEE_TIME.DATE_OUT) >= SYSDATE-60
It doesn't throw any error, it just keeps on processing forever. I'm guessing something must be looping but I can't figure it out.
Thanks for any help.

A slight change to your query in joining the time table, it need not be joined while CONNECT BY, instead separate it.
SELECT NUMBER,
EMPLOYEE_NAME,
SUPERVISOR,
HIRE_DATE,
MAX(TIME.DATE_OUT)
FROM
(SELECT NUMBER,
EMPLOYEE.NAME AS EMPLOYEE_NAME,
S.NAME AS SUPERVISOR,
EMPLOYEE.HIRE_DATE
FROM EMPLOYEE
LEFT JOIN EMPLOYEE_NUMBER ON EMPLOYEE_NUMBER.NUMBER = EMPLOYEE.SUPERVISOR_NUMBER
LEFT JOIN EMPLOYEE S ON S.NUMBER = EMPLOYEE.SUPERVISOR_NUMBER
WHERE LEVEL =2
START WITH EMPLOYEE_NUMBER.USERID = (ID OF SUPERVISOR HERE)
CONNECT BY PRIOR EMPLOYEE.NUMBER = EMPLOYEE.SUPERVISOR_NUMBER) EMP_SUP
INNER JOIN EMPLOYEE_TIME TIME
ON( EMP_SUP.NUMBER = TIME.NUMBER)
GROUP BY
NUMBER,
EMPLOYEE_NAME,
SUPERVISOR,
HIRE_DATE
HAVING MAX(DATE_OUT) >= SYSDATE-60

Related

Customer details based on booking count

Write a query to display user id and user name where the number of seats booked by the user in the individual booking is greater than 1. Display records in ascending order by user name.
I have tried this query and I m getting errors. Please help me!!
select u.user_id,u.name
from users u join bookingdetails bd
on u.name=bd.name
join tickets t on u.user_id=t.user_id
group by u.name
having count(t.no_seats) > 1
order by u.name;
select distinct u.user_id,u.name from users u
join tickets t on u.user_id = t.user_id
where
u.user_id in( select user_id from tickets where no_seats>1)
order by u.name;
If I understand your assignment it looks like you should do
WITH cteBookings AS (SELECT bd.USER_ID, SUM(t.NO_SEATS) AS TOTAL_SEATS_BOOKED
FROM BOOKINGDETAILS bd
INNER JOIN PAYMENTS p
ON p.BD_ID = bd.BD_ID
INNER JOIN TICKETS t
ON t.TICKET_ID = p.TICKET_ID
GROUP BY bd.USER_ID)
SELECT DISTINCT b.USER_ID, u.USER_NAME
FROM USERS u
INNER JOIN cteBookings b
ON b.USER_ID = u.USER_ID
WHERE b.TOTAL_SEATS_BOOKED > 1
ORDER BY u.USER_NAME ASC
Best of luck.
According to the question, it asks for the individual bookings > 1
So, the solution is very simple i.e, for each ticket_id, the no of seats should be greater than 1.
SELECT DISTINCT(user_id),name
FROM Tickets inner join Users
USING (user_id)
WHERE no_seats>1
ORDER BY name;
It will simpler if you try it without joins.
A simple example using a subquery:
select user_id, name from users
where user_id = any(select distinct user_id from tickets where no_seats > 1)
order by name asc;

How to use GROUP BY clause with COUNT(*)

I have two tables on Oracle database, one is named departments_table and the other is locations_table. The departments.table has dep_id, dep_name, location_id, staff_id, employer_id. The locations table consists of location_id, city_id, streetname_id and postcode_id. How do I calculate the number of departments that each location has?
This is the code below is what I have tried to replicate but have been unsuccessful. The error message below that is what shows once the code has submitted.
SELECT dep_name, location_id,
COUNT(*)
FROM departments_table
WHERE location_id => 1
GROUP BY dep_name;
The results of this is an error, " not a single group function "
If you want to count how many departments are in each location, then you must group by location, not by department name, right? Let's start with that.
Then, you don't need ANYTHING about the individual departments in the output of the query, do you? You just need the location id and the count of departments.
select location_id, count(*) as cnt
from departments_table
group by location_id
;
This does most of the work. You may want to add the location name (city, address, etc.), which is/are stored elsewhere - in the locations_table. So you will need a join. And there may be locations in that table that are not, in fact, the location of any department (their id doesn't appear in the departments_table at all). If so, you would need an OUTER join. Also for those departments you probably want to show a count of 0 (rather than null) - you can "fix" that with the nvl() function. So you will end up with something like
select l.*, nvl(g.cnt, 0) as department_count
from locations_table l
left outer join
( select location_id, count(*) as cnt
from departments_table
group by location_id
) g
on l.location_id = g.location_id
;
SELECT l.location_id, l.city, COUNT(d.DEPARTMENT_ID)
FROM OEHR_LOCATIONS l, OEHR_DEPARTMENTS d WHERE l.location_id = d.location_id
GROUP BY l.location_id, l.city ORDER BY l.city;
This method works. I created aliases and made minor changes. OEHR stands for the table names so ignore that.

Only want to return rows that have duplicate unitid within group by unitid

I am working in SSRS querying against an oracle database.
So I have a data source and this report is supposed to find duplicate Work Orders based on multiple open workorders on a unique unitid. So I only want to show groups that have more then one entry as they are grouped by unitid.
SELECT
COMPSTSB.UNITID,
COMPSTSB.UNITTYPE,
ACTDEFN.ACTDESC,
ACTDEFN.ACTCODE,
HISTORY.WONO,
HISTORY.COMPFLAG,
HISTORY.ADDDTTM,
HISTORY.COMMENTS
FROM (IMSV7.COMPSTSB COMPSTSB INNER JOIN IMSV7.HISTORY HISTORY ON
COMPSTSB.COMPKEY=HISTORY.COMPKEY) INNER JOIN IMSV7.ACTDEFN ACTDEFN ON
HISTORY.ACTKEY=ACTDEFN.ACTKEY
WHERE HISTORY.COMPFLAG='Y' AND NOT (ACTDEFN.ACTCODE='DBR' OR ACTDEFN.ACTCODE='IN')
ORDER BY COMPSTSB.UNITTYPE, COMPSTSB.UNITID, HISTORY.ADDDTTM
Can anyone point me in the right direction? And yes before someone says this has been asked a million times, I did search. Point me to the million times and I will see if I feel they match my question. Ideally in the SQL I could make new column that returned a count of the unitid and I did attempt this but failed, and then I could filter on that column to remove any that only had one count. I really don't think this should be difficult but I have spent about 4 hours on it so far.
Thanks in advance!
Steven
If I understood your question right, the following should give you what's needed :
SELECT * FROM
(
SELECT
COMPSTSB.UNITID,
COMPSTSB.UNITTYPE,
ACTDEFN.ACTDESC,
ACTDEFN.ACTCODE,
HISTORY.WONO,
HISTORY.COMPFLAG,
HISTORY.ADDDTTM,
HISTORY.COMMENTS,
COUNT(1) OVER (PARTITION BY COMPSTSB.UNITID) AS numDups
FROM (IMSV7.COMPSTSB COMPSTSB INNER JOIN IMSV7.HISTORY HISTORY ON
COMPSTSB.COMPKEY=HISTORY.COMPKEY) INNER JOIN IMSV7.ACTDEFN ACTDEFN ON
HISTORY.ACTKEY=ACTDEFN.ACTKEY
WHERE HISTORY.COMPFLAG='Y' AND NOT (ACTDEFN.ACTCODE='DBR' OR ACTDEFN.ACTCODE='IN')
)a
WHERE a.numDups >1
ORDER BY COMPSTSB.UNITTYPE, COMPSTSB.UNITID, HISTORY.ADDDTTM
I expect you are concerned about having duplicate values of UNITID in the IMSV7.COMPSTSB table. If so adding this join to your query should enable ou to identify them:
JOIN (SELECT COMPSTSB.UNITID
FROM IMSV7.COMPSTSB
GROUP BY COMPSTSB.UNITID
HAVING COUNT(COMPSTSB.UNITID) > 1) dups
ON DUPS.UNITID = COMPSTSB.UNITID
JOIN IMSV7.HISTORY HISTORY
Here's the full query:
SELECT COMPSTSB.UNITID
, COMPSTSB.UNITTYPE
, ACTDEFN.ACTDESC
, ACTDEFN.ACTCODE
, HISTORY.WONO
, HISTORY.COMPFLAG
, HISTORY.ADDDTTM
, HISTORY.COMMENTS
FROM IMSV7.COMPSTSB COMPSTSB
JOIN (SELECT COMPSTSB.UNITID
FROM IMSV7.COMPSTSB
GROUP BY COMPSTSB.UNITID
HAVING COUNT(COMPSTSB.UNITID) > 1) dups
ON DUPS.UNITID = COMPSTSB.UNITID
JOIN IMSV7.HISTORY HISTORY
ON COMPSTSB.COMPKEY = HISTORY.COMPKEY
JOIN IMSV7.ACTDEFN ACTDEFN
ON HISTORY.ACTKEY = ACTDEFN.ACTKEY
WHERE HISTORY.COMPFLAG = 'Y'
AND ACTDEFN.ACTCODE NOT IN ('DBR','IN')
ORDER BY COMPSTSB.UNITTYPE
, COMPSTSB.UNITID
, HISTORY.ADDDTTM;
Since the above query didn't work you can try outer joins to similar sub queries on each of your tables and limit to only records where the outer joined table returns data. This will show you which tables in your query are cuasing your extra rows.:
SELECT COMPSTSB.UNITID
, COMPSTSB.UNITTYPE
, ACTDEFN.ACTDESC
, ACTDEFN.ACTCODE
, HISTORY.WONO
, HISTORY.COMPFLAG
, HISTORY.ADDDTTM
, HISTORY.COMMENTS
, DUPS.UNITID UNITID_DUP
, DUPS2.COMPKEY COMPKEY_DUP
, DUPS3.ACTKEY ACTKEY_DUP
FROM IMSV7.COMPSTSB COMPSTSB
JOIN IMSV7.HISTORY HISTORY
ON COMPSTSB.COMPKEY = HISTORY.COMPKEY
JOIN IMSV7.ACTDEFN ACTDEFN
ON HISTORY.ACTKEY = ACTDEFN.ACTKEY
LEFT JOIN (SELECT COMPSTSB.UNITID
FROM IMSV7.COMPSTSB
GROUP BY COMPSTSB.UNITID
HAVING COUNT(COMPSTSB.UNITID) > 1) dups
ON DUPS.UNITID = COMPSTSB.UNITID
LEFT JOIN (SELECT HISTORY.COMPKEY
FROM IMSV7.HISTORY
GROUP BY HISTORY.COMPKEY
HAVING COUNT(HISTORY.COMPKEY) > 1) dups2
ON DUPS.UNITID = COMPSTSB.UNITID
LEFT JOIN (SELECT ACTDEFN.ACTKEY
FROM IMSV7.ACTDEFN
GROUP BY ACTDEFN.ACTKEY
HAVING COUNT(ACTDEFN.ACTKEY) > 1) dups3
ON DUPS.UNITID = COMPSTSB.UNITID
WHERE HISTORY.COMPFLAG = 'Y'
AND ACTDEFN.ACTCODE NOT IN ('DBR','IN')
AND ( DUPS.UNITID IS NOT NULL OR
DUPS2.COMPKEY IS NOT NULL OR
DUPS3.ACTKEY IS NOT NULL)
ORDER BY COMPSTSB.UNITTYPE
, COMPSTSB.UNITID
, HISTORY.ADDDTTM;

Filter a query in Oracle

I have the following query
select a.empid, a.age, a.city, b.name
join supervisor b on a.supervisorid = b.empid
There is a chance that entries in "Supervisor" table may not be present in "Employee" table as an Employee
After forming the above query , i want to make "b.supervisorname" field as "null", if "b.supervisorid" not in "a.empid" column
EMPLOYEE TABLE:
EMPID--AGE--CITY--SUPERVISOR
1--12--A--123
2--21--B--1
3--23--C--2
Supervisor Table:
SUPERVISOR TABLE
EMPID--NAME
123--ABC
1--EFG
2-HIJ
OUTPUT:
EMPID--AGE--CITY--NAME
1--12--A--null
2--21--B--ABC
3--23--C--EFG
i dont want to use,
select a.empid, a.age, a.city, b.name
from employee a
join supervisor b on a.supervisorid =
(select empid
from supervisor
where empid in (select empid from employee))
as this kind of querying affects the performance
Is there any shortcut way to do it?
You should ALWAYS use explicit joins to avoid performance issues. And in general it helps to define a FROM clause in queries
The query below should work for you:
select
e.empid,
e.age,
e.city,
s.name
FROM
employee e
LEFT OUTER JOIN
supervisor s
on e.supervisor = s.empid

Using an alias in a select statement in a join query

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.

Resources