How to return names of events scheduled on the same date at the same location as 'JOHN BIRTHDAY' - oracle

I got this question in the exam wrong, and I want to know how to fix it? How to return names of events scheduled on the same date at the same location as 'JOHN BIRTHDAY'?
Select ename from event e1, location L where e1.LID=L.LID AND
E1.ENAME='JOHN BIRTHDAY' AND E1.START_DATE=DATE'2017-01-02';
EVENT TABLE: LID= LOCATION ID , AND MID = MEMBER ID
EID ENAME MID LID START_DAT END_DATE
1 JOHN BIRTHDAY 1 2 02-JAN-17 02-JAN-17
2 SAMI BIRTHDAY 2 1 02-JAN-17 02-JAN-17
4 THANKSGIVING 2 2 02-JAN-17 02-JAN-17
3 SUSAN GRADUATION 1 2 02-JAN-17 02-JAN-17

What you did wrong...
You are looking up data in two tables, EVENT and LOCATION. You match them by LOCATION_ID - no harm there. But then, from EVENT you select just one row: the row where ENAME = 'JOHN BIRTHDAY'. Then the first mistake: to that, you add "and START_DATE - ..." No need for that; you already limited the rows from the EVENT table to the row with ENAME = 'JOHN BIRTHDAY'.
Then: you are joining to LOCATION for absolutely no reason; you are not selecting anything from LOCATION. (Nor do you need to, since the problem is asking you to find other events that share the same location, meaning the same LOCATION_ID, and that is already available in the EVENT table.)
The biggest mistake: nowhere in your code are you looking for OTHER events. With some constraints: same date and location as 'JOHN BIRTHDAY', but that is irrelevant since you are not looking for ANY other events, anywhere in the code.
How to solve this problem correctly:
You need to look up data in EVENT twice. One time to find out when and where 'JOHN BIRTHDAY' will be. (It is not stated in the problem, but the convention during an exam is that you are not supposed to read the date and location in the table, write them down, and then hard-code them in your query; instead, they should be "variables" in the solution.) Second time you need to look IN THE SAME TABLE to find other events with the same date and location.
So this means you will need to join EVENT to itself (give it two different aliases, so you can refer to them clearly - "this" copy vs. "that" copy of the table). Join them by LID and ... date ... but that's more complicated (see below). Select the ENAME from one copy, while in the other copy put the WHERE condition: where ENAME = 'JOHN BIRTHDAY'.
Something like
select e1.ename
from event e1 join event e2 on e1.lid = e2.lid and e1.start_date = e2.start_date
where e2.ename = 'JOHN BIRTHDAY'
;
which is what Darzen posted in a different Answer before I typed mine.
This may be all you need, except for the following interesting question. The base data has a START_DATE and an END_DATE, not just a straight DATE. Does that mean that an event may be from 9 AM to noon, while 'JOHN BIRTHDAY' is from 6 PM to 8:30 PM? Or perhaps, does it mean that some events may extend for two or three days, and you need to find all those that overlap with 'JOHN BIRTHDAY'?
That makes the problem more interesting, but let's leave it alone; what I shared with you so far may suffice.
Final note - you may be better off learning modern (SQL Standard) notation for joins. You used Oracle syntax, which is not wrong, but it is not the best option.

select b.ename
from events a ,
(select ename, lid, start_Date from events) b
where a.lid = b.lid and a.start_date = b.start_date
and a.ename = 'JOHN BIRTHDAY' and b.ename <> 'JOHN BIRTHDAY';

Looks to me like
WITH cteJOHN_BIRTHDAY AS (SELECT *
FROM EVENT
WHERE ENAME = 'JOHN BIRTHDAY')
SELECT e.ENAME
FROM EVENT e
INNER JOIN cteJOHN_BIRTHDAY j
ON j.START_DAT = e.START_DAT AND
j.LID = e.LID AND
j.EID <> e.EID
ought to do it.
Not tested on animals - you'll be first! :-)

