How to return the rows between 20th and 30th in Oracle Sql [duplicate] - sql

This question already has answers here:
Oracle SQL: Filtering by ROWNUM not returning results when it should
(2 answers)
Closed 4 years ago.
so I have a large table that I'd like to output, however, I only want to see the rows between 20 and 30.
I tried
select col1, col2
from table
where rownum<= 30 and rownum>= 20;
but sql gave an error
I also tried --where rownum between 20 and 30
it also did not work.
so whats the best way to do this?

SELECT *
FROM T
ORDER BY I
OFFSET 20 ROWS --skips 20 rows
FETCH NEXT 10 ROWS ONLY --takes 10 rows
This shows only rows 21 to 30. Take care here that you need to sort the data, otherwise you may get different results every time.
See also here in the documentation.
Addendum: As in the possible duplicate link shown, your problem here is that there can't be a row with number 20 if there is no row with number 19. That's why the rownum-approach works to take only the first x records, but when you need to skip records you need a workaround by selecting the rownum in a subquery or using offset ... fetch
Example for a approach with using rownum (for lower oracle versions or whatever):
with testtab as (
select 'a' as "COL1" from dual
union all select 'b' from dual
union all select 'c' from dual
union all select 'd' from dual
union all select 'e' from dual
)
select * from
(select rownum as "ROWNR", testtab.* from testtab) tabWithRownum
where tabWithRownum.ROWNR > 2 and tabWithRownum.ROWNR < 4;
--returns only rownr 3, col1 'c'

Whenever you use rownum, it counts the rows that your query returns. SO if you are trying to filter by selecting all records between rownum 20 and 30, that is only 10 rows, so 20 and 30 dont exist. You can however, use WITH (whatever you want to name it) as and then wrap your query and rename your rownum column. This way you are selecting from your select. Example.
with T as (
select requestor, request_id, program, rownum as "ROW_NUM"
from fnd_conc_req_summary_v where recalc_parameters='N')
select * from T where row_num between 20 and 30;

Related

ORACLE SQL: select MAX value based on 2 different variables (group by function) [duplicate]

This question already has answers here:
Fetch the rows which have the Max value for a column for each distinct value of another column
(35 answers)
Select First Row of Every Group in sql [duplicate]
(2 answers)
Return row with the max value of one column per group [duplicate]
(3 answers)
SQL: getting the max value of one column and the corresponding other columns [duplicate]
(2 answers)
Closed 11 months ago.
I think my problem is quite simple but I don´t really get it :)
I'm using SQL Developer as IDE and have a large table which looks like this:
ID
technology
speed
1
3G
20
1
2G
10
1
4G
40
1
5G
100
2
3G
60
2
4G
90
2
5G
150
3
3G
30
3
4G
50
I need the max value of 'technology' for each 'ID' and also need the 'speed' in the result:
ID
technology
speed
1
5G
100
2
5G
150
3
4G
50
my SQL looks like that:
SELECT ID, MAX(technology) AS technology, speed
FROM "table"
GROUP BY ID, speed;
but with this SQL I get multiple selections for each ID
any ideas?
Since you're including speed in your select and group, and those values vary per row, your current query will basically return the full table just with the MAX(technology) for each row. This is because speed can't be grouped into a single value as they are all different.
ie.
ID technology speed
1 5G 20
1 5G 10
1 5G 40
1 5G 100
Based purely on your sample set, you could select the MAX(speed) since it always coincides with the MAX(technology), and then you would get the right results:
ID technology speed
1 5G 100
However, if the MAX(technology) ever has less than the MAX(speed), the above would become incorrect.
A better approach would be to use a window function because you would remove that potential flaw:
with cte as (
SELECT ID, technology, speed,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY technology DESC) RN
FROM table)
SELECT *
FROM cte
WHERE RN = 1
This assigns a number to each row, starting with number 1 for the row that has the MAX(technology) (ie. ORDER BY technology DESC), and does this for each ID (ie. PARTITION BY ID).
Therefore when we select only the rows that are assigned row number 1, we are getting the full row for each max technology / id combination.
One last note - if there are duplicate rows with the same ID and technology but with various speeds, this would pick one of them at random. You would need to further include an ORDER for speed in that case. Based on your sample set this doesn't happen, but just an fyi.
You can also use the keep keyword. It is handy in situations like this one since ordering by one column and outputting another column happens in one clause, no subquery or CTE is needed. The drawback is that it is Oracle proprietary syntax.
with t (ID, technology, speed) as (
select 1, '3G', 20 from dual union all
select 1, '2G', 10 from dual union all
select 1, '4G', 40 from dual union all
select 1, '5G', 100 from dual union all
select 2, '3G', 60 from dual union all
select 2, '4G', 90 from dual union all
select 2, '5G', 150 from dual union all
select 3, '3G', 30 from dual union all
select 3, '4G', 50 from dual
)
select id
, max(technology) keep (dense_rank first order by speed desc)
, max(speed)
from t
group by id
Db fiddle.

