MySQL refinement of query - mysql

I'm uing the following query to give me a set of the last 20 matches for a team. I want to find their goals scored in the last 20 matches and order the results by (goals scored, date):
SELECT * FROM (
SELECT *, `against` AS `goalsF` , `for` AS `goalsA`
FROM `matches` , `teams` , `outcomes`
WHERE(
`home_team_id`=7 AND `matches`.away_team_id = `teams`.team_id
OR
`away_team_id`=7 AND `matches`.home_team_id = `teams`.team_id
)
AND `matches`.score_id = `outcomes`.outcome_id
ORDER BY `date` DESC
LIMIT 0 , 20
) res
ORDER BY `goalsF`
The problem is that:
if the team we're looking up is the home team, we need to count "goalsfor".
if the team ins the away team we need to count "goalsagainst" to find their for goals.
So what I need to be able to do is something like:
if (`home_team_id`=7 AND `matches`.away_team_id = `teams`.team_id)
SELECT *, `for` AS `goalsF` , `against` AS `goalsA`
if (`away_team_id`=7 AND `matches`.home_team_id = `teams`.team_id)
SELECT *, `against` AS `goalsF` , `for` AS `goalsA`
But, this must be peformed on the sub-set or results. I'm not sure if this is even possible, but it is beyond my knowledge of MYSQL.
Any help would be hugely appreciated.
Alan.

First, you really need to learn ANSI standard join syntax, where you put the join conditions in an on clause rather than a from clause. Also, aliases may a query much more readable.
The following does the logic that you want, although it does not include the team names:
SELECT *
FROM (SELECT *,
(case when m.home_team_id = 7 then o.against end) as `goalsF` ,
(case when m.away_team_id = 7 then o.`for` end) as `goalsA`
FROM `matches` m join
`outcomes` o
on m.score_id = o.outcome_id
WHERE m.home_team_id = 7 or m.away_team_id = 7
ORDER BY `date` DESC
LIMIT 0 , 20
) res
ORDER BY `goalsF`
To get the team names, you should join twice to the teams table, once for the home team and once for the away team. You can do this either in the subquery or afterwards. It is also a good idea to explicit mention the columns you are choosing and to include a table alias on every column reference:
SELECT *
FROM (SELECT m.*, o.*,
homet.team_name as hometeam_name, awayt.team_name as away_team_name,
(case when m.home_team_id = 7 then o.against end) as `goalsF` ,
(case when m.away_team_id = 7 then o.`for` end) as `goalsA`
FROM `matches` m join
`outcomes` o
on m.score_id = o.outcome_id join
teams homet
on homet.team_id = m.home_team_id join
teams awayt
on awayt.team_id = m.away_team_id
WHERE m.home_team_id = 7 or m.away_team_id = 7
ORDER BY `date` DESC
LIMIT 0 , 20
) res
ORDER BY `goalsF`
EDIT:
To get just goals for team 7, you can use:
(case when m.home_team_id = 7 then o.`for`
when m.away_team_id = 7 then o.against
end) as goals
To get goals for the other team:
(case when m.home_team_id = 7 then o.against
when m.away_team_id = 7 then o.`for`
end) as goals
EDIT II:
To get the "other" team name, the logic is similar. Replace the team name references in the select with:
(case when m.home_team_id = 7 then awayt.team_name
when m.away_team_id = 7 then homet.team_name
end) as goals

Related

GROUPed or used in an aggregate function error

