Sql - Use the biggest value to select other data - sql

I've a big doubt on the way to retrieve the biggest value of a table and use it in another query.
Consider this :
CREATE TABLE people
(
peopleID int NOT NULL,
cityID int NOT NULL
)
The following request gives me the number of people per city
SELECT peopleID, COUNT(*)
FROM people
GROUP BY cityID
Suppose I want the people list of the biggest city, I would write this request like :
SELECT people.peopleID, people.cityID
FROM people,
(
SELECT cityID, COUNT(*) AS "people_count"
FROM people
GROUP BY cityID
) g
WHERE people.cityID = g.cityID
HAVING people_count = MIN(people_count)
but doesn't work, what's the best way to achieve this request?
Thanks :)

This technique should work in most databases:
SELECT peopleID
FROM people
WHERE cityID =
(
SELECT cityID
FROM people
GROUP BY cityID
ORDER BY COUNT(*) DESC
LIMIT 1
)
LIMIT 1 is not standard SQL (the standard states that you should use FETCH FIRST 1 ROWS ONLY). See here for a list of how to fetch only the first row in a variety of databases:
Select (SQL) - Result limits
Edit: I misunderstood your question. I thought you meant what is a sensible way to perform this query that can easily be modified to work in almost any SQL database. But it turns out what you actually want to know is how to write the query so that it will work using exactly the same syntax in all databases. Even random databases that no-one uses that don't even properly support the SQL standard. In which case you can try this but I'm sure you can find a database where even this doesn't work:
SELECT peopleID, cityID
FROM people
WHERE cityID = (
SELECT MAX(cityID)
FROM (
SELECT cityID
FROM people
GROUP BY cityID
HAVING COUNT(*) =
(
SELECT MAX(cnt) FROM
(
SELECT cityID, COUNT(*) AS cnt
FROM people
GROUP BY cityID
) T1
)
) T2
)

Related

How to get the three most common foreign keys from a table in a T-SQL procedure

My DBMS professor wants me to write a SQL procedure which checks whether an entered author ID is one of the three most common authors in a table of problems. Authors which work on the most problems, at least the top three, get higher raises applied to their entries in the Author table.
Each entry in the Problem table has an AuthorID as a foreign key. I need to count the number of problems associated with each author and list them in order so that I can check if the AuthorID passed as a parameter into the procedure is one of the top three entries in the counted list.
Finding that list isn't the problem. There are plenty of other problems showing how to use GROUP BY, ORDER BY, and COUNT() to find the table of highest authors. The issue is that SQL Server doesn't seem to want me to select the information from a table created by GROUP BY.
I can use if-then statements after that to finish off.
The problem table looks like this
CREATE TABLE Problem
(
pid int,
pname varchar(255),
max_score int,
aid int
)
One method is:
select aid
from (select aid, rank() over (order by count(*) desc) as seqnum
from Problem
group by aid
) a
where seqnum <= 3;
This demonstrates combining window functions with aggregation functions.
Of course this is not stored procedure, this is just query, but may be this what you need?
select aid from (
select aid, rank() over(order by cnt desc) rnk from (
select aid, count(*) cnt
from Problem
group by aid
) t
) tt
where rnk <= 3;

SQL: find most common values for specific members in column 1

I have the following SQL related question:
Let us assume I have the following simple data table:
I would like to identify the most common street address and place it in column 3:
I think this should be fairly straight-forward using COUNT? Not quite sure how to go about it though. Any help is greatly appreciated
Regards
This is a very long method that I just wrote. It only lists the most frequent address. You have to get these values and insert them into the table. See if it works for you:
select * from
(select d.company, count(d.address) as final, c.maxcount,d.address
from dbo.test d inner join
(select a.company,max(a.add_count) as maxcount from
(select company,address,count(address) as add_count from dbo.test group by company,address)a
group by a.company) c
on (d.company = c.company)
group by d.company,c.maxcount,d.address)e
where e.maxcount=e.final
Here is a query in standard SQL. It first counts records per company and address, then ranks them per company giving the most often occurring address rank #1. Then it only keeps those best ranked address records, joins with the table again and shows the results.
select
mytable.company,
mytable.address,
ranked.address as most_common_address
from mytable
join
(
select
company,
address,
row_number() over (partition by company oder by cnt desc) as rn
from
(
select
company,
address,
count(*) over (partition by company, address) as cnt
from mytable
) counted
) ranked on ranked.rn = 1
and ranked.company = mytable.company
and ranked.address = mytable.address;
This select statement will give you the most frequent occurrence. Let us call this A.
SELECT `value`,
COUNT(`value`) AS `value_occurrence`
FROM `my_table`
GROUP BY `value`
ORDER BY `value_occurrence` DESC
LIMIT 1;
To INSERT this into your table,
INSERT INTO db (col1, col2, col3) VALUES (val1, val2, A)
Note that you want that whole select statment for A!
You don't mention your DBMS. Here is a solution for Oracle.
select
company,
address,
(
select stats_mode(address)
from mytable this_company_only
where this_company_only.company = mytable.company
) as most_common_address
from mytable;
This looks a bit clumsy, because STATS_MODE is only available as an aggregate function, not as an analytic window function.

