Displaying values that occur consecutively - mysql

I'm trying to display a list of all Directors who have directed 2 years consecutively.
Given the following data:
Pantomime table:
Year titleID DirectorID
2000 1 1
2001 2 7
2002 3 7
2003 4 8
2004 5 9
2005 6 9
This is the desired outcome:
DirectorID
7
9
This is the query I have tried so far but was unable to get the desired results.
SELECT directorID
FROM pantomime
where directorID = directorID+1
GROUP BY directorID

One method uses exists:
select distinct p.directorId
from pantomine p
where exists (select 1
from pantomine p2
where p2.directorId = p.directorId and p2.year = p.year + 1
);
There are other fun variants on this idea, such as using in:
select distinct p.directorId
from pantomine p
where p.year in (select p2.year + 1
from pantomine p2
where p2.directorId = p.directorId
);
And here is a totally arcane method that doesn't use join-like mechanisms at all (just aggregation):
select distinct directorId
from ((select directorId, year from pantomine)
union all
(select directorId, year + 1 from pantomine)
) p
group by directorId, year
having count(*) = 2;
This is also one of those really, really rare cases of using select distinct with group by.

you can use join to see which entries has the next year's value, and then with distinct get the relevant id's:
select distinct a.directorID
from Pantomime as a
inner join Pantomime as b on a.year = b.year-1
and a.directorID = b.directorID;
since I'm using inner join, we'll get records from a only if they exist in b- meaning if year-1 appears in your table for this directorId

Try this, no joins or sub-queries, just a simple grouping:
SELECT directorID
FROM pantomime
GROUP BY directorID
HAVING COUNT(*) = 2
AND MAX(Year) = MIN(Year) + 1
Here is a fiddle.

Related

Correct join syntax within multiple queries and sub queries

