Oracle, comparing issue - oracle

Good day!
Folks, I have an issue when I want to compare multiple values.
For example I have the Log Data for today, 23rd April. This is related to yesterday's job(22nd April) with specific name.
In my comparison, I should select all logs with their job dates with this specific name. I say that logs' dates must be bigger than our job with this specific name. There are other jobs earlier done with this specific name.
How can I choose only the last nearest one?
Please could you advise?
For example:
Table: Job
id
name
job start date
Table: Log
id
type
Log date
We have 3 different jobs with name "Coding"
and 3 different logs.
Jobs:
1, coding, 21-Apr-10
2, coding, 21-Apr-14
3, coding, 21-Apr-18
Logs:
1. 1, error, 23-Apr-10
2. 2, error, 23-Apr-15
3. 3, error, 20-Apr-18
4. 4, error, 23-Apr-18
And there, when I want to execute logs and jobs, I must look to their dates. For example, error with id 1 is related to job with id 1, because log's date is bigger than job's date.
Or also, log with id 3 should be related to job with id 2.

Generally speaking, something like this might do the job:
select *
from some_table t
where t.date_column = (select max(t1.date_column)
from some_table t1
where t1.id = t.id
)
If it doesn't, please, provide test case (CREATE TABLE and INSERT INTO sample data, which would be the input), and - based on that, tell us what you expect as the output.
[EDIT]
This is somewhat strange data model; jobs and logs aren't related by anything but dates ... anyway, here's code that returns what you described (at least, how I understood it).
SQL> with tjob (id, name, job_start) as
2 (select 1, 'coding', to_date('21.04.2010', 'dd.mm.yyyy') from dual union
3 select 2, 'coding', to_date('21.04.2014', 'dd.mm.yyyy') from dual union
4 select 3, 'coding', to_date('21.04.2018', 'dd.mm.yyyy') from dual
5 ),
6 logs (id, type, log_date) as
7 (select 1, 'error', to_date('23.04.2010', 'dd.mm.yyyy') from dual union
8 select 2, 'error', to_date('23.04.2015', 'dd.mm.yyyy') from dual union
9 select 3, 'error', to_date('20.04.2018', 'dd.mm.yyyy') from dual union
10 select 4, 'error', to_date('23.04.2018', 'dd.mm.yyyy') from dual
11 )
12 select j.id job_id
13 from tjob j
14 where j.job_start = (select max(j1.job_start)
15 from tjob j1
16 where j1.job_start < (select l.log_date
17 from logs l
18 where l.id = &log_id));
Enter value for log_id: 1
JOB_ID
----------
1
SQL> /
Enter value for log_id: 3
JOB_ID
----------
2
SQL>

Related

SQL: Is there a way to exclude duplicate results if a condition is met

I am trying to build a query that will search for a certain user and the most recent job they were associated with. I want to only pull the most recent date but some users have two jobs associated with the same date. Is there a way to only pull one of those while not excluding that result if it was a user's only job?
For example
User Job Date
1 Chef 7/13/21
1 Server 7/13/21
2 Server 7/3/21
3 Chef 7/1/21
Desired result:
User Job Date
1 Chef 7/13/21
2 Server 7/3/21
3 Chef 7/1/21
Thanks!
Is it possible? Yes, but data you posted as example doesn't reflect what you're saying because for user = 1 both jobs have same date.
Anyway, here's one option how to do that: use one of analytic functions (I used row_number; rank might also be an option) to "rank" rows partitioned by user & sorted by date, and then fetch the one with the lowest rank.
Sample data till line #6; query begins at line #7.
SQL> with test (cuser, job, datum) as
2 (select 1, 'chef' , date '2021-07-13' from dual union all
3 select 1, 'server', date '2021-07-13' from dual union all
4 select 2, 'server', date '2021-07-03' from dual union all
5 select 3, 'chef' , date '2021-07-01' from dual
6 ),
7 temp as
8 (select cuser, job, datum,
9 row_number() over (partition by cuser order by datum desc) rn
10 from test
11 )
12 select cuser, job, datum
13 from temp
14 where rn = 1;
CUSER JOB DATUM
--------- ------ ----------
1 chef 07/13/2021
2 server 07/03/2021
3 chef 07/01/2021
SQL>
Make use of keep dense_rank
https://oracle-base.com/articles/misc/rank-dense-rank-first-last-analytic-functions
select user, date,
MIN(job) KEEP (DENSE_RANK FIRST ORDER BY date)
from table
group by user, date
A rough example
http://sqlfiddle.com/#!4/14ee4d/2
Now this is not quite deterministic as when there are two jobs on the same date you need to specify how to sort them. If your date column includes time then you should be OK

