Oracle: Need to fetch the rows exponentially - sql

I got below query from another post which selects 100 rows from every 2000 rows.
Like this: 1-100,2001-2100,4001-4100,6001-6100,8001-8100 and so on.
SELECT * FROM (SELECT t.*,ROWNUM AS rn FROM(SELECT * FROM your_table ORDER BY your_condition) t)WHERE MOD( rn - 1, 2000 ) < 100;
Now I want to select my data exponentially.Such that it will select 100 rows from first 1000 rows, then from next 2000 rows, then from next 4000 rows.
Like this: 1-100,2000-2100,4000-4100,8000-8100,16000-16100 and so on.
The idea is to scan rows with a specific pattern.

You asked this in a comment on your previous question and I answered there...
SELECT *
FROM (
SELECT t.*,
ROWNUM AS rn -- Secondly, assign a row number to the ordered rows
FROM (
SELECT *
FROM your_table
ORDER BY your_condition -- First, order the data
) t
)
WHERE rn - POWER( -- Finally, filter the top 100.
2,
TRUNC( CAST( LOG( 2, CEIL( rn / 1000 ) ) AS NUMBER(20,4) ) )
) * 1000 + 1000 <= 100
This will take the first 100 rows from the groups 1-1000, 1001-3000, 3001-7000, 7001-15000, etc.
Or, to get the rows:
1-100,2000-2100,4000-4100,8000-8100,16000-16100, 32000-32100 and so on.
Then:
WHERE CASE -- Finally, filter the top 100.
WHEN rn <= 2000 THEN rn
ELSE rn - POWER(
2,
TRUNC( CAST( LOG( 2, CEIL( rn / 1000 - 1 ) ) AS NUMBER(20,4) ) )
) * 1000
END <= 100

You could use power function and simple hierarchical query, then join it with your table. Here is example with all_objects view:
with rng as (select 0 num from dual union all
select 1000 * power(2, level) from dual connect by level < 10 )
select *
from (select row_number() over (order by object_name) rn, object_name from all_objects)
join rng on rn between num + 1 and num + 100

