Can I use a CTE data inside another CTE by joining both of them (Oracle SQL) - sql

Requirement
I want to get the first four hundred GROUP_ID's from a table(greater than input GROUP_ID), and in the same table against each GROUP_ID, there are two LOG_ID's out of which I want the lowest one. Once I get the lowest LOG_ID, I will use that LOG_ID to get the data from another table where it is a foreign key.
APPROACH I USED
First I have formed a subset of top 400 GROUP_ID's which are greater than input GROUP_ID's
Then I used all the GROUP_IDs in my second subset where I will get the lowest LOG_ID against each GROUP_ID.
And finally, when I have the lowest LOG_ID, I used it to get the details from another table.
QUERY USED
WITH INIT AS (
SELECT GROUP_ID
FROM PV_ADAPTER_LOG
WHERE GROUP_ID > 2004141441192825
AND ADAPTER_ID IN ('2568','2602')
ORDER BY GROUP_ID
FETCH FIRST 400 ROWS ONLY
)
,INIT2 AS (
SELECT MIN(L.LOG_ID) AS LOG_ID
FROM PV_ADAPTER_LOG L
JOIN INIT ON INIT.GROUP_ID =L.GROUP_ID
GROUP BY L.GROUP_ID
)
SELECT A.LOG_ID,A.OPER_SEQ AS CALL_SEQUENCE,A.GROUP_ID ,B.INTERFACE_ID,A.INSTRUCTION_NAME, B.ADAPTER_DETAIL AS XML_CONTENT,B.SEQ AS XML_SEQUENCE
FROM INIT2
JOIN PV_ADAPTER_LOG A ON A.LOG_ID=INIT2.LOG_ID
JOIN PV_ADAPTER_LOG_DETAIL B ON B.LOG_ID=A.LOG_ID
Is my approach right or is there any other way to achieve this.

I think this is what you're looking for:
Use row_number ordered by group to find the first 400 rows
Use row_number partitioned by group and ordered by log to find the first log per group
Which is:
WITH INIT AS (
SELECT P.*,
ROW_NUMBER () OVER (
ORDER BY GROUP_ID
) RN,
ROW_NUMBER () OVER (
PARTITION BY GROUP_ID
ORDER BY LOG_ID
) MN
FROM PV_ADAPTER_LOG p
WHERE GROUP_ID > 2004141441192825
AND ADAPTER_ID IN ('2568','2602')
)
SELECT * FROM INIT
WHERE RN <= 400
AND MN = 1

You can use the analytical function to get the first 400 groups and then record with min log_id per group in a single query as follows:
SELECT GROUP_ID, LOG_ID FROM
(SELECT P.GROUP_ID, P.LOG_ID,
ROW_NUMBER() OVER (ORDER BY GROUP_ID) AS RNGRP,
ROW_NUMBER() OVER (PARTITION BY GROUP_ID ORDER BY LOG_ID) AS RNLOG
FROM PV_ADAPTER_LOG
WHERE GROUP_ID > 2004141441192825
AND ADAPTER_ID IN ('2568','2602'))
WHERE RNGRP <= 400 AND RNLOG = 1;
You can then use it wherever you want to use it. (In CTE or In Inner view)

Related

How to compare max value in each group to current row? - SQL

I want to apply conditional logic based on whether or not my item_no is the highest in its group.
Sample Data:
group_id item_no
oiegown 1
oiegown 2
oiegown 3
wefwefw 1
wefwefw 2
My Approach:
CASE WHEN (
SELECT MAX(item_no)
FROM my_table
GROUP BY group_id
) = item_no
THEN /*logic here*/
My subquery, as desired, retrieves the highest item_no per group.
However, the query does not work as I get the Scalar subquery produced more than one element error. How can I work around this?
Your approach corrected (correlate the subquery to get the maximum for the group ID of the current row only):
CASE WHEN (
SELECT MAX(item_no)
FROM my_table m
WHERE m.group_id = my_table.group_id
) = item_no
THEN /*logic here*/
The alternative with a window function:
CASE WHEN MAX(item_no) OVER (PARTITION BY group_id) = item_no
THEN /*logic here*/

How to select duplicates by first order of appearance

I am looking to select unique values from a SQL database but I want to make sure that I am selecting only the first duplicate in order of appearance (in my case - date in the hospital, intime col)
You can see the code below.
I am trying to take only the IDs of the first time the patients were hospitalized which correspond to the "intime" col.
I have no absolute way to check that by ordering as I did and by using groupby, SQL will in fact return the id in the same order.
Thank you very much.
WITH ccupatients AS
(SELECT HADM_ID
FROM `physionet-data.mimiciii_clinical.icustays` i
WHERE first_careunit = 'CCU'
ORDER BY intime)
SELECT hadm_id
FROM ccupatients
GROUP BY hadm_id
Use ROW_NUMBER() if your RDBMS supports it: this works by ranking records by increasing intime within groups of records having the same ham_id, and then filtering in the outer query on the top record per group:
SELECT hadm_id
FROM (
SELECT hadm_id, ROW_NUMBER() OVER(PARTITION BY hadm_id ORDER BY intime) rn
FROM `physionet-data.mimiciii_clinical`.icustays
WHERE first_careunit = 'CCU'
) x
WHERE rn = 1
If you RDBMS does not support window functions such as ROW_NUMBER(), another option is to use a NOT EXISTS condition with a correlated subquery:
SELECT hadm_id
FROM `physionet-data.mimiciii_clinical`.icustays i
WHERE
first_careunit = 'CCU'
AND NOT EXISTS (
SELECT 1
FROM `physionet-data.mimiciii_clinical`.icustays i1
WHERE
i1.first_careunit = 'CCU'
AND i1.hadm_id = i.hadm_id
AND i1.intime < i.intime
)