Oracle SQL - SUM aggregate not working in LEVEL

I have this query where it has an output when using count but no output when using sum
select to_char(counter)
from (select level counter from dual connect by level <= 1000000)
where counter =
(select sum(a.amount) from table a)
I'm wondering it's because the query only supports outputs of whole numbers? I am expecting to have outputs with decimals.
I am using this as a table value set in Oracle HCM, if anyone's familiar with that. It's why I don't have the aggregate in the SELECT statement as it doesn't support it.
Are you sure your subquery is returning rows? Because SUM of an empty set is null not zero, eg
SQL> select to_char(counter)
2 from ( select level counter from dual connect by level <= 1000000 )
3 where counter =
4 (select sum(deptno) from scott.emp);
TO_CHAR(COUNTER)
----------------------------------------
310
SQL> select to_char(counter)
2 from ( select level counter from dual connect by level <= 1000000 )
3 where counter =
4 (select sum(deptno) from scott.emp where 1=0);
no rows selected
Your query says: Create all integers from 1 to 1,000,000 and of these show me the one that matches exactly the total amount of the table. So if that total is 123.45, you will get no row. If that amount is -123, you will get no row. If that amount is 1,000,001 you will get no row.
If you simply want the total, but you can have aggregations only in subqueries for some weird reason, just do
select * from (select sum(amount) as total from table) t;

Execute both the query and the COUNT of the query

I'm trying to build a query or a PL/SQL code to both execute a query, as well as return the number of results of this query.
Is this possible in a single query? Right now I feel like I'm being very wasteful: I first wrap the query in a COUNT (without ORDER BY) and then run the same query again (without the COUNT). A difference of a few seconds will probably not change the total number of rows, I can live with that.
The DB I'm using is Oracle Enterprise 12.2
An easy SQL way:
a test table:
create table testTable(a, b, c) as (
select 1, 'one', 'XX' from dual UNION ALL
select 2, 'two', 'YY' from dual UNION ALL
select 3, 'three', 'ZZ' from dual
)
A simple query:
select a, b, c
from testTable
A B C
---------- ----- --
1 one XX
2 two YY
3 three ZZ
3 rows selected.
The query with the number of records :
select a, b, c, count(*) over (partition by 1) as count
from testTable
A B C COUNT
---------- ----- -- ----------
1 one XX 3
2 two YY 3
3 three ZZ 3
3 rows selected.
You can try to do something like:
WITH test_query
AS (SELECT LEVEL just_a_number
FROM dual
CONNECT BY level < 101)
SELECT just_a_number
, COUNT(just_a_number) OVER (ORDER BY 1) total_count
FROM test_query;
Where COUNT(just_a_number) OVER (ORDER BY 1) will return total number of rows fetched in each row. Note that it may slow down the query.
Typically, when I do something like this, I create a stored procedure that returns 2 values. The first would be the result set as a REF CURSOR, and the other a number(12,0) returning the count. Both would of course require separate queries, but since it is in a single stored procedure, it is only one database connection and command being executed.
You are of course right for having your COUNT query forgo the ORDER BY clause.
To answer your question, you are not being wasteful per se. This is common practice in enterprise software.

UNION between select with 0 rows and select with 20745 gives 20740

In SQL Server 2008 I have a behaviour I don't understand.
I'm doing a UNION between two select statements.
First select returns 20745 rows
Second select returns 0 rows
When I using union bewteen the two selects, I get 20740 rows, I would exspect 20745 as union only returns distinct values.
To get the excepted result I used union all but there is something I don't understand about it. Does anyone have an explanation?
There must be duplicate rows in your first SELECT statement. Note that UNION eliminates duplicates from your result set.
If you want to return all rows, use UNION ALL instead.
Example:
--UNION ALL
WITH TableA(n) AS (
SELECT * FROM (
VALUES(1),(2),(3),(4),(1)
)t(n)
),
TableB(n) AS (
SELECT * FROM (
VALUES(10),(20),(30),(40)
)t(n)
)
SELECT n FROM TableA UNION ALL
SELECT n FROM TableB
The above will return:
n
-----------
1
2
3
4
1
10
20
30
40
While the UNION variant
SELECT n FROM TableA UNION
SELECT n FROM TableB
will return:
n
-----------
1
2
3
4
10
20
30
40
union removes duplicate results, regardless of whether they come from two different selects or from the same one. If you want to preserve duplicated, use union all instead:
SELECT *
FROM table1
UNION ALL
SELECT *
FROM table2
First select statement has duplicates :) That's normal behavior.
Try putting a distinct in the first select statement - it should also return 20740 rows.
That should help you better understand what is happening.

