MYSQL For/While Loop - mysql

Is it possible to do a For or While loop in MYSQL?
I've got the following code extract, but the full code goes up to home_id_15, home_score_15, away_id_15 and away_score_15:
$query3 = '
SELECT match_date, fixture_id, COUNT(a.home) AS home, SUM(a.points) AS points FROM
(
SELECT match_date, fixture_id, home_id_1 AS home, home_score_1 AS points FROM scores
WHERE home_id_1 =' .intval($_REQUEST['ID']).'
UNION ALL
SELECT match_date, fixture_id, away_id_1 AS home, away_score_1 AS points
FROM scores
WHERE away_id_1 =' .intval($_REQUEST['ID']).'
UNION ALL
SELECT match_date, fixture_id, home_id_2 AS home, home_score_2 AS points
FROM scores
WHERE home_id_2 =' .intval($_REQUEST['ID']).'
UNION ALL
SELECT match_date, fixture_id, away_id_2 AS home, away_score_2 AS points
FROM scores
WHERE away_id_2 =' .intval($_REQUEST['ID']).'
UNION ALL) a
GROUP BY match_date'
The first and second sub-SELECTS are basically being repeated until they reach 15.
This seems a bit long-winded and I was wondering if it's possible to use a loop in MYSQL to output
home_id_1, home_score_1, away_id_1, away_score_1 [up to] home_id_15, home_score_15, away_id_15, away_score_15
, respectively?
Thanks,
Dan.

It looks like you might need to normalize your database a little bit more. Let's say you had 6 scores for each row. Instead of making each score a column, make a separate table called "scores" or something like that with a foreign key column and a score column. Then join the table with this scores table.
Example:
TABLE: team
team_id
name
TABLE: scores
team_id
score
SELECT t.*, s.score
FROM team t
join scores s
on t.team_id=s.team_id;
Todo: Add the concept of matches into your schema and the Join

Related

MySQL - Column to Row

In the original problem, we have a table that stores the date and win/loss information for each game played by a team.
the matches table
We can use the following SQL statements to get information about the number of games won and lost for each day.
SELECT match_date AS match_date,
SUM(IF(result = 'win',1,0)) AS win,
SUM(IF(result = 'lose',1,0)) AS lose
FROM matches
GROUP BY date;
We store the query results in the matches_2 table.the matches_2 table
My question is, how can we get the matches table based on the matches_2 table with a query?
In the simpler case, we can achieve the task of 'column to row' using union/union all. But that doesn't seem to work in this problem.
All relevant sql code can be found in the following fiddle:
https://dbfiddle.uk/rM-4Y_YN
You can use recursive CTE for this:
WITH RECURSIVE wins (mdate, w) AS
(
SELECT match_date as mdate, 1
FROM matches
WHERE win>0
UNION ALL
SELECT match_date, w + 1 FROM matches
JOIN wins on match_date=mdate
WHERE w < win
),
losses (mdate, l) AS
(
SELECT match_date as mdate, 1
FROM matches
WHERE lose>0
UNION ALL
SELECT match_date, l + 1 FROM matches
JOIN losses on match_date=mdate
WHERE l < lose
)
SELECT mdate as match_date, 'lose' FROM losses
UNION ALL
SELECT mdate as match_date, 'win' FROM wins
See a db-fiddle

MySQL query to get common or duplicate values from different columns

I have a table with two columns, one column (AffiliationCountry) shows the countries and the other column (ArtSubareaKeyword) shows the subject areas in related countries with comma-separated values.
I want to extract the subject area which is repeating for a country the same country one or more times and save it in a new column with the name "MostPopularSubjectArea".
Table with values:
As you can see in the table that a country is repeating and its values are also repeating.
AffiliationCountry
ArtSubareaKeyword1
ArtSubareaKeyword1
ArtSubareaKeyword1
Spain
Cell membranes
Cell staining
Coimmunoprecipitation
Kazakhstan
Factor analysis
Human performance
Immunofluorescence
Japan
Bone marrow
Diagnostic medicine
Genetic loci
Kazakhstan
Drug research
Factor analysis
Human performance
Results that are required:
I want a SQL query that can store for that country a new column that stores the common subjects area which is occurring more.
AffiliationCountry
MostPopularSubjectArea
Kazakhstan
Human performance
As per the table, you can select the pair of columns, union them and find the count using group by:
select
t1.affiliation_country, t1.keyword, count(t1.keyword) as count_keyword
from
(
select affiliation_country, lower(artsubareakeyword1) keyword from affliation_details
union all
select affiliation_country, lower(artsubareakeyword2) from affliation_details
union all
select affiliation_country, lower(artsubareakeyword3) from affliation_details
) t1
group by
t1.affiliation_country, t1.keyword
order by
count(t1.keyword) desc
Query Reference(Fiddle): https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=25bded45786a85f6740902699e633846
Updated Query:
with affiliation_details as
(
select
t1.affiliation_country, t1.keyword, count(t1.keyword) as count_keyword
from
(
select affiliation_country, lower(artsubareakeyword1) keyword from affliation_details
union all
select affiliation_country, lower(artsubareakeyword2) from affliation_details
union all
select affiliation_country, lower(artsubareakeyword3) from affliation_details
) t1
group by
t1.affiliation_country, t1.keyword
order by
count(t1.keyword) desc
)
select
distinct affiliation_country
from
affiliation_details
where
count_keyword in (
select
max(count_keyword)
from
affiliation_details
)