Related

How to find changed values in column

I have a table which looks similar to:
AcctNbr
AcctTypCD
ContractDate
Emp
WrkStLct
WrkStRgn
10001
12M
11-01-2021
John Smith
Downtown
D
10002
BCK
11-02-2021
Jane Smith
Uptown
U
10003
HPLS
11-03-2021
Bob Jones
Midtown
M
10005
VPLS
11-04-2021
Chris Ice
Downtown
D
10006
CLBV
11-12-2021
Smith John
Uptown
U
10007
TI80
11-13-2021
Joann Penn
Midtown
M
10008
M360
10-04-2021
Jim Blue
Downtown
D
My initial query is:
Select acctnbr, accttypcd, contractdate, emp, wrkstlct, wrkstrgn
from tableA
where accttypcd in ('HPLS', 'VPLS')
and contractdate between trunc(sysdate,'mm') and sysdate
order by wrkstrgn, wrkstlct, emp, contractdate;
End users are requesting now a report which pulls back any time AcctTypCD changes from any value (a list of up to 80+ different values) to either 'HPLS' or 'VPLS' and the emp who made the change, what would be the best way to accomplish this?
I apologize in advance for any initial mistakes in this question or if this is a duplicate, first time asking a question.
Its hard to determine fully what you want to achieve given that every row has a different AcctNbr, but your description says "If acctnbr 10007 changes from ...". I will assume that a change means a new row in the table. On that assumption, you could do something like
Select acctnbr, accttypcd, contractdate, emp, wrkstlct, wrkstrgn,
lag(accttypcd) over ( partition by acctnbr order by contractdate)
from tableA
where accttypcd in ('HPLS', 'VPLS')
and contractdate between trunc(sysdate,'mm') and sysdate
order by wrkstrgn, wrkstlct, emp, contractdate;
where the LAG function will show you the previous value of 'accttypcd' where the definition of "previous" is segmented by acctnbr (the 'partition by' part) and sequenced by contractdata (the 'order by' part).
Analytic SQL (like lag, lead etc) is a big topic, so you can get a full tutorial here
https://www.youtube.com/watch?v=0cjxYMxa1e4&list=PLJMaoEWvHwFIUwMrF4HLnRksF0H8DHGtt

Consolidate rows

