Get highest value ID - sql

I need to print the Region ID and the count of countries he holds, but only for the highest one.
My table is like this:
COUNTRY_ID COUNTRY_NAME REGION_ID
AR Argentina 2
AU Belgium 3
BE Brazil 1
BR Canada 2
CA Switzerland 2
CN China 1
DE Germany 3
And the result must be something like:
REGION_ID Quantity
2 3

You can use below sql for that purpose.
I use row_number analytic function to rank the rows after being grouped by REGION_ID column. Then, I filter by rnb = 1 to get the row with maximun quantity.
select REGION_ID, QUANTITY
from (
select t.REGION_ID, count(*)quantity, row_number()over(order by count(*) desc) rnb
from YourTable t
group by t.REGION_ID
)
where rnb = 1
demo

From Oracle 12c, you can use:
SELECT region_id,
COUNT(*) AS quantity
FROM table_name
GROUP BY region_id
ORDER BY quantity DESC
FETCH FIRST ROW ONLY;
Before that:
SELECT *
FROM (
SELECT REGION_ID,
COUNT(*) AS quantity
FROM table_name
GROUP BY region_id
ORDER BY quantity DESC
)
WHERE ROWNUM = 1;
Which, for your sample data:
CREATE TABLE table_name (COUNTRY_ID, COUNTRY_NAME, REGION_ID) AS
SELECT 'AR', 'Argentina', 2 FROM DUAL UNION ALL
SELECT 'AU', 'Belgium', 3 FROM DUAL UNION ALL
SELECT 'BE', 'Brazil', 1 FROM DUAL UNION ALL
SELECT 'BR', 'Canada', 2 FROM DUAL UNION ALL
SELECT 'CA', 'Switzerland', 2 FROM DUAL UNION ALL
SELECT 'CN', 'China', 1 FROM DUAL UNION ALL
SELECT 'DE', 'Germany', 3 FROM DUAL;
Both output:
REGION_ID
QUANTITY
2
3
db<>fiddle here

In Oracle you may nest aggregate functions to calculate total on group by result. So you may use last aggregate function to get highest id:
select
max(region_id) keep(
dense_rank last
/*
region_id for highest count(*)
calculated per region_id
*/
order by count(*) asc
) as region_id,
max(count(*)) as quantity
from table_name
group by region_id
REGION_ID | QUANTITY
--------: | -------:
2 | 3
db<>fiddle here

Related

Hackerrank SQL problem to solve in Oracle's SQL version

Query the two cities in STATION with the shortest and longest CITY names, as well as their respective lengths (i.e.: number of characters in the name). If there is more than one smallest or largest city, choose the one that comes first when ordered alphabetically.
The STATION table is described as follows:
Sample Input
For example, CITY has four entries: DEF, ABC, PQRS and WXY.
Sample Output
ABC 3
PQRS 4
Explanation
When ordered alphabetically, the CITY names are listed as ABC, DEF, PQRS, and WXY, with lengths and . The longest name is PQRS, but there are options for shortest named city. Choose ABC, because it comes first alphabetically.
A little bit of analytic functions; sample data in lines #1 - 6; query begins at line #7.
SQL> with station (city) as
2 (select 'DEF' from dual union all
3 select 'ABC' from dual union all
4 select 'PQRS' from dual union all
5 select 'WXY' from dual
6 )
7 select city, len
8 from (select city,
9 length(city) len,
10 rank() over (partition by length(city) order by city) rn
11 from station
12 )
13 where rn = 1
14 order by city;
CITY LEN
---- ----------
ABC 3
PQRS 4
SQL>
Reading your comment, it seems you want something like this:
SQL> with station (city) as
2 (select 'DEF' from dual union all
3 select 'ABC' from dual union all
4 select 'PQRS' from dual union all
5 select 'WXY' from dual union all
6 select 'XX' from dual union all
7 select 'ABCDE' from dual
8 )
9 select city, len
10 from (select city,
11 length(city) len,
12 rank() over (order by length(city) , city) rna,
13 rank() over (order by length(city) desc, city) rnd
14 from station
15 )
16 where rna = 1
17 or rnd = 1
18 order by len, city;
CITY LEN
----- ----------
XX 2
ABCDE 5
SQL>
Try this SQL statement with the fetch first row only clause:
with station (city) as
(select 'DEF' from dual union all
select 'ABC' from dual union all
select 'PQRS' from dual union all
select 'WXY' from dual)
(select city,
length(city)
from station
order by 2, 1
fetch first row only)
union
(select city,
length(city)
from station
order by 2 desc, 1
fetch first row only);
I solved the question this way:
select min(tt.city), tt.city_length
from (select s.city, length(s.city) city_length
from station s
where length(s.city) = (select max(length(t.city)) from station t)
or length(s.city) = (select min(length(p.city)) from station p)
order by 2, 1) tt
group by tt.city_length;
You can use the ROW_NUMBER analytic function in the ORDER BY clause and then FETCH FIRST ROW WITH TIES:
SELECT city,
LENGTH(city) AS length
FROM station
ORDER BY
LEAST(
ROW_NUMBER() OVER ( ORDER BY LENGTH( city ) ASC, city ),
ROW_NUMBER() OVER ( ORDER BY LENGTH( city ) DESC, city )
)
FETCH FIRST ROW WITH TIES;
Which, for the sample data:
CREATE TABLE station ( city ) AS
SELECT 'ABC' FROM DUAL UNION ALL
SELECT 'DEF' FROM DUAL UNION ALL
SELECT 'PQRS' FROM DUAL UNION ALL
SELECT 'XYZ' FROM DUAL;
Outputs:
CITY | LENGTH
:--- | -----:
PQRS | 4
ABC | 3
db<>fiddle here
select min(city) || ' ' ||length(min(city)) from station
UNION
select max(city) || ' ' ||length(max(city)) from station;