I have two queries that end up having the same format. Each has a Month, a year, and some relevant data per month/year. The schema looks like this:
subs Month Year
8150 1 2015
11060 1 2016
5 2 2014
6962 2 2015
8736 2 2016
Cans months years
2984 1 2015
2724 1 2016
13 2 2014
2563 2 2015
1901 2 2016
The first query syntax looks like this:
SELECT
COUNT(personID) AS subs_per_month,
MONTH(Date_1) AS month_1,
YEAR(Date_1) AS year_1
FROM
(SELECT
personID, MIN(date) AS Date_1
FROM
orders
WHERE
isSubscription = 1
GROUP BY personID
ORDER BY Date_1) AS my_sub_q
GROUP BY month_1 , year_1
The second query:
SELECT
COUNT(ID), MONTH(date) AS months, YEAR(date) AS years
FROM
orders
WHERE
status = 4 AND isSubscription = 1
GROUP BY months , years
ORDER BY months, years
The end goal is to write a simple join so that the final dataset looks like this:
subs cans months years
8150 2984 1 2015
11060 2724 1 2016
5 13 2 2014
6962 2563 2 2015
8736 1901 2 2016
I'm a little overwhelmed with how to do this correctly, and after a lot of trial and all error, I thought I'd ask for help. What's confusing is where the JOIN goes, and how that looks relative to the rest of the syntax.
Without giving consideration to simplifying your queries you can use your two queries as inline views and simply select from both (I aliased Q1 and Q2 for your queries and named fields the same within each for simplicity.
Select Q1.cnt as Subs, Q2.cnt as Cans, Q1.months, Q1.years
from (SELECT
COUNT(personID) AS Cnt,
MONTH(Date_1) as Months,
YEAR(Date_1) AS years
FROM (SELECT personID, MIN(date) AS Date_1
FROM orders
WHERE isSubscription = 1
GROUP BY personID) AS my_sub_q
GROUP BY month_1 , year_1) Q1
INNER JOIN (SELECT COUNT(ID) cnt, MONTH(date) AS months, YEAR(date) AS years
FROM orders
WHERE status = 4
AND isSubscription = 1
GROUP BY months, years) Q2
ON Q1.Months = Q2.Months
and Q1.Years = Q2.years
Order by Q1.years, Q2.months
Temporary table approach:
create temporary table first_query
<<your first query here>>;
create temporary table second_query
<<your second query here>>;
select fq.subs, sq.cans, fq.months, fq.years
from first_query fq
join second_query sq using (months, years)
Your table preview and query columns do not match for first query, so I assumed both tables have columns - months and years.
One messy query approach:
SELECT fq.subs_per_month subs, sq.cans, sq.months, sq.years
FROM
(SELECT
COUNT(personID) AS subs_per_month,
MONTH(Date_1) AS month_1,
YEAR(Date_1) AS year_1
FROM
(SELECT
personID, MIN(date) AS Date_1
FROM
orders
WHERE
isSubscription = 1
GROUP BY personID
ORDER BY Date_1) AS my_sub_q
GROUP BY month_1 , year_1) fq
JOIN
(SELECT
COUNT(ID) cans, MONTH(date) AS months, YEAR(date) AS years -- I added 'cans'
FROM
orders
WHERE
status = 4 AND isSubscription = 1
GROUP BY months , years
ORDER BY months, years) sq
ON fq.month_1 = sq.months AND fq.year_1 = sq.years
Please use following query
select t1.subs as subs,t2.Cans as cans,t1.months,t1.year as years from table1 t1 inner join
table2 t2 on t1.month=t2.months and t1.year=t2.years

SQL Query to Count who has the highest number of match victories

I'm creating a simple database which will allow me to track snooker results, producing head to head results between players. Currently I have 3 tables: (Player, Fixture, Result)
PlayerID PlayerName
1 Michael Abraham
2 Ben Mullen
3 Mark Crozier
FixtureID Date TableNo Group
1 07/12/2015 19:00:00 12 0
2 08/12/2015 12:00:00 9 0
ResultID FixtureID PlayerID FramesWon
1 1 1 3
2 1 3 1
3 2 1 2
4 2 3 5
As you can see in the Result table, Player1 has played Player3 two times, with Player1 winning the first match 3-1, and Player3 winning the second match 5-2. I would like a query which returns the total number of matches won between the two players. In this case the expected output should be:
PlayerID MatchesWon
1 1
3 1
Any help would be appreciated - I'm not even sure if this can be achieved via a query
I agree using windowing function would be best way to go if available (SQL Server for example)
Might be possible with a straight SQL method this way (given that the one having most wins in a "fixture" is the match winner)
SELECT PlayerId, FixtureID, Count(*) As MatchesWon
FROM Result r
WHERE r.Frameswon = (SELECT MAX(frameswon) FROM Result r2
WHERE
r.FixtureId = r2.FixtureId)
GROUP BY PlayerID,FixtureId
OR if can leave out the fixtureId, and filter for just the 2 players something like this one as well. with data given above should bring the sample results.
SELECT PlayerId, MatchesWon
FROM
(
SELECT FixtureID,PlayerId, Count(*) As MatchesWon
FROM Result r
WHERE r.Frameswon = (SELECT max(frameswon) FROM Result r2
WHERE
r.FixtureId = r2.FixtureId)
GROUP BY FixtureId,PlayerID
) s
WHERE
PlayerID IN (1,3)
Perhaps this would work for you:
select playerid, count(*) as matcheswon
from result as r1
where frameswon =
(
select max(frameswon)
from result as r2
where r2.fixtureid = r1.fixtureid
)
group by playerid
In a fiddle here: http://sqlfiddle.com/#!9/60821/2
This is the alternative you can try.
SELECT r.PlayerID, COUNT(r.PlayerID)
FROM (
SELECT FixtureID, MAX(FramesWon) AS FramesWon
FROM `result`
GROUP BY FixtureID
) win
INNER JOIN result r ON win.FixtureID = r.FixtureID AND win.FramesWon = r.FramesWon
GROUP By r.PlayerID

Get product total sales per moth, with 0 in the gaps

I have been stuck in a recent problem with a SQL Query. What I'm trying to archieve is to get each product in the store and show how many of them has been sold each month. However, sometimes there are some months where these products were not sold, which means they won't be displayed.
For instance, this is the result I'm getting right now
Article Month Sold
CN140027 6 312
CN140027 7 293
CN140027 12 122
CN140186 1 10
CN140186 4 2
While I want to get something more like this
Article Month Sold
CN140027 6 312
CN140027 7 293
CN140027 8 0
CN140027 9 0
CN140027 10 0
CN140027 11 0
CN140027 12 122
CN140186 1 10
CN140186 2 0
CN140186 3 0
CN140186 4 2
And here is the query I'm using at the moment
SELECT k.artikelnr, Months.datefield as `Months`, IFNULL(SUM(k.menge),0) as `Quantity`
FROM store_shop_korb as k LEFT OUTER JOIN office_calendar AS Months
ON Months.datefield = month(k.date_insert)
WHERE k.date_insert BETWEEN "2014-12-01" AND "2015-12-31"
group by k.artikelnr, Months.datefield
What am I missing? Or what am I doing wrong? Any help is really appreciated.
Thanks in advance.
EDIT:
Additional information:
office_calendar is the calendar table. It only contains the months as registry, from 1 to 12.
Additionally, I'm taking the article/product ID from a table called 'store_shop_korb', which contains all the lines of a made order (so it contains the article ID, its price, the quantity for each order..)
This works for me:
SELECT k.artikelnr, c.datefield AS `Month`, COALESCE(s.Quantity, 0) AS Sold
FROM (
SELECT artikelnr
FROM store_shop_korb
GROUP BY artikelnr
) k
JOIN office_calendar c
LEFT JOIN (
SELECT artikelnr, MONTH(date_insert) AS monthfield, SUM(menge) AS Quantity
FROM store_shop_korb
GROUP BY artikelnr, MONTH(date_insert)
) s ON k.artikelnr = s.artikelnr AND c.datefield = s.monthfield
ORDER BY k.artikelnr, c.datefield
If you have a table of articles, you can use it in the place of subquery k. I'm basically normalizing on the fly.
Explanation:
There's basically 3 sets of data that get joined. The first is a distinct set of articles (k), the second is a distinct set of months (c). These two are joined without restriction, meaning you get the cartesian product (every article x every month). This result is then left-joined to the sales per month (s) so that we don't lose 0 entries.
Add another where condition , i think it will solve your problem
SELECT k.artikelnr, Months.datefield as `Months`, IFNULL(SUM(k.menge),0) as `Quantity`
FROM store_shop_korb as k LEFT OUTER JOIN office_calendar AS Months
ON Months.datefield = month(k.date_insert)
WHERE IFNULL(SUM(k.menge),0)>0 AND k.date_insert BETWEEN "2014-12-01" AND "2015-12-31"
group by k.artikelnr, Months.datefield
I have tried this in MSAccess and it seems to work OK
SELECT PRODUCT, CALENDAR.MONTH, A
FROM CALENDAR LEFT JOIN (
SELECT PRODUCT, MONTH(SALEDTE) AS M, SUM(SALEAMOUNT) AS A
FROM SALES
WHERE SALEDTE BETWEEN #1/1/2015# AND #12/31/2015#
GROUP BY PRODUCT, MONTH(SALEDTE) ) AS X
ON X.M = CALENDAR.MONTH
If you already have a calender table then use this.
SELECT B.Article,
A.Month,
COALESCE(c.Sold, 0)
FROM (SELECT DISTINCT Months.datefield --Considering this as months feild
FROM office_calendar AS Months) A
CROSS JOIN (SELECT DISTINCT article
FROM Yourtable) B
LEFT OUTER JOIN Yourtable C
ON a.month = c.Month
AND b.Article = c.Article
Else you need a months table. Try this.
SELECT *
FROM (SELECT 1 AS month UNION
SELECT 2 UNION
SELECT 3 UNION
SELECT 4 UNION
SELECT 5 UNION
SELECT 6 UNION
SELECT 7 UNION
SELECT 8 UNION
SELECT 9 UNION
SELECT 10 UNION
SELECT 11 UNION
SELECT 12) A
CROSS JOIN (SELECT DISTINCT article
FROM Yourtable) B
LEFT OUTER JOIN Yourtable C
ON a.month = c.Month
AND b.Article = c.Article

SQL Incorrect SUMS from multiple JOINS

I'm trying to sum multiple tables using Joins and Sums in MySQL and not having much success.
My Tables (Unnecessary Columns Removed)
Students
idStudent studentname studentyear
1 foobar 11
2 barfoo 11
3 thing 8
Athletics_Results
idResult idStudent points
1 1 14
2 1 11
3 3 7
4 2 9
Team_Results
idTeamResults year points
1 11 9
2 8 8
3 7 14
So let me explain about the tables, because I admit they're poorly named and designed.
Students holds the basic info about each student, including their year and name. Each student has a unique ID.
Athletics_Results stores the results from athletics events. The idStudent column is a foreign key and relates to idStudent in the student column. So student foobar (idStudent 1) has scored 14 and 11 points in the example.
Team_Results stores results from events that more than one student took part in. It just stores the year group and points.
The Aim
I want to be able to produce a sum of points for each year - combined from both athletics_results and team_results. EG:
year points
7 14 <-- No results in a_r, just 14 points in t_r
8 15 <-- 7 points in a_r (idResult 4) and 8 in t_r
11 43 <-- 14, 11, 9 points in a_r and 9 in t_r
What I've tried
For testing purposes, I've not tried combining the a_r scores and t_r scores yet but left them as two columns so I can see what's going on.
The first query I tried:
SELECT students.studentyear as syear, SUM(athletics_results.points) as score, SUM(team_results.points) as team_score
FROM students
JOIN team_results ON students.studentyear = team_results.year
JOIN athletics_results ON students.idStudent = athletics_results.idStudent
GROUP BY syear;
This gave different rows for each year (as desired) but had incorrect SUMS. I learnt this was due to not grouping the joins.
I then created this code:
SELECT studentyear as sYear, teamPoints, AthleticsPoints
FROM students st
JOIN (SELECT year, SUM(tm.points) as teamPoints
FROM team_results tm
GROUP BY year) tr ON st.studentyear = tr.year
JOIN (SELECT idStudent, SUM(atr.points) as AthleticsPoints
FROM athletics_results atr
) ar ON st.idStudent = ar.idStudent
Which gave correct SUMS but only returned one year group row (e.g the scores for Year 11).
EDIT - SQLFiddle here: http://sqlfiddle.com/#!9/dbc16/. This is with my actual test data which is a bigger sample than the data I posted here.
http://sqlfiddle.com/#!9/ad111/7
SELECT tr.`year`, COALESCE(tr.points,0)+COALESCE(SUM(ar.points),0)
FROM Team_Results tr
LEFT JOIN Students s
ON tr.`year`=s.studentyear
LEFT JOIN Athletics_Results ar
ON s.idStudent = ar.idStudent
GROUP BY tr.year
According to your comment and fiddle provided
check http://sqlfiddle.com/#!9/dbc16/3
SELECT tr.`year`, COALESCE(tr.points,0)+COALESCE(SUM(ar.points),0)
FROM (
SELECT `year`, SUM(points) as points
FROM Team_Results
GROUP BY `year`) tr
LEFT JOIN Students s
ON tr.`year`=s.studentyear
LEFT JOIN Athletics_Results ar
ON s.idStudent = ar.idStudent
GROUP BY tr.year
Try this http://sqlfiddle.com/#!9/2bfb1/1/0
SELECT
year, SUM(points)
FROM
((SELECT
a.year, SUM(b.points) AS points
FROM
student a
JOIN at_result b ON b.student_id = a.id
GROUP BY a.year) UNION (SELECT
a.year, SUM(a.points) AS points
FROM
t_result a
GROUP BY a.year)) c
GROUP BY year;
On your data I get:
year points
7 14
8 15
11 43
Can be done in multiple ways. My first thought is:
SELECT idStudent, year, SUM(points) AS totalPoints FROM (
SELECT a.idStudent, c.year, a.points+b.points AS points
FROM students a
INNER JOIN Athletics_Results b ON a.idStudent=b.idStudent
INNER JOIN Team_Results c ON a.studentyear=c.year) d
GROUP BY idStudent,year

MySQL join with a subquery

I have three tables and am trying to get info from two and then perform a calculation on the third and display all the results in one query.
The (simplified) tables are:
table: employee_work
employee_id name
1 Joe
2 Bob
3 Jane
4 Michelle
table: carryover
employee_id days
1 5
2 10
3 3
table: timeoff
employee_id time_off_type days
1 Carryover 2
1 Leave 3
1 Carryover 1
2 Sick 4
2 Carryover 4
3 Leave 1
4 Sickness 4
The results I would like are:
employee_id, carryover.days, timeoff.days
1 5 3
2 10 4
3 3 0
However when I run the query, whilst I get the correct values in columns 1 and 2, I get the same number repeated in the third column for all entries.
Here is my query:
Select
employee_work.employee_id,
carryover.carryover,
(SELECT SUM(days) FROM timeoff WHERE timeoff.time_off_type = 'Carryover'
AND timeoff.start_date>='2013-01-01') AS taken
From
carryover Left Join
employee_work On employee_work.employee_id = carryover.employee_id Left Join
timeoff On employee_work.employee_id = timeoff.employee_id Left Join
Where
carryover.carryover > 0
Group By
employee_work.employee_id
I have tried to group by in the sub query but I then get told "Subquery returns more than one row" - how can I ensure that the sub query is respecting the join so it only looks at each employee at a time so I get my desired results?
The answer to your question is to use a correlated subquery. You don't need to mention the timeoff table twice in this case:
Select
employee_work.employee_id,
carryover.carryover,
(SELECT SUM(days)
FROM timeoff
WHERE timeoff.time_off_type = 'Carryover' and
timeoff.start_date>='2013-01-01' and
timeoff.employee_id = employee_work.employee_id
) AS taken
From
carryover Left Join
employee_work On employee_work.employee_id = carryover.employee_id
Where
carryover.carryover > 0
Group By
employee_work.employee_id;
An alternative structure is to do the grouping for all employees in the from clause. You can also remove the employee_work table, because it does not seem to be being used. (You can use carryover.employee_id for the id.)
Select co.employee_id, co.carryover, et.taken
From carryover c Left Join
(SELECT employee_id, SUM(days) as taken
FROM timeoff
WHERE timeoff.time_off_type = 'Carryover' and
timeoff.start_date>='2013-01-01'
) et
on co.employee_id = et.employee_id
Where c.carryover > 0;
I don't think the group by is necessary. If it is, then you should probably have an aggregation function in the original query.