I want to Group By CUSTOM_DRIVER in the query because I am getting over 1.3 million results and I just need to view them by Custom Driver for the month by store.
The query is returning me
I want something like this
I've tried using Distinct and Group By but I keep getting the error:
Attribute A.STORE_NUM must be GROUPed or used in an aggregate function
How do I get around this? The code I've been using is:
SELECT *
FROM(
SELECT
SE.STORE_NUM,
DM.MONTH_NAME_445 AS MO_445,
--DM.WEEK_START_DT_MON AS WK_BEGIN_MONDAY,
--SE.METRIC_DATE,
substring(SE.ROLLUP_NAME, 39, 20) as CUSTOM_DRIVER,
SE.DT_IMPORTED,
SE.METRIC_VALUE
FROM DNA_PUBLIC.ADMIN.SCHEDULE_EFFECTIVENESS SE
JOIN DNA_PUBLIC.ADMIN.DAY_MAP DM ON SE.METRIC_DATE = DM.DATE_DT
JOIN (
SELECT DISTINCT
METRIC_DATE,
MAX(DATE(DT_IMPORTED)) AS MAX_DT
FROM DNA_PUBLIC.ADMIN.SCHEDULE_EFFECTIVENESS
GROUP BY 1
) MX ON SE.METRIC_DATE = MX.METRIC_DATE
WHERE METRIC_NAME = 'Coverage Effectiveness'
AND SE.ROLLUP_NAME LIKE 'O%'
AND SE.METRIC_DATE between '07/27/2020' and '11/11/2020'
AND CAST(SE.DT_IMPORTED AS DATE) = MAX_DT
--AND CUSTOM_DRIVER = 'Truck/Truck'
AND SE.INDICATOR_NAME = 'Required'
--and STORE_NUM = 1
) AS A
Order by STORE_NUM
A work around was to select the values and group by the order of the selected variables. Thanks for the all the input!
SELECT
STORE_NUM,
MO_445,
CUSTOM_DRIVER,
SUM(METRIC_VALUE) as Tot_Hrs
FROM(
SELECT
SE.STORE_NUM,
DM.MONTH_NAME_445 AS MO_445,
DM.WEEK_START_DT_MON AS WK_BEGIN_MONDAY,
SE.METRIC_DATE,
substring(SE.ROLLUP_NAME, 39, 20) as CUSTOM_DRIVER,
SE.DT_IMPORTED,
SE.METRIC_VALUE
FROM DNA_PUBLIC.ADMIN.SCHEDULE_EFFECTIVENESS SE
JOIN DNA_PUBLIC.ADMIN.DAY_MAP DM ON SE.METRIC_DATE = DM.DATE_DT
JOIN (
SELECT DISTINCT
METRIC_DATE,
MAX(DATE(DT_IMPORTED)) AS MAX_DT
FROM DNA_PUBLIC.ADMIN.SCHEDULE_EFFECTIVENESS
GROUP BY 1
) MX ON SE.METRIC_DATE = MX.METRIC_DATE
WHERE METRIC_NAME = 'Coverage Effectiveness'
AND SE.ROLLUP_NAME LIKE 'O%'
AND SE.METRIC_DATE between '07/27/2020' and '11/11/2020'
AND CAST(SE.DT_IMPORTED AS DATE) = MAX_DT
--AND CUSTOM_DRIVER = 'Truck/Truck'
AND SE.INDICATOR_NAME = 'Required'
and STORE_NUM = 1
) AS A
group by 1,2,3
Order by STORE_NUM

Why integer cast is not working with integer group_concat() list?

I'm stuck at the query where I need to concat IDs of the table. And from that group of IDs, I need to fetch that rows in sub query. But when I try to do so, MySQL consider group_concat() as a string. So that condition becomes false.
select count(*)
from rides r
where r.ride_status = 'cancelled'
and r.id IN (group_concat(rides.id))
*************** Original Query Below **************
-- Daily Earnings for 7 days [Final]
select
group_concat(rides.id) as ids,
group_concat(ride_category.name) as rideType,
group_concat(ride_cars.amount + ride_cars.commission) as rideAmount ,
group_concat(ride_types.name) as carType,
count(*) as numberOfRides,
(
select count(*) from rides r where r.ride_status = 'cancelled' and r.id IN (group_concat(rides.id) )
) as cancelledRides,
(
select count(*) from rides r where r.`ride_status` = 'completed' and r.id IN (group_concat(rides.id))
) as completedRides,
group_concat(ride_cars.status) as status,
sum(ride_cars.commission) + sum(ride_cars.amount) as amount,
date_format(from_unixtime(rides.requested_at/1000 + rides.offset*60), '%Y-%m-%d') as requestedDate,
date_format(from_unixtime(rides.requested_at/1000 + rides.offset*60), '%V') as week
from
ride_cars,
rides,
ride_category,
ride_type_cars,
ride_types
where
ride_cars.user_id = 166
AND (rides.ride_status = 'completed' or. rides.ride_status = 'cancelled')
AND ride_cars.ride_id = rides.id
AND (rides.requested_at >= 1559347200000 AND requested_at < 1561852800000)
AND rides.ride_category = ride_category.id
AND ride_cars.car_model_id = ride_type_cars.car_model_id
AND ride_cars.ride_type_id = ride_types.id
group by
requestedDate;
Any solutions will be appreciated.
Try to replace the sub-query
(select count(*) from rides r where r.ride_status = 'cancelled' and r.id IN (group_concat(rides.id) )) as cancelledRides,
with below to count using SUM and CASE, it will make use of the GROUP BY
SUM(CASE WHEN rides.ride_status = 'cancelled' THEN 1 ELSE 0 END) as cancelledRides
and the same for completedRides
And move to using JOIN instead of implicit joins

MySql GROUP BY Max Date