Concat multiple rows based on If/Then to poulate a 5 digit code

Hoping someone can help.
I have data as follows in two seperate columns in a table called StudentRace
Student_ID RaceCD
---------- ------
123456 1
123456 2
589645 4
987654 3
987654 4
I am looking for a way to combine the data for the students by student id to output into 00000 format. example: Student_ID 123456 RACE: 12000; Student_ID 589645 Race: 00040; Student_ID 987654 Race = 00340. I need to have it be a sub query as it is part of a large report that pulls 50+ fields. If anyone is able to help I would greatly appreciate it. I am using Toads Data Point for Oracle to create my query.
The result can be achieved with a single group by without any joins.
Setup
CREATE TABLE studentrace
(
Student_ID,
RaceCD
)
AS
SELECT 123456, 1 FROM DUAL
UNION ALL
SELECT 123456, 2 FROM DUAL
UNION ALL
SELECT 589645, 4 FROM DUAL
UNION ALL
SELECT 987654, 3 FROM DUAL
UNION ALL
SELECT 987654, 4 FROM DUAL;
Query
SELECT student_id, LPAD (SUM (racecd * POWER (10, 5 - racecd)), 5, '0') AS race
FROM studentrace
GROUP BY student_id;
Result
STUDENT_ID RACE
_____________ ________
589645 00040
987654 00340
123456 12000
You can use a partitioned outer join:
SELECT t.Student_id,
LISTAGG( COALESCE( t.raceCD, 0 ) ) WITHIN GROUP ( ORDER BY r.race )
AS RaceCDs
FROM ( SELECT LEVEL AS race
FROM DUAL
CONNECT BY LEVEL <= 5 ) r
LEFT OUTER JOIN table_name t
PARTITION BY ( t.Student_ID )
ON ( r.race = t.RaceCD )
GROUP BY t.student_id
Which, for your test data:
CREATE TABLE table_name ( Student_ID, RaceCD ) AS
SELECT 123456, 1 FROM DUAL UNION ALL
SELECT 123456, 2 FROM DUAL UNION ALL
SELECT 589645, 4 FROM DUAL UNION ALL
SELECT 987654, 3 FROM DUAL UNION ALL
SELECT 987654, 4 FROM DUAL
Outputs:
STUDENT_ID | RACECDS
---------: | :------
123456 | 12000
589645 | 00040
987654 | 00340
db<>fiddle here

Write a Query to show Id, Name and No. of department?

I am trying to write a Query to show Id, Name and No. of department in given Table which are referring more than one department.
ID Name Department
-- ---- ----------
1 Sam HR
1 Sam FINANCE
2 Ron PAYROLL
3 Kia HR
3 Kia IT
Result :
ID Name Department
-- ---- ----------
1 Sam 2
3 Kia 2
I tried using group by id and using count(*), but query is giving error.
How can I do this?
Without seeing your query, a blind guess is that you wrongly wrote the GROUP BY clause (if you used it) and forgot to include the HAVING clause.
Anyway, something like this might be what you're looking for:
SQL> with test (id, name, department) as
2 (select 1, 'sam', 'hr' from dual union
3 select 1, 'sam', 'finance' from dual union
4 select 2, 'ron', 'payroll' from dual union
5 select 3, 'kia', 'hr' from dual union
6 select 3, 'kia', 'it' from dual
7 )
8 select id, name, count(*)
9 from test
10 group by id, name
11 having count(*) > 1
12 order by id;
ID NAM COUNT(*)
---------- --- ----------
1 sam 2
3 kia 2
SQL>
You were right about using count(). You need to group by other columns though and only count unique departments then filter on the number in having clause.
select id, name, count(distinct department) as no_of_department
from table
group by id, name
having count(distinct department) > 1
This can also be done using analytic functions like below:
select *
from (
select id, name, count(distinct department) over (partition by id, name) as no_of_department
from table
) t
where no_of_department > 1
You can use window function with subquery :
select distinct id, name, Noofdepartment
from (select t.*, count(*) over (partition by id,name) Noofdepartment
from table t
) t
where Noofdepartment > 1;
However, you can also use group by clause:
select id, name, count(*) as Noofdepartment
from table t
group by id, name
having count(*) > 1;

