Accesing parent identifier in an ordered nested subquery - sql

I need a query to get the value of an item together with the value of the previous item if exists.
I am using the following query (a simplification of the actual):
select v1.value item_value,
nvl(
(
select * from (
select v2.value
from ITEMS v2
where v2.insert_date<v1.insert_date
order by v2.insert_date desc
) where rownum=1
), 0
) as previous_value
from ITEMS v1
where v1.item_id=1234
This query won't work (ORA-00904) because I am using v1.insert_date in an inner select with two levels of nesting.
How can I achieve this with Oracle 11?

I think you can achieve this with analytic function LAG. More info about analytic functions LAG LEAD
I created a sample query:
with items as (
select 1 as value, sysdate as insert_date from dual
union all
select 2 as value, sysdate-1 as insert_date from dual
union all
select 3 as value, sysdate+1 as insert_date from dual
)
select v1.value item_value,
lag(v1.value,1,0) over (order by v1.insert_date desc) as previous_value,insert_date
from ITEMS v1
order by insert_date desc

Related

Oracle Select max where a certain key is matched

i'm working with oracle, plSql, i need to query a table and select the max id where a key is matched, now i have this query
select t.* from (
select distinct (TO_CHAR(I.DATE, 'YYMMDD') || I.AUTH_CODE || I.AMOUNT || I.CARD_NUMBER) as kies, I.SID as ids
from transactions I) t group by kies, ids order by ids desc;
It's displaying this data
If i remove the ID from the query, it displays the distinct keys (in the query i use the alias KIES because keys was in blue, so i thought it might be a reserved word)
How can i display the max id (last one inserted) for every different key without displaying all the data like in the first image??
greetings.
Do you just want aggregation?
select thekey, max(sid)
from (select t.*,
(TO_CHAR(t.DATE, 'YYMMDD') || t.AUTH_CODE || t.AMOUNT || t.CARD_NUMBER) as thekey,
t.SID
from transactions t
) t
group by thekey
order by max(ids) desc;
Since you haven't provided data in text format, its difficult to type such long numbers and recreated the data.
However I think you can simply use the MAX analytical function to achieve your results.
with data as (
select 1111 keys,1 id from dual
union
select 2222, 1 from dual
union
select 1111, 2 from dual
union
select 2222,3 from dual
union
select 9999, 1 from dual
union
select 1111, 5 from dual
)
select distinct keys, max(id) over( partition by (keys)) from data
This query returns -
KEYS MAX(ID)OVER(PARTITIONBY(KEYS))
1111 5
9999 1
2222 3

Table with 2 dates I want a single list of unique dates