How do i combine 3 queries into 1

I have 2 tables:
Battles
--battleId(primary)--gameId(foreign)--endTime
BattleParticipants
--battleParticipantId(primary)--userId(foreign)--someNumerical--score--battleId(foreign)
Given userId, i want to get only ended battle data for each battle user participated in with additional player rank information and total number of player participated in battle information.Ended battle means battles only with endTime smaller than current_time
returned battle data should include:
battleId:
endTime:
gameId:
score:
someNumerical:
rankOfPlayerInBattle:
totalNumberOfPlayersParticipatedInBattle:
for each battle player participated.
With below query i can get battleId,endTime,gameId,score,someNumerical as i want:
SELECT b.battleId,b.endTime,b.gameId,bp.score,bp.someNumerical FROM battles b JOIN battleparticipants bp ON b.battleId=bp.battleId WHERE bp.userId="someuserid" AND b.endTime<CURRENT_TIMESTAMP
Given battleId and userId this query returns me rank of user in that battle:
SET #i=0;
SELECT battleId, userId,score, #i:=#i+1 AS myRank
FROM battleparticipants WHERE battleId="asd1234" AND userId="someuserid"
ORDER BY score DESC
Given battleId this query gives me total number of players participated in that battle:
SELECT COUNT(*) FROM battleparticipants WHERE battleId="asd1234"
So given those seperated queries how do i retrieve data i want in one query ? (i dont really need to use above queries i just gave them as example since they get datas i want seperately)
i cant add fiddle because for some reason create code is throwing error. for now i add pictures of tables and data:
Battles table:
BattleParticipants table:
Below query wrong result:
SELECT b.battleId,b.endTime,b.gameId,bp.score,bp.someNumerical , RANK()OVER(PARITITON BY b.battleId, bp.userId order by score desc) rankOfPlayerInBattle , BP_C.CNT totalNumberOfPlayersParticipatedInBattle
FROM battles b
JOIN battleparticipants bp ON b.battleId=bp.battleId
JOIN (SELECT battleId , COUNT(*) CNT FROM battleparticipants GROUP BY battleId) BP_C ON BP_C.battleId=bp.battleId
WHERE bp.userId="someuserid" AND b.endTime<CURRENT_TIMESTAMP
You can use above SQL.

Query for getting top 5 candidate in every group in single table