Select Duplicate records in Oracle

I have a table below in Oracle
Table1
State | Product |other fields
CA | P1 | xxxx
OR | P1 | xxxx
OR | P1 | xxxx
OR | P1 | xxxx
WA | P1 | xxxx
VA | P2 | xxxx
My Output should be only select if State has been occurred more than once.
State | Product |other fields
OR | P1 | xxxx
If you just want to count duplicate states then:
SELECT DISTINCT
State,
Product,
Other_Fields
FROM (
SELECT t.*,
COUNT(1) OVER ( PARTITION BY State ) AS cnt
FROM Table1 t
)
WHERE cnt > 1;
If you want to consider duplicate rows (considering all fields) then:
SELECT *
FROM Table1
GROUP BY State, Product, Other_Fields
HAVING COUNT(1) > 1;
select state, product, column_3, column_4
from (
select state, product, column_3, column_4,
count(*) over (partition by state) as cnt
from the_table
) t
where cnt > 1;
You could use ROW_NUMBER analytic function.
For example,
SQL> WITH sample_data AS(
2 SELECT 'CA' State, 'P1' product FROM dual UNION ALL
3 SELECT 'OR', 'P1' product from dual union all
4 SELECT 'OR', 'P1' product FROM dual UNION ALL
5 SELECT 'OR', 'P1' product FROM dual UNION ALL
6 SELECT 'WA', 'P1' product FROM dual UNION ALL
7 SELECT 'VA', 'P2' product from dual
8 )
9 -- end of sample_data mimicking real table
10 SELECT distinct state,
11 product
12 FROM
13 (SELECT state,
14 product,
15 row_number() OVER(PARTITION BY state ORDER BY product) rn
16 FROM sample_data
17 )
18 WHERE rn >1;
ST PR
-- --
OR P1
SQL>

Use SUM function in oracle

I have a table in Oracle which contains :
id | month | payment | rev
----------------------------
A | 1 | 10 | 0
A | 2 | 20 | 0
A | 2 | 30 | 1
A | 3 | 40 | 0
A | 4 | 50 | 0
A | 4 | 60 | 1
A | 4 | 70 | 2
I want to calculate the payment column (SUM(payment)). For (id=A month=2) and (id=A month=4), I just want to take the greatest value from REV column. So that the sum is (10+30+40+70)=150. How to do it?
You can also use below.
select id,sum(payment) as value
from
(
select id,month,max(payment) from table1
group by id,month
)
group by id
Edit: for checking greatest rev value
select id,sum(payment) as value
from (
select id,month,rev,payment ,row_number() over (partition by id,month order by rev desc) as rno from table1
) where rno=1
group by id
This presupposes you don't have more than one value per rev. If that's not the case, then you probably want a row_number analytic instead of max.
with latest as (
select
id, month, payment, rev,
max (rev) over (partition by id, month) as max_rev
from table1
)
select sum (payment)
from latest
where rev = max_rev
Or there's this, if I've understood the requirement right:
with demo as (
select 'A'as id, 1 as month, 10 as payment, 0 as rev from dual
union all select 'A',2,20,0 from dual
union all select 'A',2,30,1 from dual
union all select 'A',3,40,0 from dual
union all select 'A',4,50,0 from dual
union all select 'A',4,60,1 from dual
union all select 'A',4,70,2 from dual
)
select sum(payment) keep (dense_rank last order by rev)
from demo;
You can check the breakdown by including the key columns:
with demo as (
select 'A'as id, 1 as month, 10 as payment, 0 as rev from dual
union all select 'A',2,20,0 from dual
union all select 'A',2,30,1 from dual
union all select 'A',3,40,0 from dual
union all select 'A',4,50,0 from dual
union all select 'A',4,60,1 from dual
union all select 'A',4,70,2 from dual
)
select id, month, max(rev)
, sum(payment) keep (dense_rank last order by rev)
from demo
group by id, month;
select sum(payment) from tableName where id='A' and month=2 OR month=4 order by payment asc;