Oracle: Select at least one record foreach cluster - sql

Look at the following query:
SELECT *
FROM ENI_FLUSSI_HUB c1
WHERE flh_tipo_processo_cod IN ('VA', 'NUOVA_ATT_ENI')
AND rownum < 10
It simply extracts just some VA. I need the extract some VA and some NUOVA_ATT_ENI.
What is the most elegant way to do it?

You can do it like this:
SELECT *
FROM ENI_FLUSSI_HUB c1
WHERE flh_tipo_processo_cod = 'VA'
AND rownum < 5
UNION
SELECT *
FROM ENI_FLUSSI_HUB c1
WHERE flh_tipo_processo_cod = 'NUOVA_ATT_ENI'
AND rownum < 5
Is there aren't any duplicate values, you can use UNION ALL to perform faster:
SELECT *
FROM ENI_FLUSSI_HUB c1
WHERE flh_tipo_processo_cod = 'VA'
AND rownum < 5
UNION ALL
SELECT *
FROM ENI_FLUSSI_HUB c1
WHERE flh_tipo_processo_cod = 'NUOVA_ATT_ENI'
AND rownum < 5
As #DavidAldridge stated, you can always use a view to make this selection.

Here's a nicely overengineered solution:
with
va as (
select rowid ri,
t.*
from eni_flussi_hub t
where flh_tipo_processo_cod = 'VA'
and rownum <= 1),
nuova_att_eni as (
select rowid ri,
t
from eni_flussi_hub t
where flh_tipo_processo_cod = 'NUOVA_ATT_ENI'
and rownum <=1),
the_rest as (
select *
from eni_flussi_hub c1
where flh_tipo_processo_cod in ('VA','NUOVA_ATT_ENI')
and rowid not in (
select ri
from va
union all
select ri
from nuova_att_eni)
and rownum <=9)
select *
from (
select * from va
union all
select * from nuova_att_eni
union all
select * from the_rest
)
where rownum <= 10
/
I think that what it does is return at least one row for each of the two values of flh_tipe_processo_cod, and "lets nature take its course" with the rest.
You'd have to edit the *'s in the main query to avoid trying to include the rowid from the first two subquery factoring clauses.
Here's another, which I think attempts to bring back five of each but will "top up" the required total if less than five are available for either of the two subquery factoring clauses:
with
va as (
select rownum rn,
t.*
from eni_flussi_hub t
where flh_tipo_processo_cod = 'VA'
and rownum <= 10),
nuova_att_eni as (
select rownum rn,
t
from eni_flussi_hub t
where flh_tipo_processo_cod = 'NUOVA_ATT_ENI'
and rownum <=10)
select *
from (
select *
from (select * from va
union all
select * from nuova_att_eni)
order by rn asc
)
where rownum <= 10
/
Enjoy!

Related

Oracle: Need to fetch the rows exponentially

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.

How can I collectively select 100 rows from 3 different tables?

