How to list distinct column after a join and also count? - sql

I have three tables:
team(ID, name)
goal(ID, team_ID, goalType_ID, date)
goalType(ID, name)
As you can see, team_ID is the ID of teams table, and goalType_ID is the ID of goalType table.
For all teams, I want to list the number of different types of goals that ever happened, 0 should appear if none.
We don't need to care about the goals table since we don't need the name of the type of goal so I've gotten to the follow code that only uses the first two tables:
SELECT team.ID, team.name, goal.goaType_ID
FROM team LEFT JOIN goal ON team.ID=goal.team_ID
What this results in is a three-column table of information I want, but I would like to count the number of DISTINCT goalTypes, and GROUP BY team.ID or team.name and keep it three columns and also if the result is null, show 0 (team might not have scored any goals).
The resulting table looks something like this:
team.ID team.name goalsType.ID
1 Team_1 1
2 Team_2 2
2 Team_2 2
2 Team_2 2
3 Team_3 4
4 Team_4 null
5 Team_5 null
6 Team_6 1
6 Team_6 2
6 Team_6 4
6 Team_6 3
7 Team_7 5
7 Team_7 4
8 Team_8 null
I have tried a combination of GROUP BY, DISTINCT, and COUNT, but still can't get a result I want.
Maybe I'm going about this all wrong? Any help would be appreciated, Thanks.
EDIT:
Based on Gordon Linoff's answer, I tried doing:
SELECT DISTINCT team.name, COUNT(goal.goalType_ID)
FROM team LEFT JOIN goal ON team.ID=goal.team_ID
GROUP BY team.ID, team.name
and it will give me:
Name #0
Team_1 1
Team_2 3
Team_3 1
Team_4 0
Team_5 0
Team_6 4
Team_7 1
Team_8 0
If I try to use "DISTINCT team.ID, DISTINCT team.name", it will error out.

Is this what you want?
SELECT team.ID, team.name, count(distinct goal.goalType_ID) as NumGoalTypes
FROM team LEFT JOIN
goal
ON team.ID = goal.team_ID
GROUP BY team.ID, team.name;

Try this http://sqlfiddle.com/#!3/8ec680/13
;WITH cte
AS (SELECT Row_number() OVER(partition BY tname
ORDER BY goalid), * from temp)--temp= Your join statement
SELECT CASE
WHEN a.goalid IS NULL THEN 0
ELSE a.row_n
END [count],
a.tid,
a.tname,
a.goalid
FROM cte a
JOIN (SELECT Max(row_n) row_n,
tname
FROM cte
GROUP BY tname) b
ON a.row_n = b.row_n
AND a.tname = b.tname

Related

PostgreSQL group by on different columns

I have a table as the following:
branch_id
parent_branch_id
sales
1
10
2
1
5
3
8
4
3
3
5
1
1
6
6
I need to aggregate the table on branches of a father branch
branch_id
sales
1
16
3
11
6
6
What I have tried:
I tried first separating the branches which have a parent branch and aggregating them on their parent_branch_id, then joining this aggregated table with the table of the parent only branches on t1.parent_branch_id=t2.branch_id and summing the resulting columns to get total sales.
I feel this, having to join tables, is costly and maybe there is a smarter way to do it with builtin PostgreSQL functions.
You don't need a join for this:
select coalesce(parent_branch_id, branch_id) as branch_id,
sum(sales)
from t
group by coalesce(parent_branch_id, branch_id)
order by branch_id;
Here is a db<>fiddle.
Assuming that we only need to consider two levels, parents and immediate children, we can try a self join approach here:
SELECT COALESCE(t1.parent_branch_id, t1.branch_id) AS branch_id,
SUM(t1.sales) AS sales
FROM yourTable t1
LEFT JOIN yourTable t2
ON t2.branch_id = t1.parent_branch_id
GROUP BY
COALESCE(t1.parent_branch_id, t1.branch_id);

Getting sum of 2 columns each with unique condition

