Hackerrank SQL problem to solve in Oracle's SQL version - sql

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;

Related

ORA-00907: missing right parenthesis in Select query

This is the table
City
Def
Abc
Ijkl
Mnop
And
I want the output, where the smallest city name is returned, who have same number of characters.
OUTPUT
Abc 3
Ijkl 4
Notice that there were 2 cities with same number of characters Abc and Def, and only Abc will be returned.
The Oracle SQL query which i am trying to run is -
Select a.city,a.leng from
(
Select city,length(city) as leng from station
order by 2,1
) as a where a.leng in (
Select distinct(length(city)) from station
order by 1);
I hope there will be numerous ways to solve but i want to correct my approach.
A simple group by should give you the desired result
Query
WITH
station (city)
AS
(SELECT 'Def' FROM DUAL
UNION ALL
SELECT 'Abc' FROM DUAL
UNION ALL
SELECT 'Ijkl' FROM DUAL
UNION ALL
SELECT 'Mnop' FROM DUAL)
SELECT MIN (city) as city, LENGTH (city) as leng
FROM station
GROUP BY LENGTH (city)
order by leng;
Result
CITY LENG
_______ _______
Abc 3
Ijkl 4
There is no reason to over complicate this with subqueries.
This should give you the desired result
Select MIN(City), LENGTH(City)
From station
Group By LENGTH(City)

SQL select two same column on same table but with different ORDER

Trainee here.
I need to create a list last names from the same table.
say we have a table named "sample", this table only consists of:
What I want to do here is both last name column will be selected but have different order, the first column would be ascending and the second column would be descending like the photo below
Here's one option: it splits first and last name into two subqueries which use row_number analytic function. It is then used to join rows.
Lines #1 - 6 represent your sample data. Query you really need begins at line #7.
SQL> with test (last_name, first_name) as
2 (select 'L1one' , 'F1one' from dual union all
3 select 'L2two' , 'F2two' from dual union all
4 select 'L3three', 'F3hthree' from dual union all
5 select 'L4four' , 'F4four' from dual
6 ),
7 ln as
8 (select last_name,
9 row_Number() over (order by last_name) rn
10 from test
11 ),
12 fn as
13 (select first_name,
14 row_number() over (order by first_name desc) rn
15 from test
16 )
17 select l.last_name, f.first_name
18 from ln l join fn f on f.rn = l.rn
19 order by l.last_name
20 /
LAST_NA FIRST_NA
------- --------
L1one F4four
L2two F3hthree
L3three F2two
L4four F1one
SQL>
[EDIT: both last names? I thought it was a typo]
If that's so, self-join is a better option:
SQL> with test (last_name, first_name) as
2 (select 'L1one' , 'F1one' from dual union all
3 select 'L2two' , 'F2two' from dual union all
4 select 'L3three', 'F3hthree' from dual union all
5 select 'L4four' , 'F4four' from dual
6 ),
7 temp as
8 (select last_name,
9 row_number() over (order by last_name asc) rna,
10 row_number() over (order by last_name desc) rnd
11 from test
12 )
13 select a.last_name, d.last_name
14 from temp a join temp d on a.rna = d.rnd
15 order by a.last_name;
LAST_NA LAST_NA
------- -------
L1one L4four
L2two L3three
L3three L2two
L4four L1one
SQL>
Here is one way to do it. In a single subquery, assign the ordinal number (rn) based on ascending order, but also keep track of the total row count. Then follow with a join.
with
test (last_name, first_name) as (
select 'L1one' , 'F1one' from dual union all
select 'L2two' , 'F2two' from dual union all
select 'L3three', 'F3hthree' from dual union all
select 'L4four' , 'F4four' from dual
)
, prep (last_name, rn, ct) as (
select last_name, row_number() over (order by last_name), count(*) over ()
from test
)
select a.last_name as last_name_asc, b.last_name as last_name_desc
from prep a inner join prep b on a.rn + b.rn = a.ct + 1
;
LAST_NAME_ASC LAST_NAME_DESC
-------------- --------------
L1one L4four
L2two L3three
L3three L2two
L4four L1one

Get duplicate employee count department wise in single sql

ID Name dep_id
1 A 1
2 B 2
3 A 1
4 A 2
5 B 2
6 A 2
I think you want to have such a SQL
with tab( ID, Name, dep_id) as
(
select 1,'A',1 union all
select 2,'B',2 union all
select 3,'A',1 union all
select 4,'A',2 union all
select 5,'B',2 union all
select 6,'A',2
)
select name,
count(dep_id) as dept_count
from tab t
group by name
having count(name)>1;
NAME DEPT_COUNT
---- ----------
A 4
B 2
Due to you last edit( which you wanted to add to this answer ), consider grouping also by dept_id :
with tab( ID, Name, dep_id) as
(
select 1,'A',1 union all
select 2,'B',2 union all
select 3,'A',1 union all
select 4,'A',2 union all
select 5,'B',2 union all
select 6,'A',2
)
select name, dept_id,
count(dept_id) as dept_count
from tab t
group by name, dept_id
having count(name)>1;
NAME DEPT_ID DEPT_COUNT
---- ------ ----------
A 2 2
A 1 2
B 2 2