I'm trying to cut down on rows a report has. There are 2 assets that return on this query but I want them to show up on one row.
Basically if dc.name LIKE '%CT/PT%' then I want it to be same row as the asset. The SP.SVC_PT_ID is the common field to join them.
There will be times when there is no dc.name LIKE '%CT/PT%' however I still want the DV.MFG_SERIAL_NUM to populated just with a Null to the right.
Select SP.SVC_PT_ID, SP.DEVICE_ID, DV.MFG_SERIAL_NUM, dc.name,
substr(dc.name,26)
From EIP.SVC_PT_DEVICE_REL SP,
eip.device_class dc,
EIP.DEVICE DV
Where SP.EFF_START_TIME < To_date('20170930', 'YYYYMMDD') + 1
and SP.EFF_END_TIME is null
and dc.id = DV.device_class_id
and DV.ID = SP.device_id
ORDER BY SP.SVC_PT_ID, DV.MFG_SERIAL_NUM;
I'm not sure I understand what you are saying; test case would certainly help. You said that query you posted returns two rows (only if we saw which ones ...) but you want them to be displayed as the image you attached to the message.
Generally speaking, you can do that using an aggregate function (such as MAX) on certain column(s), along with the GROUP BY clause that contains the rest of them.
Just for example:
select svc_pt_id, max(ctpt_name) ctpt_name, sum(ctpt_multipler) ctpt_multipler
from ...
group by svc_pt_id
As I said: a test case would help people who'd want to answer the question. True - someone might have understood it far better than I did and will provide assistance nevertheless.
EDIT: after you posted sample data (which, by the way, don't match screenshot you posted previously), maybe something like this might do the job: use analytic function to check whether name contains CT/PT; if so, take its data. Otherwise, display both rows.
SQL> with test as (
2 select 14 svc_pt_id, 446733 device_id, 'Generic Electric' name from dual union
3 select 14, 456517, 'Generic CT/PT, Multiplier' from dual
4 ),
5 podaci as
6 (select svc_pt_id, device_id, name,
7 rank() over (partition by svc_pt_id
8 order by case when instr(name, 'CT/PT') > 1 then 1
9 else 2
10 end) rnk
11 from test
12 )
13 select svc_pt_id, device_id, name
14 from podaci
15 where rnk = 1;
SVC_PT_ID DEVICE_ID NAME
---------- ---------- -------------------------
14 456517 Generic CT/PT, Multiplier
SQL>
My TEST table (created by WITH factoring clause) would be the result of your current query.

Oracle : Date Checking is used last 6months or not

I have table "CONTACT", if contact createddate is not used last 6 months in some other table i need to remove the Contact, can anyone help to resolve this?
If you want to identify contacts that have not had an order in the last 6 months
select c.contact_id
from contact c
where not exists( select 1
from orders o
where o.contact_id = c.contact_id
and o.order_date > add_months(sysdate,-6) )
To delete the rows
delete from contact c
where not exists( select 1
from orders o
where o.contact_id = c.contact_id
and o.order_date > add_months(sysdate,-6) )
That being said, it seems unlikely that you'd want to do this. If you have a reasonable data model, deleting a contact would mean that you had to delete any order older than 6 months which seems like a bad idea. Perhaps it would make more sense to set an active_flag to 'N' in that case.

How to efficiently evaluate rows and sub records in single PL/SQL

I'm struggling writing PL/SQL (I'm new to PL/SQL) and I'm not sure how to structure SQL and loops for something like this.
I have 128 lines of SQL to create something like the following cursor:
ID Course Grade Attend? Date
123 MATH091 B Y 5/15
123 BIOL101 F N 3/10
123 ENGL201 W Y 1/2
456 MATH091 A Y 5/16
456 CHEM101 C Y 5/16
456 POLS301 NULL NULL NULL
With each ID, I need to several comparisons across the courses (e.g. which has the latest date, or were all courses attended). These comparisons need to be done in a certain order so that when they hit one that is true, they are flagged with a code and excluded from subsequent comparisons.
For example:
All courses attended? If true, output as attended and remove from next steps.
Find and store the latest date with a passing grade.
Find and store the latest date with a non-passing grade.
Is the later date after a course with a null grade? If true, output as coming back and remove from next steps.
Etc.
Each condition can be easily written in a SQL, but I don't know/understand the appropriate structure to loop through this process.
Is there syntax that can accomplish this easily?
We're on Oracle 11g and we do not have permissions to write to a temporary table.
I don't think you need PL/SQL for this. Except for the "unknown" requirement "etc." this can all be done in a single SQL statement:
Something like:
select id, course, grade, attended, attendance_date,
count(distinct case when attended = 'Y' then course end) over (partition by id) courses_attended,
count(distinct course) over () as total_courses,
case
when count(distinct case when attended = 'Y' then course end) over (partition by id) = count(distinct course) over () then 'yes'
else 'no'
end as all_courses_attended,
max(case when attended <> 'F' then attendance_date else null end) over (partition by id) as latest_passing_date,
max(case when attended = 'F' then attendance_date else null end) over (partition by id) as latest_non_passing_date
from attendees
order by id;
Btw: the attended column is not necessary if you have an attendance_date. If that date is not NULL than obviously the student attended the course. Otherwise she/he didn't.
Of course I have no idea what the "etc." steps should do though....
SQLFiddle example: http://sqlfiddle.com/#!4/e7c95/1

Selecting data from one table or another in multiple queries PL/SQL