VARCHAR column with numeric and non-numeric data to be filtered by numeric comparasion in WHERE clause

DB: Oracle (But I'm looking for generic solution in ANSI SQL)
SELECT *
FROM TABLE1 A INNER JOIN TABLE2 B ON A.FIELD_KEY = B.FIELD_KEY
WHERE TO_NUMBER (FIELD_VALUE) < 10
I've a table (TABLE1) which stores 'Value' and 'Frequency'. Now data in the FIELD_VALUE column can be numeric as well as non-numeric. The datatype of this column is VARCHAR2. I want to filter this table where Value < 10 (say).
I understand that 'Where TO_NUMBER(Value) < 10' won't work because Value column contains non numeric data as well.
But, I'm joining table TABLE1 with table TABLE2 Such that post join only numeric values are returned in the resultset in 'Value' column and then I'm applying 'Where TO_NUMBER(Value) < 10'on this already filtered resultset. I'm expecting that since the resultset is already filtered and contains only numeric data in the 'Value' column I should be able to filter the resultset further with 'Where TO_NUMBER(Value) < 10' caluse but that ain't happening due to the fact that Oracle optimizer changes the order of my where clause and join condition thus I'm getting 'ORA-01722: invalid number' error.
Solution that works for me is:
WITH BASE_QUERY
AS (SELECT *
FROM TABLE1 A INNER JOIN TABLE2 B ON A.FIELD_KEY = B.FIELD_KEY)
SELECT *
FROM BASE_QUERY A
INNER JOIN
BASE_QUERY B
ON A.VALUE = B.VALUE AND TO_NUMBER (FIELD_VALUE) < 10
But I've to do a self-join here which is costly and unnecessary. Plus I'm not sure if this solution will work always. I mean, if Oracle changes the execution plan and executes TO_NUMBER (FIELD_VALUE) < 10 before joining the tables then the query might again fail with same `ORA-01722: invalid number ‘error.
Questions:
Is my solution guaranteed to work always?
Is my solution reasonable performance wise?
Is there a better way to do this?
But I've to do a self-join here which is costly and unnecessary.
So don't do the join at all. All you need is the rows which have the FIELD_VALUE as only DIGIT. You want to ignore the ALPHANUMERIC values. So, filter out only the digits.
For example,
TRANSLATE and REPLACE
SQL> WITH DATA AS(
2 SELECT 'mix-'||LEVEL str FROM dual CONNECT BY LEVEL <=10
3 UNION ALL
4 SELECT to_char(LEVEL) FROM dual CONNECT BY LEVEL <=10
5 )
6 SELECT str FROM DATA
7 WHERE REPLACE(translate(str, '0123456789',' '), ' ') IS NULL
8 /
STR
--------------------------------------------
1
2
3
4
5
6
7
8
9
10
10 rows selected.
SQL>
REGEXP_LIKE
SQL> WITH DATA AS(
2 SELECT 'mix-'||LEVEL str FROM dual CONNECT BY LEVEL <=10
3 UNION ALL
4 SELECT to_char(LEVEL) FROM dual CONNECT BY LEVEL <=10
5 )
6 SELECT str FROM DATA
7 WHERE REGEXP_LIKE(str, '^[[:digit:]]+')
8 /
STR
--------------------------------------------
1
2
3
4
5
6
7
8
9
10
10 rows selected.
SQL>
Personally, I would go with TRANSLATE and REPLACE, since REGEXP is still quite resource consuming.
If you can guarantee that content in field_value starting with a digit will be strictly numeric, you can employ a subquery that filters your table to leave only records that have numeric content in the field_value column:
select *
from (
select t1.*
from table t1
where t1.field_value >= '0'
and t1.field_value <= chr(ascii('9')+1) -- ':'; invariant to charset and encoding
) t
where to_number(t.field_value) < 10
;
I'd rather advise you to use one of Lalit Kumar B's solution which appear to be more robust ( think of a relaxation of the 'infer numeric from initial digit' policy in the future ).