select first 10 rows, then decide which rows to include afterwards - sql

I have 2 columns in table. The 1st column represents the name of different companies and the 2nd column displays the count of products.
COL 1 COL 2
CompA 2323
CompB 2320
CompC 1999
CompD 1598
CompE 1400...so on
What i want to do is to display first 10 rows showing first 10 companies name and its count in desc order.
Then I want to compare 10th company count with 11th company count. If they match, then display 11th company name and its count. If 10th company count doesn't match 11th company count, then display first 10 records only.
I have read only access so I can't update or insert new records in table.
How can this be done?

If what you want to do is display the top ten counts, including ties. This is simple to solve with an ANALYTIC function such as RANK() or DENSE_RANK() ...
SQL> select * from
2 ( select
3 ename
4 , sal
5 , rank() over (order by sal desc) sal_rank
6 from emp
7 )
8 where sal_rank <= 10
9 /
ENAME SAL SAL_RANK
---------- ---------- ----------
QUASSNOI 6500 1
SCHNEIDER 5000 2
FEUERSTEIN 4500 3
VERREYNNE 4000 4
LIRA 3750 5
PODER 3750 5
KESTELYN 3500 7
TRICHLER 3500 7
GASPAROTTO 3000 9
ROBERTSON 2990 10
RIGBY 2990 10
11 rows selected.
SQL>
Note that if RIGBY had had the same salary as GASPAROTTO, their SAL_RANK would have been 9, ROBERTSON's would have been 11 and the result set would have comprised ten rows.
DENSE_RANK() differs from RANK() in that it always returns the top ten whatevers, instead of skipping ties...
SQL> select * from
2 ( select
3 ename
4 , sal
5 , dense_rank() over (order by sal desc) sal_rank
6 from emp
7 )
8 where sal_rank <= 10
9 /
ENAME SAL SAL_RANK
---------- ---------- ----------
1
SCHNEIDER 5000 2
FEUERSTEIN 4500 3
VERREYNNE 4000 4
LIRA 3750 5
PODER 3750 5
KESTELYN 3500 6
TRICHLER 3500 6
GASPAROTTO 3000 7
ROBERTSON 2990 8
RIGBY 2990 8
SPENCER 2850 9
BOEHMER 2450 10
13 rows selected.
SQL>

Try this:
SELECT col1, col2
FROM (
SELECT col1, col2
FROM Table
WHERE col2 >= (SELECT col2
FROM (SELECT col2 FROM Table ORDER BY col2 DESC) t1
WHERE t1.ROWNUM = 10)
ORDER BY col2 DESC) t2
WHERE t2.ROWNUM <= 11

With Top10Co As
(
Select Col1 As CompanyName, Col2 As Cnt
, Row_Number() Over ( Order By Col2 Desc ) As Num
From MyTable
)
Select CompanyName, Cnt
From Top10Co
Where Num <= 10
Union All
Select Col1, Col2
From MyTable
Where Exists (
Select 1
From Top10Co As T2
Where T2.Num = 10
And T2.CompanyName <> MyTable.Col1
And T2.Cnt = MyTable.Col2
)

I think that what you are saying can be done by writing a pl/sql block of code of top-n query.
some thing like this would help
decalre
v_col_1 companies.col_1%TYPE;
v_col_2 companies.col_2%TYPE;
count number;
col_2_all number;
CURSOR companies is SELECT *
FROM (select * from companies ORDER BY col_2)
WHERE rownum <= 10
ORDER BY rownum;
begin
loop
fetch companies into v_cal_1,v_col_2;
count++;
if count =10 then
col_2_all=v_col_2
dbms_output.put_line('company name'||v_cal_1||'company count'||v_cal_2);
elsif count =11 then
if col_2_all=v_col_2 then
dbms_output.put_line('company name'||v_cal_1||'company count'||v_cal_2);
end if;
end if;
EXIT WHEN count>11;
end;
i am not sure about the syntax but it has to be something like this :)

Related

Oralce sql:I want to select the TOP 3 Records [duplicate]

