db2 select x random rows for a given id - sql

If I have two columns - an ID field and a score field that can take 10 possible values, how can I select 5 random rows per ID? I know I can select 5 random rows from a table by using the following:
select *, rand() as idx
from mytable
order by idx fetch first 5 rows only
but how about 5 rows per ID?

You can do this using row_number():
select t.*
from (select t.*,
row_number() over (partition by idx order by rand()) as seqnum
from mytable t
) t
where seqnum <= 5;

Related

Retrieve specific rows without using rownum

Since I cant use rownum in the query, how can i use rowid to get result from 2nd row until 4th row using rowid or other possible solution apart from rownum.
Here is my current query where it will retrieve 2nd and 4th row:
SELECT * FROM Record a
WHERE
2 = (SELECT COUNT (rowid)
FROM Record b
WHERE a.rowid >= b.rowid)
UNION
SELECT * FROM Record a
WHERE
4 = (SELECT COUNT (rowid)
FROM Record c
WHERE a.rowid >= c.rowid);
Maybe there are other better ways to do it? TQ
If you can't use rownum, then use row_number():
SELECT a.*
FROM (SELECT a.*, ROW_NUMBER() OVER (ORDER BY rowid) as seqnum
FROM Record a
) a
WHERE seqnum BETWEEN 2 and 4;
Note: The ?? is for an ordering column. SQL tables represent unordered sets, so there is no concept of a first row or a second row, except in reference to an ordering column. You can use rowid for this purpose.
In Oracle 12c, you would use OFFSET/FETCH:
SELECT a.*
FROM Record a
OFFSET 1 ROWS
FETCH FIRST 3 ROWS ONLY;
I should point out that you can use rownum. You just can't do:
SELECT a.*
FROM Record a
WHERE rownum BETWEEN 2 and 4;
You can use it in a subquery:
SELECT a.*
FROM (SELECT a.*, rownum as seqnum
FROM Record a
) a
WHERE seqnum BETWEEN 2 and 4;
Do note that without an ORDER BY, there is no guarantee that the results come back in any order, including rowid order.
If you want to avoid rownum and row_number, use sum:
select *
from (
select sum(1) over ( order by rowid /* or whatever you need */ ) as rn,
r.*
from record
)
where rn between 2 and 4
The trick is only in the fact that here sum(1) gives the same thing than count(1) or count(rowid) or whatever count on a not null value, and this is the same thing than counting the rows with row_number or rownum.
In this way you use the sum to compute a row_number, without explicitly writing 'row_number' or 'rownum'.
SQL> create table testTab(x) as ( select level from dual connect by level <= 6);
Table created.
SQL> select t.*,
2 count(1) over (order by rowid desc) as count,
3 sum(1) over (order by rowid desc) as sum,
4 row_number() over (order by rowid desc) as rowNumber
5 from testTab t;
X COUNT SUM ROWNUMBER
---------- ---------- ---------- ----------
6 1 1 1
5 2 2 2
4 3 3 3
3 4 4 4
2 5 5 5
1 6 6 6
The external query simply applies the filter.
With Oracle 12c, you can now easily do row limiting. In your scenario you can do something like this:
SELECT *
FROM RECORD
OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY
UNION
SELECT *
FROM RECORD
OFFSET 3 ROWS FETCH NEXT 1 ROWS ONLY

Select next 20 rows after top 10

I'm trying to select next 20 rows after top 10 rows.
select TOP 20 *
from memberform
where Row_Number over(10)
You need to use something like a CTE (Common Table Expression) and a ROW_NUMBER to define row numberings for your data set - then select from that numbered CTE for the set of rows you want:
;WITH PagingCte AS
(
SELECT
(list of columns),
RowNum = ROW_NUMBER() OVER (ORDER BY -some-column-of-yours-)
FROM
dbo.memberform
)
SELECT
(list of columns)
FROM
PagingCte
WHERE
RowNum BETWEEN 10 AND 29
In the inner ROW_NUMBER() window function, you need to define an ordering which defines how the rows are numbered (order by date, or by ID, or whatever makes sense for you).
Without an explicit ordering, there is no next 20 after the first 10 to be had..
do you mean offset clause ?
OFFSET excludes the first set of records.
OFFSET can only be used with an ORDER BY clause.
OFFSET with FETCH NEXT returns a defined window of records.
OFFSET with FETCH NEXT is great for building pagination support.
The general syntax to exclude first n records is:
SELECT column-names
FROM table-name
ORDER BY column-names
OFFSET n ROWS
Please refer to http://www.dofactory.com/sql/order-by-offset-fetch
WITH T AS
(
SELECT TOP 30 name,
row_number() OVER (ORDER BY id) AS RN
FROM memberform
ORDER BY id
)
SELECT
MAX(CASE WHEN RN <=10 THEN name END) AS Col1,
MAX(CASE WHEN RN > 10 THEN name END) AS Col2
FROM T
GROUP BY RN % 10

