SQL Max Value for a Specified Limit - sql

I'm trying to return a list of years when certain conditions are met but I am having trouble with the MAX function and having it work with the rest of my logic.
For the following two tables:
coach
coach | team | wins | year
------+------+------+------
nk01 | a | 4 | 2000
vx92 | b | 1 | 2000
nk01 | b | 5 | 2003
vx92 | a | 2 | 2003
team
team | worldcupwin | year
-----+-------------+------
a | Y | 2000
b | N | 2000
a | Y | 2003
b | N | 2003
I want to get the following output:
years
-----
2000
Where the years printed are where the coaches' team with most wins during that year also won the world cup.
I decided to use the MAX function but quickly ran into the problem of not knowing how to use it to only be looking for max values for a certain year. This is what I've got so far:
SELECT y.year
FROM (SELECT c.year, MAX(c.wins), c.team
FROM coach AS c
WHERE c.year >= 1999
GROUP BY c.year, c.team) AS y, teams AS t
WHERE y.year = t.year AND t.worldcupwin = 'Y' AND y.team = t.team;
This query outputs all years greater than 1999 for me, rather than just those where a coach with the most wins also won the world cup.
(Using postgresql)
Any help is appreciated!

You can use correlated subquery
DEMO
SELECT c.year, c.team
FROM coachs AS c inner join teams t on c.team = t.team and c.year=t.year
WHERE c.year >= 1999 and exists (select 1 from coachs c1 where c.team=c1.team
having max(c1.wins)=c.wins)
and t.worldcupwin = 'Y'
OUTPUT:
year team
2000 a

The following query uses DISTINCT ON:
SELECT DISTINCT ON (year) c.year, wins, worldcupwin, c.team
FROM coach AS c
INNER JOIN team AS t ON c.team = t.team AND c.year = t.year
WHERE c.year > 1999
ORDER BY year, wins DESC
in order to return the records having the biggest number of wins per year
year wins worldcupwin team
---------------------------------
2000 4 Y a
2003 5 N b
Filtering out teams that didn't win the world cup:
SELECT year, team
FROM (
SELECT DISTINCT ON (year) c.year, wins, worldcupwin, c.team
FROM coach AS c
INNER JOIN team AS t ON c.team = t.team AND c.year = t.year
WHERE c.year > 1999
ORDER BY year, wins DESC) AS t
WHERE t.worldcupwin = 'Y'
ORDER BY year, wins DESC
gives the expected result:
year team
-------------
2000 a
Demo here

You can use the below to get the desired result:
EASY METHOD
SELECT TOP 1 c.year
FROM coach AS c INNER JOIN team AS t ON c.team = t.team AND c.year = t.year
WHERE t.worldcupwin = 'Y'
ORDER BY c.wins DESC;

use row_number() window function
select a.coach,a.team,a.win,a.year from
(select c.*,t.*,
row_number()over(order by wins desc) rn
from coach c join team t on c.team=t.team
where worldcupwin='Y'
) a where a.rn=1

Related

SQL query for until 2005 but not after 2005?

Find all Id who had taught until 2005 but had not taught after 2005.
for eg.
year ID
2010 A
2009 C
2005 B
2002 D
2002 C
2001 B
2000 A
Then the result should give only B and D.
The table has columns ID and year and I want to print out ID.
SELECT ID
FROM university.teaches
WHERE year <= 2005 AND
year NOT IN (SELECT year FROM university.teaches WHERE year> 2005);
I am trying something like this but it gives result including A and C
You should check for ids and not years with the IN operator:
SELECT DISTINCT ID
FROM university.teaches
WHERE id NOT IN (SELECT id FROM university.teaches WHERE year > 2005);
The subquery of IN returns all the ids that have taught after 2005, so NOT IN will return all the rest ids.
See the demo.
Results:
| ID |
| --- |
| B |
| D |
Use GROUP BY and MAX():
select id
from university.teaches
group by id
having max(year) <= 2005;

how to remove duplicate results