The easiest way to ask my question is with a Hypothetical Scenario.
Lets say we have 3 tables. Singapore_Prices, Produce_val, and Bosses_unreasonable_demands.
So Prices is a pretty simple table. Item column containing a name, and a Price column containing a number.
Produce_Val is also simple 2 column table. Type column containing what type the produce is (Fruit or veggie) and then Name column (Tomato, pineapple, etc.)
The Bosses_unreasonable_demands only contains one column, Fruit, which CAN contain the names of some fruits.
OK? Ok.
SO, My boss wants me to write a query that returns the prices for every fruit in his unreasonable demands table. Simple enough. BUT, if he doesn't have any entries in his table, he just wants me to output the prices of ALL fruits that exist in produce_val.
Now, assuming I don't know where the DBA who designed this silly hypothetical system lives (and therefore can't get him to fix this), our query would look like this:
if <Logic to determine if Bosses demands are empty>
Then
select Item, Price
from Singapore_Prices
where Item in (select Fruit from Bosses_Unreasonable_demands)
Else
select Item, Price
from Singapore_Prices
where Item in (select Name from Produce_val where type = 'Fruit')
end if;
(Well, we'd select those into a variable, and then output the variable, probably with bulk-collect shenanigans, but that's not important)
Which works. It is entirely functional, and won't be slow, even if we extend it out to 2000 other stores other than Singapore. (Well, no slower than anything else that touches 2000 some tables) BUT, I'm still doing two different select statements that are practically identical. My Comp Sci teacher rolls in their grave every time my fingers hit ctrl-V. I can cut this code in half and only do one select statement. I KNOW I can.
I just have no earthly idea how. I can't use cursors as an in statement, I can't use nested tables or varrays, I can't use cleverly crafted strings, I... I just... I don't know. I don't know how to do this. Is there a way? Does it exist?
Or do I have to copy/paste forever?
Your best bet would be dynamic SQL, because you can't parameterize table or column names.
You will have a SQL query template, have a logic to determine tables and columns that you want to query, then blend them together and execute.
Another aproach, (still a lot of ctrl-v like code) is to use set construction UNION ALL:
select 1st query where boss_condition
union all
select 2nd query where not boss_condition
Try this:
SELECT *
FROM (SELECT s.*, 'BOSS' AS FRUIT_SOURCE
FROM BOSSES_UNREASONABLE_DEMANDS b
INNER JOIN SINGAPORE_FRUIT_LIST s
ON s.ITEM = b.FRUIT
CROSS JOIN (SELECT COUNT(*) AS BOSS_COUNT
FROM BOSSES_UNREASONABLE_DEMANDS)) x
UNION ALL
(SELECT s.*, 'NORMAL' AS FRUIT_SOURCE
FROM PRODUCE_VAL p
INNER JOIN SINGAPORE_FRUIT_LIST s
ON (s.ITEM = p.NAME AND
s.TYPE = 'Fruit')
CROSS JOIN (SELECT COUNT(*) AS BOSS_COUNT
FROM BOSSES_UNREASONABLE_DEMANDS)) n
WHERE (BOSS_COUNT > 0 AND FRUIT_SOURCE = 'BOSS') OR
(BOSS_COUNT = 0 AND FRUIT_SOURCE = 'NORMAL')
Share and enjoy.
I think you can use nested tables. Assume you have a schema-level nested table type FRUIT_NAME_LIST (defined using CREATE TYPE).
SELECT fruit
BULK COLLECT INTO my_fruit_name_list
FROM bosses_unreasonable_demands
;
IF my_fruit_name_list.count = 0 THEN
SELECT name
BULK COLLECT INTO my_fruit_name_list
FROM produce_val
WHERE type='Fruit'
;
END IF;
SELECT item, price
FROM singapore_prices
WHERE item MEMBER OF my_fruit_name_list
;
(or, WHERE item IN (SELECT column_value FROM TABLE(CAST(my_fruit_name_list AS fruit_name_list)) if you like that better)

Resources