I have a table with one of the columns is of type varchar(city). and want to find the longest and shortest of values stored in that column.
select a.city, a.city
This is an approach with a CTE. First it finds the longest and shortest, than the matching cities:
DECLARE @tbl TABLE(CityName VARCHAR(100));
INSERT INTO @tbl VALUES ('xy'),('Long name'),('very long name'),('middle'),('extremely long name');
WITH MyCTE AS
(
SELECT MAX(LEN(CityName)) AS Longest
,MIN(LEN(CityName)) AS Shortest
FROM @tbl
)
SELECT *
FROM MyCTE
--You must think about the chance of more than one city matching the given length
CROSS APPLY(SELECT TOP 1 CityName FROM @tbl WHERE LEN(CityName)=Longest) AS LongestCity(LongName)
CROSS APPLY(SELECT TOP 1 CityName FROM @tbl WHERE LEN(CityName)=Shortest) AS ShortestCity(ShortName)
The result
Longest Shortest LongName ShortName
19 2 extremely long name xy
I used the WITH clause for Oracle to save the shortest /longest names in a temporary variable rather than querying inside the main "from" clause. SQL WITH clause example
WITH shortnames AS
(SELECT city, length(city)
FROM station
ORDER BY length(city) asc, city),
longnames AS
(SELECT city, length(city)
FROM station
ORDER BY length(city) desc, city)
SELECT * FROM shortnames WHERE ROWNUM=1
UNION ALL
SELECT * FROM longnames WHERE ROWNUM=1;
For shortest name of city :
SELECT ST.CITY,LENGTH(ST.CITY) AS LENGTH FROM STATION ST
ORDER BY LENGTH ASC, ST.CITY ASC
LIMIT 1;
For longest name of city :
SELECT ST.CITY,LENGTH(ST.CITY) AS LENGTH FROM STATION ST
ORDER BY LENGTH DESC, ST.CITY DESC
LIMIT 1;
select * from (select city,length(city)from station group by city having length(city) in ((select min(length(city))from station))order by city) where rownum<2
UNION
select * from (select city,length(city)from station group by city having length(city) in ((select max(length(city))from station))order by city) where rownum<2;
I don’t think that we need to use Min and Max functions and Group by is also not required.
We can achieve this using the below code:
select top 1 City, LEN(City) City_Length from STATION order by City_Length ASC,City ASC
select top 1 CITY, LEN(city) City_Length from station order by City_Length desc, City ASC
but in this case, it will display output in 2 table and if we would like to combine in a single table then we can use Union or Union ALL. Below is the SQL query for the same
select * from (
select top 1 City, LEN(City) City_Length from STATION order by City_Length ASC,City ASC) TblMin
UNION
select * from (
select top 1 CITY, LEN(city) City_Length from STATION order by City_Length desc, City ASC) TblMax
here I am nesting the select statement inside a subquery because when we are using order by clause then we cannot use Union or Union ALL directly that is why I have written it inside a subquery.
I did this in SQL Server using CTE and dense_rank function. How the ranking works?
First partition (form groups) over the lengths, i.e same lengths make a group (partition). Then order all the names alphabetically within each partition. Then assign ranks (dRank column) within each partition. So rank 1s in each group will be assigned to names which alphabetically appear first in their respective partition. All this happens in the common table expression (cte block)
"with cte as
(
select *, LEN(city) as length, DENSE_RANK() over (partition by len(city) order by city) as dRank from Station
)"
select city,length from cte where dRank = 1 and length = (select MIN(length) from cte)
UNION
select city,length from cte where dRank = 1 and length = (select max(length) from cte)"