From what you describe, you can use logs to define the groups. This is probably close enough to what you want:
select t.*
from (select t.*,
row_number() over (floor(log(2, floor(1 + (seqnum - 1) / 1000) ))
order by col
) as seqnum_2
from (select t.*, row_number() over (order by col) as seqnum
from t
) t
where seqnum_2 <= 100;
The difference from your description is that the first group is 1-999, 1000-1999, and so on.

Related

Randomly flagging records in an Oracle Table

Given a table of IDs in an Oracle database, what is the best method to randomly flag (x) percent of them? In the example below, I am randomly flagging 20% of all records.
ID ELIG
1 0
2 0
3 0
4 1
5 0
My current approach shown below works fine, but I am wondering if there is a more efficient way to do this?
WITH DAT
AS ( SELECT LEVEL AS "ID"
FROM DUAL
CONNECT BY LEVEL <= 5),
TICKETS
AS (SELECT "ID", 1 AS "ELIG"
FROM ( SELECT *
FROM DAT
ORDER BY DBMS_RANDOM.VALUE ())
WHERE ROWNUM <= (SELECT ROUND (COUNT (*) / 5, 0) FROM DAT)),
RAFFLE
AS (SELECT "ID", 0 AS "ELIG"
FROM DAT
WHERE "ID" NOT IN (SELECT "ID"
FROM TICKETS)
UNION
SELECT * FROM TICKETS)
SELECT *
FROM RAFFLE;
You could use a ROW_NUMBER approach here:
WITH cte AS (
SELECT t.*, ROW_NUMBER() OVER (ORDER BY dbms_random.value) rn,
COUNT(*) OVER () cnt
FROM yourTable t
)
SELECT t.*,
CASE WHEN rn / cnt <= 0.2 THEN 'FLAG' END AS flag -- 0.2 to flag 20%
FROM cte t
ORDER BY ID;
Demo
Sample output for one run of the above query:
Note that one of five records is flagged, which is 20%.

Returning the results of multiple 'WITH CTE' queries as one result

I am trying to select the highest price from the same product over n periods of time i.e. last 5, 50, 100, 500.
At the moment I'm running the query four times for above periods like this:
;WITH CTE AS
(
SELECT TOP (500) * FROM Ticker WHERE ProductId='BTC-USD'
ORDER BY ID DESC
) SELECT TOP (1) * FROM CTE
ORDER BY PRICE desc
Is there a way I can get all the results at once in 4 rows?
Hmmmm . . . My first thought is a union all:
with cte as (
select top (500) t.*, row_number() over (order by id desc) as seqnum
from Ticker t
where ProductId = 'BTC-USD'
order by id desc
)
select 500 as which, max(cte.price) as max_price from cte where seqnum <= 500 union all
select 100 as which, max(cte.price) from cte where seqnum <= 100 union all
select 50 as which, max(cte.price) from cte where seqnum <= 50 union all
select 5 as which, max(cte.price) from cte where seqnum <= 5;
But, I have another idea:
with cte as (
select top (500) t.*, row_number() over (order by id desc) as seqnum
from Ticker t
where ProductId = 'BTC-USD'
order by id desc
)
select v.which, x.max_price
from (values (5), (50), (100), (500)) v(which) cross apply
(select max(price) as max_price from cte where seqnum <= which) x;
Of course, the "500" in the CTE needs to match the maximum value in v. You can actually get rid of the TOP in the CTE.

Oracle: I need to select n rows from every k rows of a table

For example:
My table has 10000 rows. First I will divide it in 5 sets of 2000(k) rows. Then from each set of 2000 rows I will select only top 100(n) rows.
With this approach I am trying to scan some rows of table with a specific pattern.
Assuming you are ordering them 1 - 10000 using some logic and want to output only rows 1-100,2001-2100,4001-4100,etc then you can use the ROWNUM pseudocolumn:
SELECT *
FROM (
SELECT t.*,
ROWNUM AS rn -- Secondly, assign a row number to the ordered rows
FROM (
SELECT *
FROM your_table
ORDER BY your_condition -- First, order the data
) t
)
WHERE MOD( rn - 1, 2000 ) < 100; -- Finally, filter the top 100 per 2000.
Or you could use the ROW_NUMBER() analytic function:
SELECT *
FROM (
SELECT t.*,
ROW_NUMBER() OVER ( ORDER BY your_condition ) AS rn
FROM your_table
)
WHERE MOD( rn - 1, 2000 ) < 100;
Is it possible to increase the set of sample data exponentially. Like 1k, 2k, 4k,8k....and then fetch some rows from these.
Replace the WHERE clause with:
WHERE rn - POWER(
2,
TRUNC( CAST( LOG( 2, CEIL( rn / 1000 ) ) AS NUMBER(20,4) ) )
) * 1000 + 1000 <= 100
This solution uses the analytic ntile() to split the raw data into five buckets. That result set is labelled using the analytic row_number() which provides a filter to produce the final set:
with sq1 as ( select id, col1, ntile(5) over (order by id asc) as quintile
from t23
)
, sq2 as ( select id, col1, quintile
, row_number() over ( partition by quintile order by id asc) as rn
from sq1 )
select *
from sq2
where rn <= 200
order by quintile, rn
/
use partition by and order by with row_number. it will look like following:
row_number()over(partition by partition_column order by order_column)<=100
partition_column will be your condition to divide set.
order_column will be your condition to select top 100.

How to get the middle most record(s) from a group of data in sql

create table #middle
(
A INT,
B INT,
C INT
)
INSERT INTO #middle (A,B,C) VALUES (7,6,2),(1,0,8),(9,12,16),(7, 16, 2),(1,12,8), (9,12,16),(9,12,16),(7, 16, 2),(1,12,8), (9,12,16)
;WITH MIDS
AS (SELECT *,
Row_number()
OVER (
ORDER BY a, b, c DESC )AS rn
FROM #middle)
SELECT *
FROM MIDS
WHERE rn <= (SELECT CASE ( Count(*)%2 )
WHEN 0 THEN ( Count(*) / 2 ) + 1
ELSE ( Count(*) / 2 )
END
FROM MIDS) except (SELECT *
FROM MIDS
WHERE rn < (SELECT ( Count(*) / 2 )
FROM MIDS))
The query i have tried works >4 records but not for '3'.Now my question is how should i modify my query so that for 3 records i should get the 2nd record which is the middle most record among them,try to insert only 3 records from above records and help. Thanks in advance.
You can use OFFSET and FETCH
select *
from #middle
order by a, b, c desc
offset (select count(*) / 2 - (case when count(*) % 2 = 0 then 1 else 0 end) from #middle) rows
fetch next (select 2 - (count(*) % 2) from #middle) rows only
There are many ways to get the median in SQL. Here is a simple way:
select h.*
from (select h.*, row_number() over (order by a, b, c desc) as seqnum,
count(*) over () as cnt
from #highest h
) h
where 2 * rn in (cnt, cnt - 1, cnt + 1);
For an even number of records, you will get two rows. You need to decide what you actually want in this case.
How about this:
**EDITED
;WITH MIDS
AS (SELECT *,
Row_number()
OVER (
ORDER BY a, b, c DESC )AS rn
FROM #middle),
Cnt
AS
(SELECT COUNT(*) c, COUNT(*)%2 as rem, COUNT(*)/2 as mid FROM Mids)
SELECT *
FROM MIDS
CROSS APPLY cnt
where (rn >= cnt.mid and rn <= cnt.mid + 1 AND cnt.rem = 0) OR
(cnt.rem <> 0 AND rn = cnt.mid+1)

Get 1000 row set from Oracle table [duplicate]

This question already has an answer here:
SQL (ORACLE): ORDER BY and LIMIT [duplicate]
(1 answer)
Closed 7 years ago.
I want to select 1000 rows at a time using the query:
SELECT * FROM MEMBERID_1M WHERE ROWNUM <1000
How do I get the next set of 1000 rows from this table in a for loop?
I suggest you to use ROW_NUMBER() function like this: (my id is your PK)
SELECT M.*
FROM (
SELECT MEMBERID_1M.*, ROW_NUMBER() OVER (ORDER BY id) As rn
FROM MEMBERID_1M ) M
WHERE
(rn <= 1000)
And for next:
SELECT M.*
FROM (
SELECT MEMBERID_1M.*, ROW_NUMBER() OVER (ORDER BY id) As rn
FROM MEMBERID_1M ) M
WHERE
(rn > 1000) AND (rn <= 2000)
For page :i:
SELECT M.*
FROM (
SELECT MEMBERID_1M.*, ROW_NUMBER() OVER (ORDER BY id) As rn
FROM MEMBERID_1M ) M
WHERE
(rn > :i * 1000) AND (rn <= (:i + 1) * 1000)
Reproducing the answer
There is only a rather convoluted way to do this, which is a real pain with Oracle. They should just implement a LIMIT/OFFSET clause...
The rownum gets assigned after the row has been selected by the where clause, so that a rownum must always start with 1. where rownum > x will always evaluate to false.
Also, rownum gets assigned before sorting is done, so the rownum will not be in the same order as your order by says.
You can get around both problems with a subselect:
select a,b,c, rn from
( select a,b,c, rownum rn from
( select a,b,c from the_table where x = ? order by c)
where rownum < Y)
where rn > X
If you do not need to sort (but only then), you can simplify to
select a,b,c, rn from
( select a,b,c, rownum rn from the_table where rownum < Y )
where rn > X
You should be paging on server side .use this query
and more detail refer this link
http://www.oracle.com/technetwork/issue-archive/2007/07-jan/o17asktom-093877.html
select *
from
( select rownum rnum, a.*
from (SELECT * FROM MEMBERID_1M ) a
where rownum <= :M )
where rnum >= :N;