Second maximum value using OVER - max

I'm trying to compare one source of demand against the top two different highest demand sources such that I can do some operations in SQL.
Here is what I have for the maximum and the specific demand, but I can't figure out how to find the second max. I've seen max(x) where x <> max(x), but I am not sure how to work that into windows functions.
select row_id_str, Max_NR, Max_R
from(
select row_id_str,
MAX(NR_close) OVER (PARTITION BY row_id_str) as Max_NR,
MAX(R_close) OVER (PARTITION BY row_id_str) as Max_R
from(
SELECT row_id_str, demand, close,
sum(if(demand = 'root', close, 0)) as R_close,
sum(if(demand != 'root', close, 0)) as NR_close
FROM [scratch] group by 1,2,3
order by row_id_str, close desc)
group by row_id_str, NR_close, R_close)
group by 1,2,3
Solution
select row_id_str, Max_NR, Max_R, Sum(if(RN=2, NR_close, 0)) as Second_Max
from(
select row_id_str, NR_close,
MAX(NR_close) OVER (PARTITION BY row_id_str) as Max_NR,
MAX(R_close) OVER (PARTITION BY row_id_str) as Max_R,
ROW_NUMBER() over (partition by row_id_str order by NR_close desc) as RN
from(
SELECT row_id_str, demand_rtb_name, close, sum(if(demand_rtb_name = 'rubicon', close, 0)) as R_close, sum(if(demand_rtb_name != 'rubicon', close, 0)) as NR_close
FROM [scratch.oRubicon1_3] group by 1,2,3
order by row_id_str, close desc)
group by row_id_str, close, NR_close, R_close)
group by 1,2,3,

You can use MAX in combination with ROW_NUMBER().
Wrap a new select around it, and select a particular row with WHERE RowNumber = 2
SELECT
tMax.row_id_str,
tMax.MyMax,
ROW_NUMBER() OVER (ORDER BY(tMax.MyMax)) AS RowNumber
FROM
(SELECT
t.row_id_str,
MAX(t.NR_close)
FROM
table AS t
GROUP BY
t.row_id_str
) AS tMax

Related

equivalent of distinct On in Oracle

How to translate the following query to Oracle SQL, as Oracle doesn't support distinct on()?
select distinct on (t.transaction_id) t.transaction_id as transactionId ,
t.transaction_status as transactionStatus ,
c.customer_id as customerId ,
c.customer_name as customerName,
You can use ANY_VALUE with group by for this:
https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/any_value.html
Example: https://dbfiddle.uk/WUxvjv5J
with t (a,b,c) as (
select 1,10,1 from dual union all
select 1,10,2 from dual union all
select 1,10,3 from dual union all
select 1,20,4 from dual union all
select 1,20,5 from dual union all
select 1,30,7 from dual
)
select a,b,any_value(c)
from t
group by a,b;
Yes, Oracle has a full set of windowing functions you can use for this. The simplest is ROW_NUMBER:
SELECT *
FROM (SELECT x.col1,
x.col2,
x.col3,
ROW_NUMBER() OVER (PARTITION BY x.col1 ORDER BY x.col2 DESC) seq
FROM table x)
WHERE seq = 1
for each distinct col1, it will number the highest col2 value as seq=1, the next highest as seq=2, etc... so you can filter on 1 to get the desired row. You can used as complex ORDER BY logic as you need to pick the row you want. The key thing is that the ORDER BY goes inside the ROW_NUMBER OVER clause along with the distinct (PARTITION BY) definition, not outside in the main query block.

selecting records from hive table where there are duplicates with a given criteria

Table 1 has duplicate entries in column A with same frequency values. I need to select one random record out of those .If the duplicate entry contain 'unknown' as a column B value ( like in record "d") select one from other rows . I need a select statement which satisfy the above . Thanks .
These conditions can be prioritized using a case expression in order by with a function like row_number.
select A,B,frequency,timekey
from (select t.*
,row_number() over(partition by A order by cast((B = 'unknown') as int), B) as rnum
from tbl t
) t
where rnum = 1
Here for each group of A rows, we prioritize rows other than B = 'unknown' first, and then in the order of B values.
Use row_number analytic function. If you want to select not unknown record first, then use the query below:
select A, B, Frequency, timekey
from
(select
A, B, Frequency, timekey,
row_number() over(partition by A,Frequency order by case when B='unknown' then 1 else 0 end) rn
)s where rn=1
And if you want to select unknown if they exist, use this row_number in the query above:
row_number() over(partition by A,Frequency order by case when B='unknown' then 0 else 1 end) rn