Say for example I have a table with date start and date end
Item 1 10/2/2019 12/2/2019
Item 2 10/2/2019 15/2/2019.
I wish to have a result of
Item 1 10/2/2019
Item 2 12/2/2019
Item 3 15/2/2019
In a single column that I can use for further queries
Can’t think of how to get the desired result
See above
Here is what you asked for if you are using Oracle:
select 'Item ' || ROW_NUMBER() OVER(
ORDER BY date_row
) row_num
, date_row
from (
select start_date as Date_row from table1
union
select end_date as Date_row from table1);
And here is the DEMO
Here is what you asked for if you are using MySQL:
select concat("Item ", ROW_NUMBER() OVER(ORDER BY date_row)) as Item
, date_row
from (
select start_date as Date_row from table1 as t1
union
select end_date as Date_row from table1 as t2
) as test;
And here is the DEMO
Here is what you asked for if you are using Postgres:
select 'Item ' || ROW_NUMBER() OVER(
ORDER BY date_row
) row_num
, date_row
from (
select start_date as Date_row from table1 as t1
union
select end_date as Date_row from table1 as t2) as t3;
And here is the DEMO
Probably this:
SELECT 'startdate' as datekind, startdate
FROM table
UNION
SELECT 'enddate', enddate
FROM table
The kind is optional but I added it in to demo how you would retain knowledge of whether a date was start or end. You can add other columns like ID in in the same way
If you don't want to squish duplicates add the word ALL after UNION
NOTE - the presence of the kind column will influence whether a date is deemed a duplicate of another row or not. This query can still produce repeated dates if one is a start and the other an end. If this is unacceptable, remove the dateline column (and accept that you won't know what they are)
If we're generating a unique list of dates and the lowest item associated:
SELECT x.d, min(x.item) as i
FROM(
SELECT startdate as d, item FROM table
UNION ALL
SELECT enddate, item FROM table
) x
GROUP BY x.d

Trying to find the most recent date where a status field has changed in Oracle SQL

I have a table that shows a full history of location ID's (LOCN_ID), which includes an ACTIVE_STATUS field showing A for active, or I for inactive. Each time a location's active status changes, a new record is created with a new OP_DATE. However, any time the EXTERNALLY_VISIBLE field in the table gets changed, another record with a new OP_DATE is also created.
Here is an example of this.
For each LOCN_ID in the table, I need to be able to find the most recent OP_DATE that the ACTIVE_STATUS field changed (to either I or A). I don't care about when the EXTERNALLY_VISIBLE field changed. For the LOCN_ID shown in the example, the result should be:
OP_DATE LOCN_ID ACTIVE_STATUS
12/9/11 7:34 558732 I
There are also some cases where a LOCN_ID's active status will have never changed, in which case the result should be the oldest OP_DATE for that LOCN_ID.
How would I be able to write a query in Oracle SQL to show this desired output for each LOCN_ID?
You have to handle both situations, when there is a row where status changed and when it's absent. Lag() is obvious as it is designed to find previous values. Optional is older, slower self-join. Also we need row_number(), because you have complicated conditional ordering. In row_number as first part we need descending order, then ascending in case there were no status changes. It can be done like here:
select op_date, locn_id, active_status
from (
select a.*, row_number()
over (partition by locn_id
order by case when active_status <> las then sysdate-op_date end,
op_date) as rn
from (select t.*, lag(active_status) over (partition by locn_id order by op_date) las
from t) a)
where rn = 1
dbfiddle demo
You can use row_number():
select t.*
from (select t.*,
row_number() over (partition by locn_id order by op_date desc) as seqnum
from (select t.*,
lag(active_status) over (partition by locn_id order by op_date) as prev_active_status
from t
) t
where prev_active_status <> active_status
) t
where seqnum = 1;
I have created the following query for you using LEFT JOIN:
-- SAMPLE DATA
WITH DATAA (OP_DATE, LOCN_ID, ACTIVE_STATUS, EXTERNALLY_VISIBLE)
AS
(
SELECT TO_DATE('04/06/2013 2:31','MM/DD/RRRR HH24:MI'), 558732, 'I', 'Y' FROM DUAL UNION ALL
SELECT TO_DATE('12/09/2011 7:34','MM/DD/RRRR HH24:MI'), 558732, 'I', 'N' FROM DUAL UNION ALL
SELECT TO_DATE('10/02/2011 3:05','MM/DD/RRRR HH24:MI'), 558732, 'A', 'N' FROM DUAL UNION ALL
SELECT TO_DATE('10/02/2011 2:59','MM/DD/RRRR HH24:MI'), 558732, 'I', 'N' FROM DUAL UNION ALL
SELECT TO_DATE('10/02/2011 3:00','MM/DD/RRRR HH24:MI'), 558732, 'I', 'Y' FROM DUAL UNION ALL
SELECT TO_DATE('04/09/2011 2:18','MM/DD/RRRR HH24:MI'), 558732, 'A', 'Y' FROM DUAL
),
-- ACTUAL QUERY STARTS FROM HERE
CTE(OP_DATE, LOCN_ID, ACTIVE_STATUS, EXTERNALLY_VISIBLE, RN) AS (
SELECT
D.*,
ROW_NUMBER() OVER(
PARTITION BY LOCN_ID
ORDER BY
OP_DATE
) AS RN
FROM
DATAA D
)
SELECT
OP_DATE,
LOCN_ID,
ACTIVE_STATUS
FROM
(
SELECT
A.OP_DATE,
A.LOCN_ID,
A.ACTIVE_STATUS,
ROW_NUMBER() OVER(
PARTITION BY A.LOCN_ID
ORDER BY
A.OP_DATE DESC
) AS RN
FROM
CTE A
LEFT JOIN CTE B ON ( A.RN = B.RN + 1 )
WHERE
( A.ACTIVE_STATUS <> B.ACTIVE_STATUS
OR B.ACTIVE_STATUS IS NULL )
)
WHERE
RN = 1;
-- Output --
OP_DATE LOCN_ID A
------------------- ---------- -
09-12-2011 07:34:00 558732 I
Demo
Cheers!!

Oracle_ ROW_NUMBER for paging hierarchy query

This time I face a problem that is paging query for a hierarchy table.
table_name(id varchar2(50), id_parent varchar2(50) );
So for sample data I have:
WITH table_name AS
(
SELECT '3' id, '' id_parent FROM DUAL UNION ALL
SELECT '5' id, '3' id_parent FROM DUAL UNION ALL
SELECT 's' id, '3' id_parent FROM DUAL UNION ALL
SELECT '4' id, 'as' id_parent FROM DUAL UNION ALL
SELECT 'aa' id, 'as' id_parent FROM DUAL UNION ALL
SELECT 'as' id, '3' id_parent FROM DUAL UNION ALL
SELECT 'ad' id, '3' id_parent FROM DUAL UNION ALL
SELECT '2' id, '' id_parent FROM DUAL
)
SELECT LPAD('-', 2 * (level - 1), '-') || id AS id1,
id_parent,
rownum --Seem not legit here
FROM table_name
START WITH id_parent IS NULL
CONNECT BY PRIOR id = id_parent
--ORDER SIBLINGS BY id
;
This is expected output:
id id_parent rownum
2 1
3 2
--5 3 3
--ad 3 4
--as 3 5
----4 as 6
----aa as 7
--s 3 8
For now I count on rownum for row_number to make paging. As Oracle hierarchy query's doc, this make sure I can display the tree as pre-order Traveral that's what we want, but as SQL in general, IMO it don't guarantee that the same-level-nodes will be sorted (or not?).
So I need something like ROW_NUMBER() OVER (ORDER SIBLINGS BY id). But I have got no luck searching for something like that.
Is there some way to work with this?
Order first and then generate the ROWNUM afterwards using a sub-query:
SELECT t.*,
ROWNUM
FROM (
SELECT LPAD('-', 2 * (level - 1), '-') || id AS id1,
id_parent
FROM table_name
START WITH id_parent IS NULL
CONNECT BY PRIOR id = id_parent
ORDER SIBLINGS BY id
) t;
You need the outer query as the order in which execution happens in a query is:
The rows are selected;
Then the WHERE clause filters are applied (and the ROWNUM pseudo-column is
generated for each row that matches all the WHERE clause filters - in this case there is no WHERE clause so all rows will be numbered);
Then the ORDER BY clause is applied.
Applying this in a single query will get the rows found in the database then give them a row number and finally order those rows (not what you want). Using an inner query you can force the ORDER BY clause to be applied first and then the generation of the row numbers will occur, subsequently, in the execution of the outer query.

Query in oracle to extract data with prevoius daytime from certain dayime

I have name and daytime columns, so the same name could have multiple dates,
what would be the query in oracle to extract data with prevoius daytime from certain dayime, for example:
name daytime
t12 12-Mar-2016
t12 14-Aug-2016
t34 13-Jan-2005
t34 18-Mar-2005
and I need:
name daytime
t12 12-Mar-2016
t34 13-Jan-2005
thanks,
S
You could use a min and group by
select name, min(daytime)
from my_table
group by name
One way is to use the analytic function row_number() (or perhaps one of its close relatives, either rank() or dense_rank(), depending on the requirement. They will all produce the same output if there are no "ties", meaning you can't have two rows with the same name and the same daytime).
This is more often used with rn = 1 to get the max daytime; if you want the "second" most recent daytime, use rn = 2.
The inner query returns one row for each row in the input table - with the same columns and values in each row and column, and with one column added, rn, showing the "rank" of each row within its group of rows with the same name (the column in partition by), ordered by daytime descending. Then it should be obvious what the outer query does.
with
test_data ( name, daytime ) as (
select 't12', to_date('12-Mar-2016', 'dd-Mon-yyyy') from dual union all
select 't12', to_date('14-Aug-2016', 'dd-Mon-yyyy') from dual union all
select 't34', to_date('13-Jan-2005', 'dd-Mon-yyyy') from dual union all
select 't34', to_date('18-Mar-2005', 'dd-Mon-yyyy') from dual
)
-- End of test data (not part of the solution). SQL query begins BELOW THIS LINE
select name, daytime
from (
select name, daytime,
row_number() over (partition by name order by daytime desc) as rn
from test_data
)
where rn = 2
;
NAME DAYTIME
---- -----------
t12 12-Mar-2016
t34 13-Jan-2005
2 rows selected.
I am not sure I understand the question, but possibly you want to use LAG. This will give you the date before any given date, assuming no duplicates. Borrowing mathguy's test data CTE, I tried:
with
test_data ( name, daytime ) as (
select 't12', to_date('12-Mar-2016', 'dd-Mon-yyyy') from dual union all
select 't12', to_date('14-Aug-2016', 'dd-Mon-yyyy') from dual union all
select 't34', to_date('13-Jan-2005', 'dd-Mon-yyyy') from dual union all
select 't34', to_date('18-Mar-2005', 'dd-Mon-yyyy') from dual
)
-- End of test data (not part of the solution). SQL query begins BELOW THIS LINE
select * from
(
select name, lag(daytime) over(partition by name order by daytime) daytime
from test_data
)
where daytime is not null