Lookup table with best match query

To lookup a country for a phone number prefix I running the following query:
SELECT country_id
FROM phonenumber_prefix
WHERE '<myphonnumber>' LIKE prefix ||'%'
ORDER BY LENGTH(calling_prefix) DESC
LIMIT 1
To query phone numbers from a table I run a query like:
SELECT phonenumber
FROM phonenumbers
Now I want to combine those query into one, to get countries for all phone numbers. I know that I could put the first query into a function e.g. getCountry() and then query
SELECT phonenumber, getCountry(phonenumber)
FROM phonenumbers
But is there also a way to to do this with joins in one query, I'm using postgresql 9.2?
You can do this with a correlated subquery:
SELECT phonenumber,
(SELECT country_id
FROM phonenumber_prefix pp
WHERE pn.phonenumber LIKE prefix ||'%'
ORDER BY LENGTH(calling_prefix) DESC
LIMIT 1
) as country_id
FROM phonenumbers pn;
This will give you list of numbers with corresponding country ids for the longest matching prefix:
SELECT * FROM (
SELECT
p.phonenumber, pc.country_id,
ROW_NUMBER() OVER (PARTITION BY phonenumber ORDER BY LENGTH(pc.prefix) DESC) rn
FROM
phonenumber_prefix pc INNER JOIN
phonenumbera p ON p.phonenumber LIKE pc.prefix || '%' ) t
WHERE t.rn = 1
You can do it using a join as so:
select phonenumber, countryId
from phonenumbers
inner join phonenumber_prefix on phonenumber like prefix||'%'
Be aware that this will be quite inefficient though as it won't be able to use indexes for the join.
You can also do it the other way around (by extracting the correct number of digits from the start of the phone number). That will improve things a bit as it will be able to use an index to look up the prefix, but it will still need to split each phonenumber.

Finding entry with maximum date range between two columns in SQL

Note: this is in Oracle not MySQL, limit/top won't work.
I want to return the name of the person that has stayed the longest in a hotel. The longest stay can be found by subtracting the date in the checkout column with the checkin column.
So far I have:
select fans.name
from fans
where fans.checkout-fans.checkin is not null
order by fans.checkout-fans.checkin desc;
but this only orders the length of stay of each person from highest to lowest. I want it to only return the name (or names, if they are tied) of people who have stayed the longest. Also, As more than one person could have stayed for the highest length of time, simply adding limit 1 to the end won't do.
Edit (for gbn), when adding a join to get checkin/checkout from other table it wont work (no records returned)
edit 2 solved now, the below join should of been players.team = teams.name
select
x.name
from
(
select
players.name,
dense_rank() over (order by teams.checkout-teams.checkin desc) as rnk
from
players
join teams
on players.name = teams.name
where
teams.checkout-teams.checkin is not null
) x
where
x.rnk = 1
Should be this using DENSE_RANK to get ties
select
x.name
from
(
select
fans.name,
dense_rank() over (order by fans.checkout-fans.checkin desc) as rnk
from
fans
where
fans.checkout-fans.checkin is not null
) x
where
x.rnk = 1;
SQL Server has TOP..WITH TIES for this, but this is a generic solution for any RDBMS that has DENSE_RANK.
Longest is a fuzzy word, you should first define what is long for you. Using limit may not be a solution for this case. So you can define your treshold and try to filter your results where fans.checkout-fans.checkin > 10 for instance.
Try this:
select name, (checkout-checkin) AS stay
from fans
where stay is not null -- remove fans that never stayed at a hotel
order by stay desc;
For Oracle:
select * from
(
select fans.name
from fans
where fans.checkout-fans.checkin is not null
order by fans.checkout-fans.checkin desc)
where rownum=1
Another way that should work in all dbms (or almost all, at least those that support subqueries):
select fans.name
from fans
where fans.checkout-fans.checkin =
( select max(f.checkout-f.checkin)
from fans f
) ;
If both the columns are date fields you can use this query:
select fans.name from fans where fans.checkout-fans.checkin in (select max(fans.checkout-fans.checkin) from fans );

Select all data from a table which for the three ids that appear the most in another table

I have two Oracle SQL Tables:
Team and Work.
I want to select all the columns from the team table (this is fine) for three teams that appeart the most in the work table.
Team (teamId, name)
Work(workId, name, teamId).
I really don't know how to select from count max.
my first idea was to group the work table by the count of teamId but I can't group in a subquery...
Hope everything is clear, if not let me know.
Should be something like this (not quite sure about syntax):
select * from team where teamid in (
select teamid from (
select teamid, count(workid)
from work
group by teamid
order by 2)
where rownum < 4);
select *
from team
where teamID in(
select top 3 teamId
from Work
group by teamId
order by COUNT(*) desc
)