given the following schema:
CREATE TABLE IF NOT EXISTS companies (
id serial,
name text NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS cars (
id serial,
make text NOT NULL,
year integer NOT NULL,
company_id INTEGER REFERENCES companies(id),
PRIMARY KEY (id)
);
INSERT INTO companies (id, name) VALUES
(1, 'toyota'),
(2, 'chevy');
INSERT INTO cars (make, year, company_id) VALUES
('silverado', 1995, 2),
('malibu', 1999, 2),
('tacoma', 2017, 1),
('custom truck', 2010, null),
('van custom', 2005, null);
how do i select the rows for cars, only showing the newest car for a given company?
e.g.
select make, companies.name as model, year from cars
left join companies
on companies.id = cars.company_id
order by make;
outputs
make | model | year
--------------+--------+------
custom truck | | 2010
malibu | chevy | 1999
silverado | chevy | 1995
tacoma | toyota | 2017
van custom | | 2005
but i only want to show the newest "chevy", e.g.
make | model | year
--------------+--------+------
custom truck | | 2010
malibu | chevy | 1999
tacoma | toyota | 2017
van custom | | 2005
and still be able to sort by "make", and to show cars without a null company_id.
fiddle link:
https://www.db-fiddle.com/f/5Vh1sFXvEvnbnUJsCYhCHf/0
SQL can be done based on Set Math (discrete math). So, you want the set of all cars minus the set of cars whose years a less than the maximum year for a given company id.
The set of all cars:
select * from cars
The set of all cars whose year is less than the maximum year for a given company id:
select a.id from cars a, cars b where a.company_id = b.company_id and a.year < b.year
One set minus the other:
select * from cars where id not in (select a.id from cars a, cars b where a.company_id = b.company_id and a.year < b.year)
Result which includes the null company_ids because they are excluded from the id comparison:
make | model | year
--------------+--------+------
custom truck | | 2010
malibu | chevy | 1999
tacoma | toyota | 2017
van custom | | 2005
With the help of common table expressions and row_number function, we can get the desired output and below is the query that gives the desired output.
WITH temp AS
(SELECT
make
, companies.name AS model
, year
, row_number() over(PARTITION BY coalesce(companies.name, make) ORDER BY year desc) as rnk
FROM
cars
left join
companies
ON
companies.id = cars.company_id
)
SELECT
make
, model
, year
FROM
temp
WHERE
rnk = 1
;
In Postgres, this is best done using distinct on:
select distinct on (co.id) ca.*, co.name as model
from cars ca left join
companies co
on ca.company_id = co.id
order by co.id, ca.year desc;
DISTINCT ON is very handy Postgres syntax. It keeps one row for each combination in parentheses. The specific row is determined by the ORDER BY clause.
However, you have a twist, because co.id can be null. In that case, you seem to want to keep all the cars with no company.
So:
select distinct on (co.id, case when co.id is null then ca.id end) ca.*, co.name
from cars ca left join
companies co
on ca.company_id = co.id
order by co.id, case when co.id is null then ca.id end, ca.year desc;
Or perhaps more simply using union all:
-- get the ones with a company
select distinct on (co.id) ca.*, co.name
from cars ca join
companies co
on ca.company_id = co.id
union all
-- get the ones with no company
select ca.*, null
from cars ca
where ca.company_id is null
order by year desc;
In other databases, this would typically be done using row_number():
select ca.*
from (select ca.*, co.name as model,
row_number() over (partition by co.id,
case when co.id is null then ca.id end
order by year desc
) as seqnum
from cars ca left join
companies co
on ca.company_id = co.id
) ca
where seqnum = 1

SQL count categorized by different amounts

As the title suggests. I'm trying to write a query that will give me a count of all people who haven't attended something, however I then need to group them by how many times they haven't attended. Sort of like this.
|---------------------|-----------------------------------|
| No. Of People | No. of times not attended |
|---------------------|-----------------------------------|
| 12 | 1 |
|---------------------|-----------------------------------|
| 34 | 2 |
|---------------------|-----------------------------------|
In this sort of format, with the 1 meaning 'didnt attend once' and the 2 meaning 'didn't attend twice' etc etc.
This is what I have for now..
SELECT COUNT(p.PersonID)AS 'No. of People'
,COUNT(e.attended) AS 'Attended'
,et.EpisodeTypeName
FROM Person p
JOIN Episode e ON e.PersonID = p.PersonID
JOIN EpisodeType et ON et.EpisodeTypeID = e.EpisodeTypeID
WHERE e.Attended = 'No'
AND e.EpisodeDate >= '2015-04-01' AND e.EpisodeDate <= '2016-03-31'
GROUP BY e.Attended, et.EpisodeTypeName;
Any help with this would be great!
If I understand correctly, this is a histogram-of-histograms query. Also, I don't see that EpisodeType is needed, at least based on the results and query in the question.
So, a query with two levels of aggregation:
SELECT NotAttended, COUNT(*) as NumPeople
FROM (SELECT p.PersonID, COUNT(*) as NotAttended
FROM Person p JOIN
Episode e
ON e.PersonID = p.PersonID
WHERE e.Attended = 'No' AND
e.EpisodeDate >= '2015-04-01' AND e.EpisodeDate <= '2016-03-31'
GROUP BY p.PersonID
) p
GROUP BY NotAttended;

SQL query required [duplicate]

This question already has answers here:
Single SQL query required [closed]
(3 answers)
Closed 9 years ago.
I have two tables as follows
companyid name
1 pwc
2 dell
3 microsoft
4 google
5 yahoo
6 twitter
companyid state month powerconsumption
1 newyork jan 240
2 california jan 130
3 arizona jan 210
4 texas jan 130
5 texas jan 650
6 california jan 310
2 arizona jan 340
I want to have a query to list the company in each state which consumed maximum power in the month of jan.So the result in case of above data will be
arizona dell 340
california twitter 310
newyork pwc 240
texas yahoo 650
SELECT DISTINCT (SELECT name FROM company WHERE (companyid = (SELECT TOP (1) companyid FROM consumption WHERE (state = a.state) AND (month = a.month) ORDER BY powerconsumption DESC))) AS company,
state,
month,
(SELECT TOP (1) powerconsumption FROM consumption AS consumption_1 WHERE (state = a.state) AND (month = a.month) ORDER BY powerconsumption DESC) AS powerconsumption
FROM dbo.consumption AS a
Try this query:
SELECT x.State,
c.Name AS CompanyName,
x.PowerConsumption
FROM
(
SELECT pc.CompanyID, pc.State, pc.PowerConsumption,
DENSE_RANK() OVER(PARTITION BY pc.State ORDER BY pc.PowerConsumption DESC) AS Rnk
FROM dbo.PowerConsumptionTable pc
WHERE pc.Month = 'jan'
) x INNER JOIN dbo.CompanyTable c ON x.CompanyID = c.CompanyID
WHERE x.Rnk = 1
I got an answer and that seems the best way for me. It is having a sub query,but i don't see a way to avoid that.
select t2.state,t1.name,t2.powerconsumption
FROM table1 t1
JOIN table2 t2
ON t1.companyid =t2.companyid
where t2.powerconsumption =(select MAX(t3.powerconsumption) from table2 t3 where t3.state=t2.state and t3.month='jan')
SQL Fiddle

How to rank users and get a subset from this rank with my user and the above and below user by rank position

I am working on a query right now to get a ranking of my users. I have two tables one for users and the other one for profits where I save the amount and the user id to which is related. By getting the total of profits generated by a user I need to build a rank with three users, the user in the next higher ranked position to my user, my user and the user in the next lower ranked position to my user. For example:
id | name | total_profit | rank
-------+-----------------------------+--------------+------
10312 | John Doe | 7000.0 | 1
10329 | Michael Jordan | 5000.0 | 2
10333 | Kobe Bryant | 4000.0 | 3
10327 | Mike Bibby | 4000.0 | 3
10331 | Phil Jackson | 1000.0 | 4
In this if my user is Kobe Bryant I would need to get a rank with Michael Jordan, Kobe Bryant and Phil Jackson.
If my user is Mike Bibby I would need to get a rank with Michale Jordan, Mike Bybby and Phil Jackson.
Until now I have a query that returns me a full rank with all the users but I do not now what is a good way to get the three users that I want. I have tried to do this with ruby but I think it would be better if I do all this processing in the DB.
SELECT users.id, users.name, total_profit, rank() OVER(ORDER BY total_profit DESC)
FROM users
INNER JOIN (SELECT sum(profits.amount) AS total_profit, investor_id
FROM profits GROUP BY profits.investor_id) profits ON profits.investor_id = users.id
ORDER BY total_profit DESC;
I am using PostgresSQL 9.1.4
with s as (
select
users.id, users.name, total_profit,
rank() over(order by total_profit desc) as r
from
users
inner join
(
select sum(profits.amount) as total_profit,
investor_id
from profits
group by profits.investor_id
) profits on profits.investor_id = users.id
), u as (
select r from s where name = 'Kobe Bryant'
)
select distinct on (r) id, name, total_profit, r
from s
where
name = 'Kobe Bryant'
or r in (
(select r from u) - 1, (select r from u) + 1
)
order by r;
with cte_profits as (
select
sum(p.amount) as total_profit, p.investor_id
from profits as p
group by p.investor_id
), cte_users_profits as (
select
u.id, u.name, p.toral_profit,
dense_rank() over(order by p.total_profit desc) as rnk,
row_number() over(partition by up.total_profit order by up.id) as rn
from users as u
inner join cte_profits as p on p.investor_id = u.id
)
select c2.*
from cte as c
left outer join cte as c2 on
c2.id = c.id or
c2.rnk = c.rnk + 1 and c2.rn = 1 or
c2.rnk = c.rnk - 1 and c2.rn = 1
where c.name = 'Kobe Bryant'
order by c2.rnk
sql fiddle demo