I've two tables like this.
create table teams (
"ID" Integer NOT NULL ,
"STADIUM_ID" Integer NOT NULL ,
"NAME" Varchar2 (50) NOT NULL ,
primary key ("ID")
) ;
create table matches (
"ID" Integer NOT NULL ,
"WINNER_ID" Integer NOT NULL ,
"OPPONENT_ID" Integer NOT NULL ,
"WINNERSCORE" Integer,
"OPPONENTSCORE" Integer,
primary key ("ID","WINNER_ID","OPPONENT_ID")
) ;
They have the following data :
select * from matches;
ID WINNER_ID OPPONENT_ID WINNERSCORE OPPONENTSCORE
---------- ---------- ----------- ----------- -------------
1 5 2 5 2
2 4 5 1 0
3 3 2 1 0
4 3 2 1 0
5 1 2 2 0
6 3 1 2 1
select * from teams;
ID STADIUM_ID NAME
---------- ---------- -----------
1 1 Team1
2 3 Team2
3 4 Team3
4 2 Team4
5 5 Team5
I need to get the sum of the goals for each team.
For this aim, tried the following query and got the results below :
select name,
(select sum(opponentscore)
from matches
where opponent_id = teams.id) +
(select sum(winnerscore) from matches where winner_id = teams.id) sum
from teams;
NAME SUM
-------------------------------------------------- ----------
Team1 3
Team2
Team3
Team4
Team5 5
Do you have any suggestion ?
All you need is to calculate seperately opponentscore and winnerscore by each individual team, and combine them with UNION ALL :
select name, sum(score) total_score
from
(
select name, sum(winnerscore) score
from teams t join matches m on ( t.id = m.winner_id )
group by name
union all
select name, sum(opponentscore) score
from teams t join matches m on ( t.id = m.opponent_id )
group by name
)
group by name
order by 1;
SQL Fiddle Demo
you should use join and group by
select name, sum(matches.opponentscore) + sum(matches.winnerscore) my_sum
from matches
inner join teams on teams.id = matches.winner_id
group by teams.name
You could join table teams twice with table matches:
SELECT name, SUM(wonMatches.WINNERSCORE + lostMatches.OPPONENTSCORE) as goals
FROM (teams INNER JOIN matches as wonMatches ON teams.ID = wonMatches.WINNER_ID)
INNER JOIN matches as lostMatches ON teams.ID = lostMatches.OPPONENT_ID
GROUP BY name
My solution is : change your database schema. Restart thinking your app's requirements. This schema does not answer the value your user are expecting from you.
From what I see, I would say that you're trying to build an app for fans that want to track their team / favorite player progress so they can brag.
That being said, I would have, at the end, those tables :
fan
team (id_team)
player (id_player, id_team)
tournament (id_tournament)
match (id_match, id_tournament, start_on, id_team_home, id_team_visitor)
goals (id_match, id_player, goaled_on)
So now, I believe that writing your query would be much more simple. You'll just have to join team, player, count over goals and group by team.
The problem is with NULLs - the subqueries return NULL when no result is found, and NULL + anything == NULL.
Most straightforward fix is:
select name,
nvl(
(select sum(opponentscore) from matches where opponent_id = teams.id),
0
)
+
nvl(
(select sum(winnerscore) from matches where winner_id = teams.id),
0
) sum
from teams;
For performance reasons tohugh, you might want to consider using a joined query with GROUP BY as suggested by others.

How to get a correlated subquery as column

I dont know how I can do this sql query, probably its simple but I don't know how i can do it.
I have 2 tables:
Table_Articles:
COD NAME
1 Bottle
2 Car
3 Phone
Table_Articles_Registered
COD_ARTICLE DATE
1 05/11/2014
1 06/11/2014
1 07/11/2014
2 08/11/2014
2 09/11/2014
3 05/11/2014
I want take in the table Table_Articles_Registered the row with the MAX date , finally I want get this result:
COD NAME DATE
1 Bottle 07/11/2014
2 Car 09/11/2014
3 Phone 05/11/2014
I need use the sencente like this. The problem its in the subquery. Later I use other inner join in the sentence, this is only a fragment.
select
_Article.Code,
_Article.Description ,
from Tbl_Articles as _Article left join
(
select top 1 *
from ArticlesRegisterds where DATE_REGISTERED <= '18/11/2014'
order by DATE_REGISTERED
)
as regAux
on regAux.CODE_ARTICLE= _Article.CODE
I dont know how can I connect the field CODE_ARTICLE in the table ArticlesRegisterds with the first query.
I think this is a basic aggregation query with a join:
select a.cod, a.name, max(ar.date) as date
from Artiles a join
ArticlesRegisterds ar
on ar.cod_article = a.cod
group by a.cod, a.name
Try this:-
SELECT TAR.COD_ARTICLE, TA.NAME, MAX(TAR.DATE)
FROM Table_Articles_Registered TAR JOIN
Table_Articles.TA ON TAR.COD_ARTICLE = TA.COD
GROUP BY TAR.COD_ARTICLE, TA.NAME;
Can't you just do this?:
SELECT
Table_Articles.COD,
Table_Articles.NAME,
(
SELECT MAX(Table_Articles_Registered.DATE)
FROM Table_Articles_Registered
WHERE Table_Articles.COD_ARTICLE=Table_Articles.COD
) AS DATE
FROM
Table_Articles

MySql Join with Sum