How to write Oracle SQL to reset counter based on the value?

How can I reset the counter like below examples (I need to generate counter in the column named "Counter I need to generate"?
Looks like each value larger than 1 resets the counter, is that right?
If so, you could assign a group number first, based on the number of times a value > 1 occurs before the current row (including). So row 1 to 11 will be group 0, 12 and 13 will be group 1, and so on.
Then you can apply the row_number window function to generate the numbering partitioned by that group:
with VW_GROUPED as (
select
t.*,
(select count(*) from TheTable x where x.URN <= t.URN and x.GAPNOOFDAYS > 1) as GROUPNO
from
TheTable /* <- your table name here */ t)
select
g.URN,
g.CUSTOMER_ID,
g.GAPNOOFDAYS,
row_number() over (partition by GROUPNO order by URN) as "Counter I need to generate"
from
VW_GROUPED g
Here's an alternate example that generates the group in using analytic functions instead of a scalar subquery:
with grp as (
select t.*
, sum(case gapnoofdays when 1 then 0 else 1 end) over (partition by customer_id order by urn) grp
from your_table t
)
select grp.*
, row_number() over (partition by customer_id, grp order by urn) n
from grp;

Is safe to use row_number() over (order by null) as a rownum alternative in Oracle 11g?

I did some testing and it seems that row_number() over (order by null) could be used as an alternative to oracle's rownum pseudocolumn
select rownum, i.* from (
select row_number() over (order by null) as rnum, i.*
from c_invoice i
order by i.dateinvoiced desc
) i;
Is this behavior by design or is just a coincidence?
I would not recommend it. There is no guarantee that the order by null is going to respect the outer order by, although in practice the optimizer might do this.
Instead, you can safely do:
select rownum, i.*
from (select row_number() over (order by i.dateinvoiced desc) as rnum, i.*
from c_invoice i
order by i.dateinvoiced desc
) i;
You can check the query execution plans, but I suspect that the row_number() might add a slight bit of overhead.

EXTRACT(month FROM date) issue

I have an issue with the EXTRACT function on Oracle and I have no idea what is wrong with the script below. For some reason if I use the AS keyword with the EXTRACT it throws Invalid Identifier when referenced in the GROUP BY sentence. It works if I reference the column by the automatic name, given without the AS keyword.
WITH counted AS(
SELECT UNIT,
STATUS,
SDESC,
COUNT(1) AS cnt,
EXTRACT(month FROM STATUS_DATE) AS statusMonth,
ROW_NUMBER() OVER(PARTITION BY UNIT ORDER BY COUNT(1) DESC) AS rk
FROM ATMSTATUS
WHERE
STATUS_DATE BETWEEN '1-OCT-13' AND '31-OCT-13' AND
STATUS > 0
GROUP BY UNIT, STATUS, SDESC, statusMonth
ORDER BY UNIT, cnt DESC
)
SELECT *
FROM counted
WHERE rk < (10 + 1)
You can't use aliases in GROUP BY clause, you have to use the whole expression instead:
WITH counted AS(
SELECT UNIT,
STATUS,
SDESC,
COUNT(1) AS cnt,
EXTRACT(month FROM STATUS_DATE) AS statusMonth,
ROW_NUMBER() OVER(PARTITION BY UNIT ORDER BY COUNT(1) DESC) AS rk
FROM ATMSTATUS
WHERE
STATUS_DATE BETWEEN '1-OCT-13' AND '31-OCT-13' AND
STATUS > 0
GROUP BY UNIT, STATUS, SDESC, EXTRACT(month FROM STATUS_DATE)
ORDER BY UNIT, cnt DESC
)
SELECT *
FROM counted
WHERE rk < (10 + 1)

Resources