Get top N records grouped by another field

I have an Oracle table with ID, SUBJECT, and PAYLOAD (CLOB). I'd like to get a listing of the TOP 10 records who have the biggest PAYLOAD (LENGTH(PAYLOAD)) grouped by subject. So if I have 10 DISTINCT SUBJECT's in the table, the query should return 100 rows (top 10 per subject).
Use row_number():
select t.*
from (select t.*, row_number() over (partition by subject order by length(payload) desc) as seqnum
from table t
) t
where seqnum <= 10;

oracle sql wih rownum <=

why below query is not giving results if I remove the < sign from query.Because even without < it must match with results?
Query used to get second max id value:
select min(id)
from(
select distinct id
from student
order by id desc
)
where rownum <=2
student id
1
2
3
4
Rownum has a special meaning in Oracle. It is increased with every row, but the optimizer knows that is increasing continuously and all consecutive rows must met the rownum condition. So if you specify rownum = 2 it will never occur since the first row is already rejected.
You can see this very nice if you do an explain plan on your query. It will show something like:
Plan for rownum <=:
COUNT STOPKEY
Plan for rownum =:
FILTER
A ROWNUM value is not assigned permanently to a row (this is a common misconception). A row in a table does not have a number; you cannot ask for row 2 or 3 from a table
click Here for more Info.
This is from the link provided:
Also confusing to many people is when a ROWNUM value is actually assigned. A ROWNUM value is assigned to a row after it passes the predicate phase of the query but before the query does any sorting or aggregation. Also, a ROWNUM value is incremented only after it is assigned, which is why the following query will never return a row:
select *
from t
where ROWNUM > 1;
Because ROWNUM > 1 is not true for the first row, ROWNUM does not advance to 2. Hence, no ROWNUM value ever gets to be greater than 1. Consider a query with this structure:
select ..., ROWNUM
from t
where <where clause>
group by <columns>
having <having clause>
order by <columns>;
I think this is the query you are looking for:
select id
from (select distinct id
from student
order by id desc
) t
where rownum <= 2;
Oracle processes the rownum before the order by, so you need a subquery to get the first two rows. The min() was forcing an aggregation that returned only one result, but before the rownum was applied.
If you actually want only the second value, you need an additional layer of subqueries:
select min(id)
from (select id
from (select distinct id
from student
order by id desc
) t
where rownum <= 2
) t;
However, I would do:
select id
from (select id, dense_rank() over (order by id) as seqnum
from student
) t
where seqnum = 2;
Order asc instead of desc
select id from student where rownum <=2 order by id asc;
Why not just use
select id
from ( select distinct id
, row_number() over (order by id desc) x
from student
)
where x = 2
Or even really bad. Getting the count and index :)
select id
from ( select id
, row_number() over (order by id desc) idx
, sum(1) over (order by null) cnt
from student
group
by id
)
where idx = cnt - 1 -- get the pre-last
Or
where idx = cnt - 2 -- get the 2nd-last
Or
where idx = 3 -- get the 3rd
Try this
SELECT *
FROM (
SELECT id, row_number() over (order by id asc) row_num
FROM student
) AS T
WHERE row_num = 2 -- or 3 ... n
ROW_NUMBER

Apply aggregate function on subset of rows

I have a table that I want to calculate the average of one column but only for the last 10 rows.
SELECT AVG(columnName) as avg FROM tableName
I cannot apply top directly since this query only returns one row. I need a way to get the latest 10 rows and do the average on them.
Try this:
SELECT AVG(columnName) FROM
(SELECT TOP 10 columnName FROM tableName ORDER BY ColumnWhichHoldsOrder DESC) A
select avg(columnName)
from (
select columnName,
row_number() over (order by some column desc) as rn
from tableName
) t
where rn <= 10;