Oracle Rest Data Service Order by desc nulls last not working

I am not able order by field_name DESC with null last option. Since the default behaviour on DESC is nulls first, I would like them to be pushed at last.
"$orderby": {"ENAME":"DESC NULLS LAST"}
Thanks in advance
How about such a trick? If ENAME is NULL, use CHR(ASCII(1E4)) - something that is, hopefully, far enough to avoid valid first characters in actual names in your database.
SQL> with test (id, ename) as
2 (select 1, 'Little' from dual union
3 select 2, null from dual union
4 select 3, 'Zoot' from dual
5 )
6 select *
7 from test
8 order by decode(ename, null, chr(ascii(1E4)), ename) desc;
ID ENAME
---------- ------
2
3 Zoot
1 Little
SQL>

how to filter history data based on most recent record?

Table: HISTORY
CUSTOMER MONTH PLAN
1 1 A
1 2 A
1 2 B
1 3 B
In this example customer 1 had plan A and changed to B on month 2. I need to remove the change from month 2 and keep only the plan the customer migrate to, as in:
CUSTOMER MONTH PLAN
1 1 A
1 2 B
1 3 B
I've tried using sys_connect_by_path:
select month, CUSTOMER, level,
sys_connect_by_path(PLAN, '/') as path
from a
start with month = 1
connect by prior MONTH = MONTH - 1
But it doesn't seem to be right. Whats an efficient way of doing it in Oracle 12c?
I'm not sure whether you understood what comments have said - it is rows 2 and 3 that are questionable because there's no way to know which one of those happened first.
Anyway, as you said that there's nothing else in that table that would help us decide, how about something like this? Compare current plan with the next plan (sorted by month) and pick rows where there's no change in plan.
SQL> with test (customer, month, plan) as
2 (select 1, 1, 'A' from dual union all
3 select 1, 2, 'A' from dual union all
4 select 1, 2, 'B' from dual union all
5 select 1, 3, 'B' from dual
6 ),
7 inter as
8 (select customer, month, plan,
9 nvl(lead(plan) over (partition by customer order by month), plan) lead_plan
10 from test
11 )
12 select customer, month, plan
13 from inter
14 where plan = lead_plan
15 order by month;
CUSTOMER MONTH PLAN
---------- ---------- -----
1 1 A
1 2 B
1 3 B
SQL>
You could use an analytic lead() call to peek at the following month, and decide whether to use the current or following month's plan:
-- CTE for your sample data
with history (customer, month, plan) as (
select 1, 1, 'A' from dual
union all select 1, 2, 'A' from dual
union all select 1, 2, 'B' from dual
union all select 1, 3, 'B' from dual
)
-- actual query
select distinct customer, month,
case
when lead(plan) over (partition by customer order by month) != plan
then lead(plan) over (partition by customer order by month)
else plan
end as plan
from history;
CUSTOMER MONTH P
---------- ---------- -
1 1 A
1 2 B
1 3 B
You could move the lead() calculation into an inline view to reduce the repetition if you prefer.
This will probably break in interesting ways if a customer changes plan on consecutive months though, or possibly if they can change more than once in one month.

How to use Analytic Functions in this query