I have a table in which student marks in each subject and i have to get query in such a way that i will able to get all top 5 student in every subject who secure highest marks.
Here is a sample table:
My expected output look somthing like :
Top five student in PCM, ART, PCB on the basis of students marks,And also if two or more student secure same than those record also need to be in list with single query.
Original Answer
Technically, what you want to accomplish is not possible using a single SQL query. Had you only wanted one student per subject you could have achieved that using GROUP BY, but in your case it won't work.
The only way I can think of to get 5 students for each subject would be to write x queries, one for each subject and use UNION to glue them together. Such query will return a maximum of 5x rows.
Since you want to get the top 5 students based on the mark, you will have to use an ORDER BY clause, which, in combination with the UNION clauses will cause an error. To avoid that, you will have to use subqueries, so that UNION and ORDER BY clauses are not on the same level.
Query:
-- Select the 5 students with the highest mark in the `PCM` subject.
(
SELECT *
FROM student
WHERE subject = 'PCM'
ORDER BY studentMarks DESC
LIMIT 5
)
UNION
(
SELECT *
FROM student
WHERE subject = 'PCB'
ORDER BY studentMarks DESC
LIMIT 5
)
UNION
(
SELECT *
FROM student
WHERE subject = 'ART'
ORDER BY studentMarks DESC
LIMIT 5
);
Check out this SQLFiddle to evaluate the result yourself.
Updated Answer
This update aims to allow getting more than 5 students in the scenario that many students share the same grade in a particular subject.
Instead of using LIMIT 5 to get the top 5 rows, we use LIMIT 4,1 to get the fifth highest grade and use that to get all students that have a grade more or equal to that in a given subject. Though, if there are < 5 students in a subject LIMIT 4,1 will return NULL. In that case, we want essentially every student, so we use the minimum grade.
To achieve what is described above, you will need to use the following piece of code x times, as many as the subjects you have and join them together using UNION. As can be easily understood, this solution can be used for a small handful of different subjects or the query's extent will become unmaintainable.
Code:
-- Select the students with the top 5 highest marks in the `x` subject.
SELECT *
FROM student
WHERE studentMarks >= (
-- If there are less than 5 students in the subject return them all.
IFNULL (
(
-- Get the fifth highest grade.
SELECT studentMarks
FROM student
WHERE subject = 'x'
ORDER BY studentMarks DESC
LIMIT 4,1
), (
-- Get the lowest grade.
SELECT MIN(studentMarks)
FROM student
WHERE subject = 'x'
)
)
) AND subject = 'x';
Check out this SQLFiddle to evaluate the result yourself.
Alternative:
After some research I found an alternative, simpler query that will yield the same result as the one presented above based on the data you have provided without the need of "hardcoding" every subject in its own query.
In the following solution, we define a couple of variables that help us control the data:
one to cache the subject of the previous row and
one to save an incremental value that differentiates the rows having the same subject.
Query:
-- Select the students having the top 5 marks in each subject.
SELECT studentID, studentName, studentMarks, subject FROM
(
-- Use an incremented value to differentiate rows with the same subject.
SELECT *, (#n := if(#s = subject, #n +1, 1)) as n, #s:= subject
FROM student
CROSS JOIN (SELECT #n := 0, #s:= NULL) AS b
) AS a
WHERE n <= 5
ORDER BY subject, studentMarks DESC;
Check out this SQLFiddle to evaluate the result yourself.
Ideas were taken by the following threads:
Get top n records for each group of grouped results
How to SELECT the newest four items per category?
Select X items from every type
Getting the latest n records for each group
Below query produces almost what I desired, may this query helps others in future.
SELECT a.studentId, a.studentName, a.StudentMarks,a.subject FROM testquery AS a WHERE
(SELECT COUNT(*) FROM testquery AS b
WHERE b.subject = a.subject AND b.StudentMarks >= a.StudentMarks) <= 2
ORDER BY a.subject ASC, a.StudentMarks DESC

Sum values across multiple tables

I'm having trouble trying to sum a field GROUPED BY a common user ID from TWO DIFFERENT tables.
To give you a little more info... I am trying to track player performances by date (i.e.: most points scored on ALL Nov. 14's). The database is split, one table for regular season games and one table for playoffs. So, for example, a player may have played a regular season game on May 3, 2001....but a playoff game on May 3, 2005.
So, I'm trying to build a view with the sums of every player on all dates.
What I have for a single table:
SELECT PlayerId,sum(Points) as TOT_PTS
FROM RS_games
WHERE DAY(Date)=$cur_day
AND MONTH(Date)=$cur_month
GROUP BY PlayerId
...but I can't figure how I could sum the values of each player across two tables without creating a third view as a "stepping stone".
Any ideas?
If you want the results by (DAY-MONTH) you can do:
SELECT playerID,
CONCAT (DAY(DATE), '-', MONTH(DATE)) AS DAY_MONTH,
SUM(points) AS Total_Points
FROM (
SELECT playerID, DATE, points
FROM rs_games
UNION ALL
SELECT playerID, DATE, points
FROM po_games
) a
GROUP BY 1, 2
sqlfiddle demo
This way, you would end up with a result with every playerId,dd-mm, and the sum of points that were scored in that specific day across the years.
Just to lay out what I was saying:
select
ALL_players.PlayerID as PlayerID
COALESCE(reg.Points, 0) + COALESCE(po.Points, 0) as Points
from
ALL_players
left join
(select PlayerID, DATE(Date) as Date, sum(Points) as Points
from RS_games
WHERE DAY(Date)=$cur_day AND MONTH(Date)=$cur_month
group by PlayerID) as reg
on reg.PlayerID = ALL_players.PlayerID
left join
(select PlayerID, DATE(Date) as Date, sum(Points) as Points
from PO_games group by DATE(Date), PlayerID
WHERE DAY(Date)=$cur_day AND MONTH(Date)=$cur_month
group by PlayerID) as po
on po.PlayerID = ALL_players.PlayerID
EDIT: Looking again at requirements this will need either a full outer join or some adjustment... adjusting (should be working now)