This question already has answers here:
How do I limit the number of rows returned by an Oracle query after ordering?
(14 answers)
Closed 8 months ago.
I want to select the TOP 3 Records ordered desc by 'cnt'
this is top 4
a b c cnt
99 YC 市購件異常 3
99 LY 漏油 2
99 QT16 其他異常 2
99 JGSH 機構損壞 1
then
select * from ()where rownum<= 3 order by cnt desc
get data
99 YC 市購件異常 3
99 LY 漏油 2
99 JGSH 機構損壞 1
i want to get
99 YC 市購件異常 3
99 LY 漏油 2
99 QT16 其他異常 2
Try this:
SELECT T.a, T.b, T.c, T.cnt
FROM
(
SELECT *, RANK() OVER(PARTITION BY a ORDER BY cnt DESC) RNK
FROM TEST_TBL
) T
WHERE T.RNK <= 3
It looks like you want to keep "duplicates" (in the cnt column) in the result.
In that case, I'd say that it is row_number analytic function that helps:
Sample data:
SQL> with test (a, b, cnt) as
2 (select 99, 'yc' , 3 from dual union all
3 select 99, 'ly' , 2 from dual union all
4 select 99, 'qt16', 2 from dual union all
5 select 99, 'jgsh', 1 from dual union all
6 --
7 select 99, 'abc' , 2 from dual --> yet another row with CNT = 2
8 ),
Query begins here: first rank rows (line #11), and then return the top 3 (line #15):
9 temp as
10 (select a, b, cnt,
11 row_number() over (partition by a order by cnt desc) rnk
12 from test
13 )
14 select * from temp
15 where rnk <= 3;
A B CNT RNK
---------- ---- ---------- ----------
99 yc 3 1
99 ly 2 2
99 abc 2 3
SQL>
Because, if you use rank analytic function (as Hana suggested), you might get more than desired 3 rows (see the rnk column's values) (depending on data you work with, of course; rank works with data you posted, but - if there are more rows that share the same cnt value, it won't work any more):
<snip>
9 temp as
10 (select a, b, cnt,
11 rank() over (partition by a order by cnt desc) rnk
12 from test
13 )
14 select * from temp
15 where rnk <= 3;
A B CNT RNK
---------- ---- ---------- ----------
99 yc 3 1
99 ly 2 2
99 abc 2 2
99 qt16 2 2
SQL>

I want to get the last record from oracle using rownumber()

here am using oracle query for below record, i want to last rownumber 11 record.
select max(t.atdatetime),
t.lead,
t.sysid,
row_number() over(partition by t.sysid order by t.lead desc) as "number" from psd.psd_empreport t where t.sysid in(5350) group by t.lead, t.sysid order by 2 desc
SNO datetime tlcode ecode rownumb
1 7/2/2013 6:00:25 AM 67 5350 1
2 10/27/2014 8:30:34 AM 5508 5350 2
3 10/24/2014 8:30:21 AM 5477 5350 3
4 9/22/2012 12:28:20 AM 5051 5350 4
5 10/10/2012 12:28:47 AM 4736 5350 5
6 5/13/2014 8:24:21 AM 4459 5350 6
7 9/12/2012 12:28:01 AM 3688 5350 7
8 2/7/2013 12:32:34 AM 227 5350 8
9 3/27/2013 12:34:39 AM 140 5350 9
10 3/1/2013 12:33:27 AM 13 5350 10
11 9/9/2014 8:28:41 AM 122 5350 11
i want to last rownumber 11 record.
You just need to ORDER BY DESC in the ROW_NUMBER() window and then select the row having the row_number as 1 from the subquery.
For example,
SQL> SELECT *
2 FROM
3 ( SELECT empno, row_number() OVER(ORDER BY empno DESC) rn FROM emp
4 )
5 WHERE rn = 1;
EMPNO RN
---------- ----------
7934 1
So, make your existing query a subquery and filter the row_number as where rn = 1.

Assigning random value to each record, not just whole query

I have written a query which needs to match one of five possible values to around 1500 records randomly for each record. I have managed to get it to assign a value randomly, but the value assigned is the same for every record. Is there a way of doing this without using PL/SQL? Please let me know your thoughts. Query is below (database is Oracle 11g):
select
ioi.ioi_mstc
,ioi.ioi_seq2
,max(decode(rn, (select round(dbms_random.value(1,5)) num from intuit.srs_ioi where rownum < 2), uddc))
from
intuit.srs_ioi ioi
,intuit.srs_cap cap
,(select
sub.udd_code uddc
,row_number() over(partition by sub.udd_udvc order by rownum) rn
from
(select * from
intuit.men_udd udd
where
udd.udd_udvc = 'PA_REJ_REAS'
order by dbms_random.value) sub
where rownum <= 5) rejReas
where
ioi.ioi_stuc = cap.cap_stuc
and ioi.ioi_iodc = 'PAPERLESS'
and cap.cap_ayrc = '2013/4'
and cap.cap_idrc like '%R%'
group by ioi.ioi_mstc
,ioi.ioi_seq2
This is due to sub-query caching. Consider the following query and the values returned:
SQL> with numbers as (
2 select level as lvl
3 from dual
4 connect by level <= 10
5 )
6 select lvl
7 , ( select dbms_random.value(1,5)
8 from dual ) as sq
9 , dbms_random.value(1,5) as nsq
10 from numbers
11 ;
LVL SQ NSQ
---------- ---------- ----------
1 2.56973281 2.86381746
2 2.56973281 3.54313541
3 2.56973281 1.71969631
4 2.56973281 3.71918833
5 2.56973281 3.10287264
6 2.56973281 3.9887797
7 2.56973281 2.6800834
8 2.56973281 3.57760938
9 2.56973281 2.47035426
10 2.56973281 3.77448435
10 rows selected.
SQL>
The value is being cached by the sub-query; simply remove it.
select ioi.ioi_mstc
, ioi.ioi_seq2
, max(decode(rn, round(dbms_random.value(1,5)) , uddc))
from ...

Accumulate a summarized column

I could need some help with a SQL statement. So I have the table "cont" which looks like that:
cont_id name weight
----------- ---------- -----------
1 1 10
2 1 20
3 2 40
4 2 15
5 2 20
6 3 15
7 3 40
8 4 60
9 5 10
10 6 5
I then summed up the weight column and grouped it by the name:
name wsum
---------- -----------
2 75
4 60
3 55
1 30
5 10
6 5
And the result should have a accumulated column and should look like that:
name wsum acc_wsum
---------- ----------- ------------
2 75 75
4 60 135
3 55 190
1 30 220
5 10 230
6 5 235
But I didn't manage to get the last statement working..
edit: this Statement did it (thanks Gordon)
select t.*,
(select sum(wsum) from (select name, SUM(weight) wsum
from cont
group by name)
t2 where t2.wsum > t.wsum or (t2.wsum = t.wsum and t2.name <= t.name)) as acc_wsum
from (select name, SUM(weight) wsum
from cont
group by name) t
order by wsum desc
So, the best way to do this is using cumulative sum:
select t.*,
sum(wsum) over (order by wsum desc) as acc_wsum
from (<your summarized query>) t
The order by clause makes this cumulative.
If you don't have that capability (in SQL Server 2012 and Oracle), a correlated subquery is an easy way to do it, assuming the summed weights are distinct values:
select t.*,
(select sum(wsum) from (<your summarized query>) t2 where t2.wsum >= t.wsum) as acc_wsum
from (<your summarized query>) t
This should work in all dialects of SQL. To work with situations where the accumulated weights might have duplicates:
select t.*,
(select sum(wsum) from (<your summarized query>) t2 where t2.wsum > t.wsum or (t2.wsum = t.wsum and t2.name <= t.name) as acc_wsum
from (<your summarized query>) t
try this
;WITH CTE
AS
(
SELECT *,
ROW_NUMBER() OVER(ORDER BY wsum) rownum
FROM #table1
)
SELECT
c1.name,
c1.wsum,
acc_wsum= (SELECT SUM(c2.wsum)
FROM cte c2
WHERE c2.rownum <= c1.rownum)
FROM CTE c1;
or you can join instead of using subquery
;WITH CTE
AS
(
SELECT *,
ROW_NUMBER() OVER(ORDER BY usercount) rownum
FROM #table1
)
SELECT
c1.name,
c1.wsum,
acc_wsum= SUM(c2.wsum)
FROM CTE c1
INNER JOIN CTE c2 ON c2.rownum <= c1.rownum
GROUP BY c1.name, c1.wsum;

Oracle SQL- Getting "Distinct" values within a "CASE" query

I have the following query in ORACLE SQL:
Select
Trunc(Cs.Create_Dtime),
Count(Case When Cs.Cs_Listing_Id Like '99999999%' Then (Cs.Player_Id) End) As Sp_Dau,
Count(Case When Cs.Cs_Listing_Id Not Like '99999999%' Then (Cs.Player_Id) End) As Cs_Dau
From
Player_Chkin_Cs Cs
Where
Trunc(Cs.Create_Dtime) >= To_Date('2012-Jan-01','yyyy-mon-dd')
Group By Trunc(Cs.Create_Dtime)
Order By 1 ASC
I added "Distinct" just before "case" for each count. I just want to make sure that this only returns all of the distinct player_Ids in each case. Can some one confirm? Thank you! Here is the final query:
Select
Trunc(Cs.Create_Dtime),
Count(Distinct Case When Cs.Cs_Listing_Id Like '99999999%' Then (Cs.Player_Id) End) As Sp_Dau,
Count(Distinct Case When Cs.Cs_Listing_Id Not Like '99999999%' Then (Cs.Player_Id) End) As Cs_Dau
From
Player_Chkin_Cs Cs
Where
Trunc(Cs.Create_Dtime) >= To_Date('2012-Jan-01','yyyy-mon-dd')
Group By Trunc(Cs.Create_Dtime)
Order By 1 ASC;
A simple test case for you to prove count(distinct ... returns only distinct values:
11:34:09 HR#vm_xe> select department_id, count(*) from employees group by department_id order by 2 desc;
DEPARTMENT_ID COUNT(*)
------------- ----------
50 45
80 34
100 6
30 6
60 5
90 3
20 2
110 2
40 1
10 1
1
70 1
12 rows selected.
Elapsed: 00:00:00.03
11:34:12 HR#vm_xe> select count(department_id) "ALL", count(distinct department_id) "DISTINCT" from employees;
ALL DISTINCT
---------- ----------
106 11
1 row selected.
Elapsed: 00:00:00.02
11:34:20 HR#vm_xe>