I have 3 tables containing similar rows of data.
I need to select 100 rows from all of the three tables with the following conditions:
No more than 25 rows can be selected from Table A --> (name it count_a)
No more than 40 rows can be selected from Table B --> (count_b)
Any number of rows can be selected from Table C (count_c) but the number should be
count_c = 100 - (count_a + count_b)
Here is what I tried:
SELECT * FROM
(
SELECT * FROM TABLE_A WHERE ROWNUM <= 25
UNION ALL
SELECT * FROM TABLE_B WHERE ROWNUM <= 40
UNION ALL
SELECT * FROM TABLE_C
) WHERE ROWNUM <=100
But the query is too slow and does not always give me 100 rows.
Try to add WHERE ROWNUM <= 100 to the last select:
SELECT * FROM
(
SELECT TABLE_A.*, 1 as OrdRow FROM TABLE_A WHERE ROWNUM <= 25
UNION ALL
SELECT TABLE_B.*, 2 as OrdRow FROM TABLE_B WHERE ROWNUM <= 40
UNION ALL
SELECT TABLE_C.*, 3 as OrdRow FROM TABLE_C WHERE ROWNUM <= 100
) WHERE ROWNUM <=100
ORDER BY OrdRow;
Also you can try:
SELECT * FROM TABLE_A WHERE ROWNUM <= 25
UNION ALL
SELECT * FROM TABLE_B WHERE ROWNUM <= 40
UNION ALL
SELECT * FROM TABLE_C WHERE ROWNUM <=
100
-
(select count(*) TABLE_A WHERE ROWNUM <= 25)
-
(select count(*) TABLE_B WHERE ROWNUM <= 40)
Try like this,
SELECT * FROM
(
SELECT * FROM table_a where rownum <=25
UNION ALL
SELECT * FROM table_b WHERE ROWNUM <= 40
UNION ALL
SELECT * FROM table_c WHERE ROWNUM <= 100 - ((SELECT count(*) FROM table_a WHERE ROWNUM <= 25) + (SELECT count(*) FROM table_b WHERE ROWNUM <= 40))
);
Technically, you'd have to do something like this in order to guarantee that you'll always get rows from TABLE_A and TABLE_B if they exist:
SELECT * FROM (
SELECT * FROM (
SELECT 'A' t, TABLE_A.* FROM TABLE_A WHERE ROWNUM <= 25
UNION ALL
SELECT 'B' t, TABLE_B.* FROM TABLE_B WHERE ROWNUM <= 40
UNION ALL
SELECT 'C' t, TABLE_C.* FROM TABLE_C
) ORDER BY t
) WHERE ROWNUM <= 100;
This is because the optimizer is allowed to run the subqueries in any order it likes - e.g. in parallel.
With regard to performance, I suspect that the sort op will not add too much time to the execution time because it's only sorting a maximum of 100 rows anyway.
Yes ... Execution speed is a nightmare to every developer or programmer ...
In that case you can try like ... i think it will speed up your query more
DECLARE #Table_A_Row_Count INT, #Table_B_Row_Count INT,#RemainCount INT=0
--Getting count of primary tables
SELECT #Table_A_Row_Count= COUNT(1) FROM TABLE_A
SELECT #Table_B_Row_Count= COUNT(1) FROM TABLE_B
--Calculating remaining colections
IF #Table_A_Row_Count+#Table_B_Row_Count<100
BEGIN
SET #RemainCount=100-(#Table_A_Row_Count+#Table_B_Row_Count)
END
ELSE
BEGIN
--You might do somthing like this if First
--and second table covering 100 rows
SET #Table_B_Row_Count=100-#Table_A_Row_Count
END
--Finaly getting 100 rows
SELECT top #Table_A_Row_Count * FROM TABLE_A
UNION ALL
SELECT top #Table_B_Row_Count * FROM TABLE_B
UNION ALL
SELECT Top #RemainCount * FROM TABLE_C

Sql Select top 2 , bottom 2 and 6 random records

How to select top 2 , bottom 2 and 6 random (not in Top 2 and Bottom 2) records of the table using one SQL select query?
In MS SQL 2005/2008:
with cte
as
(
select
row_number() over (order by name) RowNumber,
row_number() over (order by newid()) RandomOrder,
count(*) over() Total,
*
from sys.tables
)
select *
from cte
where RowNumber <= 2 or Total - RowNumber + 1 <= 2
union all
select *
from
(
select top 6 *
from cte
where RowNumber > 2 and Total - RowNumber > 2
order by RandomOrder
) tt
Replace sys.tables with your table name and alter order by name to specify order condition for top 2 and bottom 2.
Perhaps not a single select statment, but it can be executed in one call:
/* Top 2 - change order by to get the 'proper' top 2 */
SELECT * from table ORDER BY id DESC LIMIT 2
UNION ALL
/* Random 6.. You may want to add a WHERE and random data to get the random 6 */
/* Old Statement before edit - SELECT * from table LIMIT 6 */
SELECT * from table t
LEFT JOIN (SELECT * from table ORDER BY id DESC LIMIT 2) AS top ON top.id = t.id
LEFT JOIN (SELECT * from table ORDER BY id DESC LIMIT 2) AS bottom ON bottom.id = t.id
WHERE ISNULL(top.id ) AND ISNULL(bottom.id)
ORDER BY RANDOM()
LIMIT 6
UNION ALL
/* Bottom 2 - change order by to get the 'proper' bottom 2 */
SELECT * from table ORDER BY id ASC LIMIT 2
Something along those lines. Basically the UNION All is the trick.
Assuming the "order" is by the id column:
select * from (select id, id from my_table order by id limit 2) t1
union
select * from (select id, id from my_table where id not in (
select * from (select id from my_table order by id asc limit 2) t22
union
select * from (select id from my_table order by id desc limit 2 ) t23)
order by rand()
limit 6) t2
union
select * from (select id, id from my_table order by id desc limit 2) t3
EDIT: Fixed syntax and tested query - it works