I have a table called RESULTS with this structure :
resultid,winner,type
And a table called TICKETS with this structure :
resultid,ticketid,bet,sum_won,status
And I want to show each row from table RESULTS and for each result I want to calculate the totalBet and Sum_won using the values from table TICKETS
I tried to make some joins,some sums,but I cant get what I want.
SELECT *,COALESCE(SUM(tickets.bet),0) AS totalbets,
COALESCE(SUM(tickets.sum_won),0) AS totalwins
FROM `results` NATURAL JOIN `tickets`
WHERE tickets.status<>0
GROUP BY resultid
Please give me some advice.
I want to display something like this
RESULT WINNER TOTALBETS TOTALWINS
1 2 431 222
2 3 0 0
3 1 23 0
4 1 324 111
Use:
SELECT r.*,
COALESCE(x.totalbet, 0) AS totalbet,
COALESCE(x.totalwins, 0) AS totalwins
FROM RESULTS r
LEFT JOIN (SELECT t.resultid,
SUM(t.bet) AS totalbet,
SUM(t.sum_won) AS totalwins
FROM TICKETS t
WHERE t.status != 0
GROUP BY t.resultid) x ON x.resultid = r.resultid
I don't care for the NATURAL JOIN syntax, preferring to be explicit about how to JOIN/link tables together.
SELECT *, COALESCE(SUM(tickets.bet),0) AS totalbets,
COALESCE(SUM(tickets.sum_won),0) AS totalwins
FROM `results` NATURAL JOIN `tickets`
WHERE tickets.status<>0
GROUP BY resultid
Try to replace the first * with resultid. If this helps, then add more columns to SELECT and add them to GROUP BY at the same time.

return count 0 with mysql group by

database table like this
============================
= suburb_id | value
= 1 | 2
= 1 | 3
= 2 | 4
= 3 | 5
query is
SELECT COUNT(suburb_id) AS total, suburb_id
FROM suburbs
where suburb_id IN (1,2,3,4)
GROUP BY suburb_id
however, while I run this query, it doesn't give COUNT(suburb_id) = 0 when suburb_id = 0
because in suburbs table, there is no suburb_id 4, I want this query to return 0 for suburb_id = 4, like
============================
= total | suburb_id
= 2 | 1
= 1 | 2
= 1 | 3
= 0 | 4
A GROUP BY needs rows to work with, so if you have no rows for a certain category, you are not going to get the count. Think of the where clause as limiting down the source rows before they are grouped together. The where clause is not providing a list of categories to group by.
What you could do is write a query to select the categories (suburbs) then do the count in a subquery. (I'm not sure what MySQL's support for this is like)
Something like:
SELECT
s.suburb_id,
(select count(*) from suburb_data d where d.suburb_id = s.suburb_id) as total
FROM
suburb_table s
WHERE
s.suburb_id in (1,2,3,4)
(MSSQL, apologies)
This:
SELECT id, COUNT(suburb_id)
FROM (
SELECT 1 AS id
UNION ALL
SELECT 2 AS id
UNION ALL
SELECT 3 AS id
UNION ALL
SELECT 4 AS id
) ids
LEFT JOIN
suburbs s
ON s.suburb_id = ids.id
GROUP BY
id
or this:
SELECT id,
(
SELECT COUNT(*)
FROM suburb
WHERE suburb_id = id
)
FROM (
SELECT 1 AS id
UNION ALL
SELECT 2 AS id
UNION ALL
SELECT 3 AS id
UNION ALL
SELECT 4 AS id
) ids
This article compares performance of the two approaches:
Aggregates: subqueries vs. GROUP BY
, though it does not matter much in your case, as you are querying only 4 records.
Query:
select case
when total is null then 0
else total
end as total_with_zeroes,
suburb_id
from (SELECT COUNT(suburb_id) AS total, suburb_id
FROM suburbs
where suburb_id IN (1,2,3,4)
GROUP BY suburb_id) as dt
#geofftnz's solution works great if all conditions are simple like in this case. But I just had to solve a similar problem to generate a report where each column in the report is a different query. When you need to combine results from several select statements, then something like this might work.
You may have to programmatically create this query. Using left joins allows the query to return rows even if there are no matches to suburb_id with a given id. If your db supports it (which most do), you can use IFNULL to replace null with 0:
select IFNULL(a.count,0), IFNULL(b.count,0), IFNULL(c.count,0), IFNULL(d.count,0)
from (select count(suburb_id) as count from suburbs where id=1 group by suburb_id) a,
left join (select count(suburb_id) as count from suburbs where id=2 group by suburb_id) b on a.suburb_id=b.suburb_id
left join (select count(suburb_id) as count from suburbs where id=3 group by suburb_id) c on a.suburb_id=c.suburb_id
left join (select count(suburb_id) as count from suburbs where id=4 group by suburb_id) d on a.suburb_id=d.suburb_id;
The nice thing about this is that (if needed) each "left join" can use slightly different (possibly fairly complex) query.
Disclaimer: for large data sets, this type of query might have not perform very well (I don't write enough sql to know without investigating further), but at least it should give useful results ;-)