I have a very unique query that I must write, and hoping to do it all in 1 query, but not sure if I can.
I have a list of Articles, each article has a unique ID. The application passes this ID to the stored procedure. Then, I am to retrieve that article, AND, the next & previous articles. So, the list is sorted by date, and I can get the next & previous.
I do this via LEAD & LAG. It works in one query. However, in some cases, that next or previous article has a NULL in one of the fields. If that field is NULL, I am basically to get the next article where that field is NOT NULL.
Then there is one more thing. The article passed from the application has a category assigned to it. The next & previous articles must be of the same category.
The query is pretty big now, but it work getting the next & previous to the article ID passed as the subquery sorts everything by date. But with these 2 new criteria, the NULL factor and category factor, I do not see how it is possible to do this in one query.
Any thoughts? Or need some examples, or my existing query?
Thanks for all your time.
Oracle Setup:
CREATE TABLE articles ( id, category, value, dt ) AS
SELECT 1, 1, 1, DATE '2017-01-01' FROM DUAL UNION ALL
SELECT 2, 1, 2, DATE '2017-01-02' FROM DUAL UNION ALL -- Previous row
SELECT 3, 1, NULL, DATE '2017-01-03' FROM DUAL UNION ALL -- Ignored as value is null
SELECT 4, 1, 1, DATE '2017-01-04' FROM DUAL UNION ALL -- Chosen id
SELECT 5, 2, 3, DATE '2017-01-05' FROM DUAL UNION ALL -- Ignored as different category
SELECT 6, 1, 5, DATE '2017-01-06' FROM DUAL; -- Next row
Query:
SELECT *
FROM (
SELECT a.*,
LAG( CASE WHEN value IS NOT NULL THEN id END ) IGNORE NULLS OVER ( PARTITION BY category ORDER BY dt ) AS prv,
LEAD( CASE WHEN value IS NOT NULL THEN id END ) IGNORE NULLS OVER ( PARTITION BY category ORDER BY dt ) AS nxt
FROM articles a
)
WHERE :your_id IN ( id, nxt, prv )
AND ( id = :your_id OR value IS NOT NULL )
ORDER BY dt;
(:your_id is set to 4 in the example output below.)
Output:
ID CATEGORY VALUE DT PRV NXT
---------- ---------- ---------- ------------------- ---------- ----------
2 1 2 2017-01-02 00:00:00 1 4
4 1 1 2017-01-04 00:00:00 2 6
6 1 5 2017-01-06 00:00:00 4

Oracle query execution in where clause

We are trying to execute a select query which uses a column name in the case statement and if the same column name is used in where clause it is going into an infinite loop.
eg
select empId,empName,
(case when empDept in ('A','B','C') then empAge
when empDept in ('E','F','G') then empExp
else 'Dept-Not found' end)
from employee where empDept in ('A','B','C','D','E','F','G')
It does not matter even if we put or clause in the where clause instead of in.
EDIT:Edited the Query
clearly there is something else going on. The WHERE clause is evaluated before the SELECT (column list) clause, there is no way it could produce an infinite loop.
Consider (10.2.0.1):
SQL> CREATE TABLE employee AS
2 SELECT 1 empId, 'e1' empName, 1 empAge, 10 empExp, 'A' empDept FROM dual
3 UNION ALL SELECT 2, 'e2', 2, 9, 'B' FROM dual
4 UNION ALL SELECT 3, 'e3', 3, 8, 'C' FROM dual
5 UNION ALL SELECT 4, 'e4', 4, 7, 'D' FROM dual
6 UNION ALL SELECT 5, 'e5', 5, 6, 'E' FROM dual
7 UNION ALL SELECT 6, 'e6', 6, 5, 'F' FROM dual;
Table created
SQL> select empId,empName,
2 (case when empDept in ('A','B','C') then to_char(empAge)
3 when empDept in ('E','F','G') then to_char(empExp)
4 else 'Dept-Not found' end)
5 from employee where empDept in ('A','B','C','D','E','F','G');
EMPID EMPNAME (CASEWHENEMPDEPTIN('A','B','C'
---------- ------- ----------------------------------------
1 e1 1
2 e2 2
3 e3 3
4 e4 Dept-Not found
5 e5 6
6 e6 5
As you can see in my example I had to add a to_char to your case expressions because all results from a case must have the same type. Without the to_char, in my case I obtained an ORA-00932. Maybe your tool hangs when a query returns with an error?
As a pretty wild guess based on the little information you've given, I would say that without the WHERE clause the query is doing a full table scan, but when you add the WHERE clause it switches to an index range scan. If records for the six departments you are interested in are spread throughout the table, it is very possible that an index range scan will be much less efficient than a table scan.
One possible cause of this is that statistics on the table and index are stale.
In any case, your first step when diagnosing a SQL performance problem should be to look at the execution plan.

Resources