Union of two queries with the first query resultset getting the first ten rownums in Oracle

I want Union of two queries with the first query resultset getting the first ten rownums in Oracle.
Example:
Like if first query has 10 rows and max rownum is 10.I want second query rownum to be started from 11 in the result of union.
SELECT *
FROM (
SELECT *
FROM table1
ORDER BY
col1
)
WHERE rownum <= 10
UNION ALL
SELECT *
FROM (
SELECT *, rownum AS rn
FROM (
SELECT *
FROM table2
ORDER BY
col2
)
)
WHERE rn > 10

How do I intermerge multiple SELECT results?

First of all, I'm not familiar with SQL in depth, so this may be a beginner question.
I know how to select data ordered by Id: SELECT * FROM foo ORDER BY id LIMIT 100 as well as how to select a random subset: SELECT * FROM foo ORDER BY RAND() LIMIT 100.
I'd like to merge these two queries into 1 in a zip manner, choosing limit/2 from each (i.e. 50). For example:
0
85
1
35
2
38
3
19
4
...
I would like to avoid duplicates. The easiest way is probably to just add a WHERE id > 100/2 to the part of the query that retrieves randomly ordered rows.
Additional info: It is unknown how many rows exist.
To get the "zip-manner" merge add a generated rownumber to each query and use an union with order by rownnumber.
Use even numbers for one and odd numbers for the other query.
Try this for MySQL
SELECT
#rownum0:=#rownum0+2 rn,
f.*
FROM ( SELECT * FROM foo ORDER BY id ) f, (SELECT #rownum0:=0) r
UNION
SELECT #rownum1:=#rownum1+2 rn,
b.*
FROM ( SELECT * FROM bar ORDER BY RAND() ) b, (SELECT #rownum1:=-1) r
ORDER BY rn
LIMIT 100
This should be self-explicative but doesnt remove duplicates:
select #rownum:=#rownum+1 as rownum,
(#rownum-1) % 50 as sortc, u.id
from (
(select id from player order by id limit 50)
union all
(select id from player order by rand() limit 50)) u,
(select #rownum:=0) r
order by sortc,rownum;
If you replace "union all" with "union", you remove duplicates but get less rows as a consequence.
This will deal with duplicates, does not restrict random numbers in the ids > 50, and always return 100 rows:
SELECT #rownum := #rownum + 1 AS rownum,
( #rownum - 1 ) % 50 AS sortc,
u.id
FROM ((SELECT id
FROM foo
ORDER BY Rand()
LIMIT 50)
UNION
(SELECT id
FROM foo
WHERE id <= 100
ORDER BY id)) u,
(SELECT #rownum := 0) r
WHERE #rownum < 100
ORDER BY sortc,
rownum DESC
LIMIT 100;
SELECT * FROM foo ORDER BY id LIMIT 50 UNION SELECT * FROM foo ORDER BY RAND() LIMIT 50
If I understand your requirement correctly. UNION removes duplicates by itself