Test whether MIN would work over ROW_NUMBER

Situation:
I have three columns:
id
date
tx_id
The primary id column is tx_id and is unique in the table. Each tx_id is tied to an id and it has a record date. I would like to test whether or not the tx_id is incremental.
Objective:
I need to extract the first tx_id by id but I want to prevent using ROW_NUMBER
i.e
select id, date, tx_id, row_number() over(partition by id order by date asc) as First_transaction_id from table
and simply use
select id, date, MIN(tx_id) as First_transaction_id from table
So how can i make sure since i have more than 50 millions of ids that by using MINtx_id will yield the earliest transaction for each id?
How can i add a flag column to segment those that don't satisfy the condition?
how can i make sure since i have more than 50 millions of ids that by using MINtx_id will yield the earliest transaction for each id?
Simply do the comparison:
You can get the exceptions with logic like this:
select t.*
from (select t.*,
min(tx_id) over (partition by id) as min_tx_id,
rank() over (partition by id order by date) as seqnum
from t
) t
where tx_id = min_tx_id and seqnum > 1;
Note: this uses rank(). It seems possible that there could be two transactions for an id on the same date.
use corelated sunquery
select t.* from table_name t
where t.date= ( select min(date) from table_name
t1 where t1.id=t.id)

Using MAX to compute MAX value in a subquery column

What I am trying to do: I have a table, "band_style" with schema (band_id, style).
One band_id may occur multiple times, listed with different styles.
I want ALL rows of band_id, NUM (where NUM is the number of different styles a band has) for the band ids with the SECOND MOST number of styles.
I have spent hours on this query- almost nothing seems to be working.
This is how far I got. The table (data) successfully computes all bands with styles less than the maximum value of band styles. Now, I need ALL rows that have the Max NUM for the resulting table. This will give me bands with the second most number of styles.
However, this final result seems to be ignoring the MAX function and just returning the table (data) as is. Can someone please provide some insight/working method? I have over 20 attempts of this query with this being the closest.
Using SQL*PLUS on Oracle
WITH data AS (
SELECT band_id, COUNT(*) AS NUM FROM band_style GROUP BY band_id HAVING COUNT(*) <
(SELECT MAX(c) FROM
(SELECT COUNT(band_id) AS c
FROM band_style
GROUP BY band_id)))
SELECT data.band_id, data.NUM FROM data
INNER JOIN ( SELECT band_id m, MAX(NUM) n
FROM data GROUP BY band_id
) t
ON t.m = data.band_id
AND t.n = data.NUM;
Something like this... based on a Comment under your post, you are looking for DENSE_RANK()
select band_id
from ( select band_id, dense_rank() over (order by count(style) desc) as drk
from band_style
group by band_id
)
where drk = 2;
I would use a windowing function (RANK() in this case) - which is great for find the 'n' ranked thing in a set.
SELECT DISTINCT bs.band_id
FROM band_style bs
WHERE EXISTS (
SELECT NULL
FROM (
SELECT
bs2.band_id,
bs2.num,
RANK() OVER (ORDER BY bs2.num) AS numrank
FROM (
SELECT bs1.band_id, COUNT(*) as num
FROM band_style bs1
GROUP BY bs1.band_id ) bs2 ) bs3
WHERE bs.band_id = bs3.band_id
AND bs3.numrank = 2 )

SQL ranking over two tables

I have two tables with user rankings.
Table rankingA and rankingB.
Each table has the columns:
user_id
points
group_id
Higher the points so higher the rank of the user/group...
Now i try to get the group ranking for the question which rank has my group.
So far i have this SQL:
select sum(ra.points) as rapoints, sum(rb.points) as rbpoints from public.rankinga ra
LEFT JOIN public.rankingb rb ON ra.group_id=rb.group_id and ra.user_id=rb.user_id where
ra.group_id=200;
It returns the points from rankinga and rankinb for the group 200.
How can i get the rankings of the group? I tryd it with:
row_number() OVER (ORDER BY sum(rb.points) DESC) AS rankb
but got a wrong result.
My expected result for group_id 200 is:
rapoints,rbpoints,rarank, rbrank
420, 10, 3, same points as group_id 300 so rbrank 2 or 3
How can i get this?
Setup
CREATE TABLE rankinga
(
user_id bigint,
group_id bigint,
points integer
)
CREATE TABLE rankingb
(
user_id bigint,
group_id bigint,
points integer
)
insert into public.rankinga (user_id,group_id,points) values (1,100,120),(2,100,300), (3,100,20),(4,200,300),(5,200,120),(6,300,600);
insert into public.rankingb (user_id,group_id,points) values (1,100,5),(2,100,3),(3,100,10),(4,200,2),(5,200,8),(6,300,10);
I think you want to do this with union all, aggregation, and the window function. Joining the tables is likely to miss rows (if users are in one table but not the other) or over count (if you join on group). So this may do what you want:
select group_id, sum(rapoints) as rapoints, sum(rbpoints) as rbpoints,
sum(rapoints) + sum(rbpoints) as points,
dense_rank() over (order by sum(rapoints) + sum(rbpoints) desc) as ranking
from ((select ra.group_id, sum(ra.points) as rapoints, 0 as rbpoints
from public.rankinga ra
group by ra.group_id
) union all
(select rb.group_id, 0, sum(rb.points) as rbpoints
from public.rankingb rb
group by rb.group_id
)
) ab
group by group_id;
If you want to select just one group, then put this in a subquery (or CTE) and then select the group.
Here is a SQL Fiddle.
EDIT:
If you want just the result for one group, you still need to calculate the values for all groups. So:
select ab.*
from (<above query here>) ab
where group_id = 200;