I have a table called votes with 4 columns: id, name, choice, date.
****id****name****vote******date***
****1*****sam*******A******01-01-17
****2*****sam*******B******01-05-30
****3*****jon*******A******01-01-19
My ultimate goal is to count up all the votes, but I only want to count 1 vote per person, and specifically each person's most recent vote.
In the example above, the result should be 1 vote for A, and 1 vote for B.
Here is what I currently have:
select name,
sum(case when uniques.choice = A then 1 else 0 end) votesA,
sum(case when uniques.choice = B then 1 else 0 end) votesB
FROM (
SELECT id, name, choice, max(date)
FROM votes
GROUP BY name
) uniques;
However, this doesn't work because the subquery is indeed selecting the max date, but it's not including the correct choice that is associated with that max date.
Don't think "group by" to get the most recent vote. Think of join or some other option. Here is one way:
SELECT v.name,
SUM(v.choice = 'A') as votesA,
SUM(v.choice = 'B') as votesB
FROM votes v
WHERE v.date = (SELECT MAX(v2.date) FROM votes v2 WHERE v2.name = v.name)
GROUP BY v.name;
Here is a SQL Fiddle.
Your answer are close but need to JOIN self
Subquery get Max date by name then JOIN self.
select
sum(case when T.vote = 'A' then 1 else 0 end) votesA,
sum(case when T.vote = 'B' then 1 else 0 end) votesB
FROM (
SELECT name,Max(date) as date
FROM T
GROUP BY name
) AS T1 INNER JOIN T ON T1.date = T.date
SQLFiddle
Try this
SELECT
choice,
COUNT(1)
FROM
votes v
INNER JOIN
(
SELECT
id,
max(date)
FROM
votes
GROUP BY
name
) tmp ON
v.id = tmp.id
GROUP BY
choice;
Something like this (if you really need count only last vote of person)
SELECT
sum(case when vote='A' then cnt else 0 end) voteA,
sum(case when vote='B' then cnt else 0 end) voteB
FROM
(SELECT vote,count(distinct name) cnt
FROM (
SELECT name,vote,date,max(date) over (partition by name) maxd
FROM votes
)
WHERE date=maxd
GROUP BY vote
)
PS. MySQL v 8
select
name,
sum( case when choice = 'A' then 1 else 0 end) voteA,
sum( case when choice = 'B' then 1 else 0 end) voteB
from
(
select id, name, choice
from votes
where date = (select max(date) from votes t2
where t2.name = votes.name )
) t
group by name
Or output just one row for the total counts of VoteA and VoteB:
select
sum( case when choice = 'A' then 1 else 0 end) voteA,
sum( case when choice = 'B' then 1 else 0 end) voteB
from
(
select id, name, choice
from votes
where date = (select max(date) from votes t2
where t2.name = votes.name )
) t
Based on #d-shish solution, and since introduction (in MySQL 5.7) of ONLY_FULL_GROUP_BY, the GROUP BY statement must be placed in subquery like this :
SELECT v.`name`,
SUM(v.`choice` = 'A') as `votesA`,
SUM(v.`choice` = 'B') as `votesB`
FROM `votes` v
WHERE (
SELECT MAX(v2.`date`)
FROM `votes` v2
WHERE v2.`name` = v.`name`
GROUP BY v.`name` # << after
) = v.`date`
# GROUP BY v.`name` << before
Otherwise, it won't work anymore !

mysql Sub queries in COUNT along with GROUP BY YEAR

Having some trouble figuring out the best way to do this.
Here is what I'm trying to do:
SELECT
YEAR(t.voucher_date) as period,
COUNT(t.id) as total_count,
(SELECT COUNT(t2.id) FROM booking_global as t2 where t2.booking_status = 'CONFIRMED') as confirmed,
(SELECT COUNT(t3.id) FROM booking_global as t3 where t3.booking_status = 'PENDING') as pending
FROM booking_global t
GROUP BY YEAR(t.voucher_date)
This produces the below result.
period total_count CONFIRMED PENDING
2014 4 5 3
2015 4 5 3
Expected Result
period total_count CONFIRMED PENDING
2014 4 3 1
2015 4 2 2
Here i want to get CONFIRMED / PENDING count's for respective years, rather than getting count of all statuses.
I am not sure how to use my query as a sub query and run another query on the results.
Flowing should give you right rsult
SELECT
YEAR(t.voucher_date) as period,
COUNT(t.id) as total_count,
(SELECT COUNT(t2.id) FROM booking_global as t2 where t2.booking_status = 'CONFIRMED' and YEAR(t2.voucher_date) = YEAR(t.voucher_date)) as confirmed,
(SELECT COUNT(t3.id) FROM booking_global as t3 where t3.booking_status = 'PENDING' and YEAR(t3.voucher_date) = YEAR(t.voucher_date)) as pending
FROM booking_global t
GROUP BY YEAR(t.voucher_date)
You can have a subquery that calculates each booking_status for each year. The result of which is then joined on table booking_global. Example,
SELECT YEAR(t.voucher_date) voucher_date_year,
COUNT(t.id) total_count,
IFNULL(calc.confirmed_count, 0) confirmed_count,
IFNULL(calc.pending_count, 0) pending_count
FROM booking_global t
LEFT JOIN
(
SELECT YEAR(voucher_date) voucher_date_year,
SUM(booking_status = 'CONFIRMED') confirmed_count,
SUM(booking_status = 'PENDING') pending_count
FROM booking_global
GROUP BY YEAR(voucher_date)
) calc ON calc.voucher_date_year = YEAR(t.voucher_date)
GROUP BY YEAR(t.voucher_date)