Oracle SQL displaying data from multiple records

Sorry if I am not explaining this properly, I am relatively new to SQL.
In oracle have a table describing properties (city, property type, cost of rent per month, other information)
My question is: assuming 3 unique property types (hotel, house, empty lot), how can I show which cities do not have all 3 types of properties?
GROUP BY solution, make sure there are less than 3 different property types for each city returned:
select city
from tablename
where property_type in ('hotel', 'house', 'empty lot')
group by city
having count(distinct property_type) < 3
Your SQL query should be
SELECT City
FROM YourTable
WHERE hotel <> 'hotelname' and house <> 'housename' and emptylot <> 'name'
assuming
Hotel, House, Emptylot is column name in your database.
There are two ways,
GROUP BY
Analytic COUNT() OVER()
For example, Let's say I have sample data of 3 cities, where city 1 has all the property types satisfied, rest other cities are not having all the required property types.
Using GROUP BY
SQL> -- sample table data
SQL> WITH DATA AS(
2 SELECT 1 city, 'hotel' property FROM dual UNION ALL
3 SELECT 1 city, 'house' property FROM dual UNION ALL
4 SELECT 1 city, 'empty' property FROM dual UNION ALL
5 SELECT 2 city, 'hotel' property FROM dual UNION ALL
6 SELECT 2 city, 'house' property FROM dual UNION ALL
7 SELECT 2 city, 'scrap' property FROM dual UNION ALL
8 SELECT 3 city, 'empty' property FROM dual UNION ALL
9 select 3 city, 'house' property from dual
10 )
11 -- query
12 SELECT city
13 FROM data
14 WHERE property IN ('hotel', 'house', 'empty')
15 GROUP BY city
16 HAVING COUNT(property) < 3
17 /
CITY
----------
2
3
SQL>
Using Analytic COUNT() OVER()
SQL> -- sample table data
SQL> WITH DATA AS(
2 SELECT 1 city, 'hotel' property FROM dual UNION ALL
3 SELECT 1 city, 'house' property FROM dual UNION ALL
4 SELECT 1 city, 'empty' property FROM dual UNION ALL
5 SELECT 2 city, 'hotel' property FROM dual UNION ALL
6 SELECT 2 city, 'house' property FROM dual UNION ALL
7 SELECT 2 city, 'scrap' property FROM dual UNION ALL
8 SELECT 3 city, 'empty' property FROM dual UNION ALL
9 select 3 city, 'house' property from dual
10 )
11 -- query
12 SELECT DISTINCT city
13 FROM
14 (SELECT t.* ,
15 COUNT(property) OVER(PARTITION BY city ORDER BY city) rn
16 FROM DATA t
17 WHERE property IN ('hotel', 'house', 'empty')
18 )
19 WHERE rn < 3
20 /
CITY
----------
2
3
SQL>
Could be something like this:
SELECT City
FROM YourTable
WHERE [property type] != 'hotel' OR
[property type] != 'empty lot' OR
[property type] != 'house'
(Edited)Try this query :
select t.city from table_name t
where t.city NOT IN
(select city from table_name
where ( property_type ='hotel' or
property_type ='house' or
property_type ='Empty lot')
);
(Query returning cities where all three types of property are not present):
select t.city from table t inner join
(select city from table
where property_type not in ('House','Hotel','Empty lot')) x
on t.city=x.city
group by t.city
having count(*)<3 ;

SQL find nearest number

Say I have a table like the following (I'm on Oracle 10g btw)
NAME VALUE
------ ------
BOB 1
BOB 2
BOB 4
SUZY 1
SUZY 2
SUZY 3
How can I select all rows where value is closest to, but not greater than, a given number. For example if I want to find all the rows where value is closest to 3 I would get:
NAME VALUE
------ ------
BOB 2
SUZY 3
This seems like it should be simple... but I'm having no luck.
Thanks!
SELECT name, max(value)
FROM tbl
WHERE value <= 3
GROUP BY name
This works (SQLFiddle demo):
SELECT name, max(value)
FROM mytable
WHERE value <= 3
GROUP BY name
Based on hagensofts answer:
SELECT name, max(value)
FROM tbl
WHERE value <= 3 AND ROWNUM <=2
GROUP BY name
With ROWNUM you can limit the output rows, so if you want 2 row, then you can limit the rownum.
WITH v AS (
SELECT 'BOB' NAME, 1 value FROM dual
UNION ALL
SELECT 'BOB', 2 FROM dual
UNION ALL
SELECT 'BOB', 4 FROM dual
UNION ALL
SELECT 'SUZY', 1 FROM dual
UNION ALL
SELECT 'SUZY', 2 FROM dual
UNION ALL
SELECT 'SUZY', 3 FROM dual
)
SELECT *
FROM v
WHERE (name, value) IN (SELECT name, MAX(value)
FROM v
WHERE value <= :num
GROUP BY name)
;