mySQL trouble building query with multiple limits

I Have this query which works with the single limit imposed at the end.
select distinct
loc.mID,
loc.city,
loc.state,
loc.zip,
loc.country,
loc.latitude,
loc.longitude,
baseInfo.firstname,
baseInfo.lastname,
baseInfo.profileimg,
baseInfo.facebookID,
(((acos(sin(('37.816876'*pi()/180)) * sin((`latitude`*pi()/180))+cos(('37.816876'*pi()/180)) * cos((`latitude`*pi()/180)) * cos((('-121.285410' - `longitude`)*pi()/180))))*180/pi())*60*1.1515) AS `distance`,
teams.teamName,
teams.leagueType,
teams.teamType,
teams.subcat
FROM memb_geo_locations loc
left join memb_friends friends on (friends.mID = loc.mID or friends.friendID = loc.mID) and (friends.mID = '100018' or friends.friendID = '100018')
join memb_baseInfo baseInfo on baseInfo.mID = loc.mID
join memb_teams teams on teams.mID = loc.mID
where
loc.primaryAddress = '1'
and ((friends.mID is null or friends.friendID is null)
or (friends.isactive = 2))
and (
teams.teamName like '%Anaheim Ducks%'
or teams.teamName like '%San Jose Sharks%'
or teams.teamName like '%New England Patriots%'
or teams.teamName like '%New York Yankees%'
or teams.teamName like '%Orlando Magic%'
)
and loc.mID != 100018
having `distance` < 50
order by baseInfo.firstname
asc limit 30
However I want my results to be limited by the teamName to 3 results max per, And I have tried stuff to the extent of
select distinct
loc.mID,
loc.city,
loc.state,
loc.zip,
loc.country,
loc.latitude,
loc.longitude,
baseInfo.firstname,
baseInfo.lastname,
baseInfo.profileimg,
baseInfo.facebookID,
(((acos(sin(('37.816876'*pi()/180)) * sin((`latitude`*pi()/180))+cos(('37.816876'*pi()/180)) * cos((`latitude`*pi()/180)) * cos((('-121.285410' - `longitude`)*pi()/180))))*180/pi())*60*1.1515) AS `distance`,
teams.teamName,
teams.leagueType,
teams.teamType,
teams.subcat
FROM memb_geo_locations loc
left join memb_friends friends on (friends.mID = loc.mID or friends.friendID = loc.mID) and (friends.mID = '100018' or friends.friendID = '100018')
join memb_baseInfo baseInfo on baseInfo.mID = loc.mID
join memb_teams teams on teams.mID = loc.mID
where
loc.primaryAddress = '1'
and ((friends.mID is null or friends.friendID is null)
or (friends.isactive = 2))
and (
(select * from memb_teams where teamName like '%Buffalo Bills%' limit 2),
(select * from memb_teams where teamName like '%San Jose Sharks%' limit 2),
(select * from memb_teams where teamName like '%New England Patriots%' limit 2)
)
and loc.mID != 100018
having `distance` < 150
order by baseInfo.firstname
asc limit 30
With no success, usually just syntax errors.. or Operand Should 1 Column(s) so I am reaching out here hopefully someone can give me some idea how to refine my query a bit so I can limit the results to 3 per teamName.. rather than having staggered results where I could have 20 of one and 4 of another 2 of another and 1 and 1 (which is not desired). 3 or less per team is desired, Just don't know how. Ideas, that don't involve tackling a huge data set from the query and looping over it via server side code to output results I desire?
IN MSSQL I use ROW_NUMBER function, and it would be something like this:
SELECT * FROM dbo.MyTable WHERE recno
IN (SELECT recno FROM (SELECT Teamname, ROW_NUMBER() OVER (PARTITION BY Teamname ORDER BY recno DESC) AS intRow FROM dbo.MyTable) AS T
WHERE intRow IN (1,2,3))
recno=your unique record number
Basically your Subquery selects the top 3 records, adding a new "ROW NUMBER" column.
The Top Query selects all the records with Rownumber between 1 to 3.
I know there is no ROW_NUMBER() native function in MYSQL, so you could use this instead:
MySQL - Get row number on select