I have a 'sales' table called phpbb_sold which records each 'sale' as a row.
I am able to use a WHERE clause with the uitemid field to select one particular item in the sales records, as seen below:
SELECT uitemid, locktime, migrated_sold FROM phpbb_sold WHERE uitemid=342;
+---------+------------+---------------+
| uitemid | locktime | migrated_sold |
+---------+------------+---------------+
| 342 | 1632523854 | 1 |
| 342 | 1634239244 | 1 |
| 342 | 1634240072 | 1 |
| 342 | 1636367271 | 1 |
+---------+------------+---------------+
uitemid = number that identifies this as a sale of X item. locktime = UNIX timestamp that shows the datetime that the item was sold. migrated_sold = the quantity of the item sold. So this is nice, I have a table that keeps a record of each sale as it happens.
What I want to achieve though, is a record of the total number of sales of this item type, for each day in a 6 month period spanning back from the current date, and including each day regardless of whether a sale was made or not. So the desired output of my query would be:
SELECT (the query I want goes here) and returns the following rows...;
+------------+------------+
| caldate | sold_total |
+------------+------------+
| 2021-09-23 | 2 |
| 2021-09-24 | 0 |
| 2021-09-25 | 1 |
| 2021-09-26 | 0 |
| 2021-09-27 | 0 |
| 2021-09-28 | 1 |
+------------+------------+
Note that each day is included as a row in the results, even where the sales total for that day is 0. I read that to do this, I would be required to create a calendar table with one column and all the days I want as rows, so I went ahead and did that:
SELECT caldate FROM phpbb_calendar;
+------------+
| caldate |
+------------+
| 2021-09-23 |
| 2021-09-24 |
| 2021-09-25 |
| 2021-09-26 |
| 2021-09-27 |
| 2021-09-28 |
+------------+
Now all that remains is for me to make the query. I need to somehow return all the rows from the phpbb_calendar table, joining the data from sum() (?) of the total migrated_sold for those days where exists, and a 0 where no sales took place.
I anticipated some issues with the UNIX timestamp, but it's okay because I am able to get caldate and locktime fields to be the same format by using from_unixtime(locktime, '%Y-%m-%d'), so both dates will be in the YYYY-MM-DD format for comparison.
Please could someone help me with this. I've gotten so close every time but it seems that everyone else's request is only slightly different from mine, so existing questions and answers have not been able to satisfy my requirements.
End goal is to use a JS chart library (AnyChart) to show a line graph of the number of sales of the item over time. But to get there, I first need to provide it with the query necessary for it to display that data.
Thanks
Update
Using this query:
SELECT c.caldate, u.uitemid, sum(v.migrated_sold) as total_sales
from phpbb_calendar c cross join
(select distinct uitemid from phpbb_sold) u left join
phpbb_sold v
on c.caldate = from_unixtime(v.locktime, '%Y-%m-%d') WHERE u.uitemid = 39 and c.caldate <= curdate() GROUP BY c.caldate ORDER BY c.caldate;
Returns:
But as you can see, it's just tallying up the total number of sales ever made or something - its clearly incrementing in a way I don't understand.
I don't want it to do that - I want it to count the number of total sales on each day individually. The results should look like this:
So that what is returned is basically a 'histogram' of sales, if any occurred, including 'empty' days where there were no sales (so these empty days must still be returned as rows).
SELECT c.caldate, u.uitemid, COALESCE(SUM(v.migrated_sold), 0) AS total_sales
FROM phpbb_calendar c
CROSS JOIN (SELECT DISTINCT uitemid FROM phpbb_sold WHERE uitemid = 37) u
LEFT JOIN phpbb_sold v
ON v.locktime BETWEEN UNIX_TIMESTAMP(TIMESTAMP(c.caldate)) AND UNIX_TIMESTAMP(TIMESTAMP(c.caldate, '23:59:59'))
AND u.uitemid = v.uitemid
WHERE c.caldate BETWEEN CURDATE() - INTERVAL 6 MONTH AND CURDATE()
GROUP BY c.caldate, u.uitemid
ORDER BY c.caldate;
N.B. I have changed your join to use the unix_timestamp as it should be more efficient and it can use any existing index on locktime
check this out:
select id, d, sum(s) from (
select U.id, d, 0 s from (
select adddate(current_date(),-rows.r) d from (
select (#row_number := #row_number + 1) r
from information_schema.columns,
(SELECT #row_number := 0) AS x
limit 200
) rows
) dates,
(SELECT distinct uitemid id FROM `phpbb_sold`) U
where d > adddate(current_date(), interval -6 month)
union
select uitemid, date(from_unixtime(locktime)),sum(migrated_sold)
from `phpbb_sold`
group by uitemid, date(from_unixtime(locktime))
) sales_union
group by id, d
order by id, d;
see dbfiddle
no need for calendar table
My site allows users to guess the result of a sports match. At the end of the match the guesses should be compared to the actual result. The winner(s) are the members with the closest correct guess
Im looking for a way to return all members who guessed the correct result and score difference IF NO (zero) member guessed correctly return members who guessed closest to the correct result
See MYSQL FIDLE EXAMPLE
I modified the script to change fixed values taking variables as you can see below
if(isset($_POST['resultBtn'])){
foreach($_POST['winner'] as $id =>$winner){
$winScore = $_POST['score'][$id];
:
:
$sql="SELECT p.*
FROM Multiple_Picks p
WHERE p.event_id='$matchId' AND
p.pick='$winner' AND
abs(p.score-'$winScore') = (SELECT min(abs(p2.score-1))
FROM Multiple_Picks p2
Where p2.pick=p.pick AND
p2.event_id = p.event_id)";
My problem is if I run this script on the following table:
NOTHING gets displayed even if I put result exactly correct:
My variable values are correct in the sql statment so that is not the problem
Any help will be welcomed...
IMPORTANT THE USER WHO SELECTED CLOSEST CORRECT RESULTS, FOR ALL GAME, DURING THE ROUND IS THE WINNER
example: if user A won 4 of the picks and user B won 5 of the picks then user B is the winner of the round
Why don't you want just
SELECT p.*, abs(p.score-'$winScore') as diff
FROM Multiple_Picks p
WHERE p.event_id='$matchId' AND p.pick='$winner'
ORDER BY diff ASC
LIMIT 1
This will return the closest member for the event. Remove the LIMIT if you need a few of them.
Also, never put your parameters directly into the SQL query, even trusted ones (not your case) and even if you're sure they will always be integer or non-string type. Use prepared statements.
In this answer I call a "Best" pick any pick that has chosen the correct winner for a particular match, and has the closest score to the actual match score.
These scripts also respect the different "rounds" in the competition, since that is an important complication.
This answer comes in two parts: first a query that is similar to the one in the question that returns all the "Best" picks for a particular match. To make it easier to run in SQL Fiddle, I have used MySQL variables instead of PHP variables.
Schema with test data:
create table Multiple_Picks (
pick_id int,
member_nr int,
event_id int,
pick varchar(100),
score int
);
insert into Multiple_Picks
values
(11,100,1,'Crusaders',15),
(12,100,2,'Waratahs',10),
(13,100,3,'Chiefs',4),
(21,200,1,'Crusaders',15),
(22,200,2,'Waratahs',10),
(23,200,3,'Lions',4),
(31,300,1,'Crusaders',15),
(32,300,2,'Waratahs',12),
(33,300,3,'Lions',6),
(41,100,4,'Crusaders',20),
(42,100,5,'Waratahs',20),
(43,100,6,'Lions',20)
;
Queries to show all picks and then best picks for a particular match:
set #matchId = 2;
set #winner = 'Waratahs';
set #winScore = 8;
-- Show all picks for a particular match
select * from Multiple_Picks
where event_id = #matchId;
-- Show best picks for a particular match
select p.*
from Multiple_Picks p
where p.event_id = #matchId
and p.pick = #winner
and abs(p.score - #winScore) =
(select min(abs(other.score - #winScore))
from Multiple_Picks other
where other.event_id = #matchId
and other.pick = #winner
)
;
SQL Fiddle to show picks for particular match
-- Show all picks for a particular match
+---------+-----------+----------+----------+-------+
| pick_id | member_nr | event_id | pick | score |
+---------+-----------+----------+----------+-------+
| 12 | 100 | 2 | Waratahs | 10 |
| 22 | 200 | 2 | Waratahs | 10 |
| 32 | 300 | 2 | Waratahs | 12 |
+---------+-----------+----------+----------+-------+
-- Show best picks for a particular match
+---------+-----------+----------+----------+-------+
| pick_id | member_nr | event_id | pick | score |
+---------+-----------+----------+----------+-------+
| 12 | 100 | 2 | Waratahs | 10 |
| 22 | 200 | 2 | Waratahs | 10 |
+---------+-----------+----------+----------+-------+
Now we need to work towards finding the winner of each round of the competition.
First we have extra test data that contains the actual scores for Matches in rounds 1 and 2.
create table Matches (
event_id int,
winner varchar(100),
score int,
round int
);
insert into Matches
values
(1,'Crusaders',10,1),
(2,'Waratahs',11,1),
(3,'Lions',4,1),
(4,'Crusaders',20,2),
(5,'Waratahs',20,2),
(6,'Chiefs',20,2)
;
Now select the best picks for all Matches. The subselect (aliased as m) calculates best_diff for each match as the minimum difference between the actual score and every guessed score. This subselect is then joined to every pick so that only "Best" picks are returned.
-- Show all best picks for all Matches
select p.*, m.round
from Multiple_Picks p
join (
select m2.event_id, m2.winner, m2.score, m2.round,
min(abs(m2.score-p2.score)) as best_diff
from Matches m2
join Multiple_Picks p2
on p2.event_id = m2.event_id and p2.pick = m2.winner
group by m2.event_id, m2.winner, m2.score, m2.round
) as m
on p.event_id = m.event_id and p.pick = m.winner
and abs(m.score - p.score) = m.best_diff
order by m.round, p.event_id
;
It is then easy to get a count of Best picks for each player for each round by just grouping the previous query by member_nr and round:
-- Show a count of best picks for each player for each round
select p.member_nr, m.round, count(*) as best_count
from Multiple_Picks p
join (
select m2.event_id, m2.winner, m2.score, m2.round,
min(abs(m2.score-p2.score)) as best_diff
from Matches m2
join Multiple_Picks p2
on p2.event_id = m2.event_id and p2.pick = m2.winner
group by m2.event_id, m2.winner, m2.score, m2.round
) as m
on p.event_id = m.event_id and p.pick = m.winner
and abs(m.score - p.score) = m.best_diff
group by p.member_nr, m.round
order by m.round, count(*) desc
;
SQL Fiddle for all best picks and counts for all matches
-- Show all best picks for all Matches
+---------+-----------+----------+-----------+-------+-------+
| pick_id | member_nr | event_id | pick | score | round |
+---------+-----------+----------+-----------+-------+-------+
| 31 | 300 | 1 | Crusaders | 15 | 1 |
| 21 | 200 | 1 | Crusaders | 15 | 1 |
| 11 | 100 | 1 | Crusaders | 15 | 1 |
| 12 | 100 | 2 | Waratahs | 10 | 1 |
| 32 | 300 | 2 | Waratahs | 12 | 1 |
| 22 | 200 | 2 | Waratahs | 10 | 1 |
| 23 | 200 | 3 | Lions | 4 | 1 |
| 41 | 100 | 4 | Crusaders | 20 | 2 |
| 42 | 100 | 5 | Waratahs | 20 | 2 |
+---------+-----------+----------+-----------+-------+-------+
-- Show a count of best picks for each player for each round
+-----------+-------+------------+
| member_nr | round | best_count |
+-----------+-------+------------+
| 200 | 1 | 3 |
| 300 | 1 | 2 |
| 100 | 1 | 2 |
| 100 | 2 | 2 |
+-----------+-------+------------+
The final stage is to select only those players for each round who have the highest number of Best picks. I tried modifying the above queries, but the nesting becomes two confusing, so my solution was to create a few logical views so that the final query can be more easily understood. The views basically encapsulate the logic of the queries I have explained above:
create view MatchesWithBestDiff as
select m.event_id, m.winner, m.score, m.round,
min(abs(m.score-p.score)) as best_diff
from Matches m
join Multiple_Picks p
on p.event_id = m.event_id and p.pick = m.winner
group by m.event_id, m.winner, m.score, m.round
;
create view BestPicks as
select p.*, m.round
from Multiple_Picks p
join MatchesWithBestDiff m
on p.event_id = m.event_id and p.pick = m.winner
and abs(m.score - p.score) = m.best_diff
;
create view BestPickCount as
select member_nr, round, count(*) as best_count
from BestPicks
group by member_nr, round
;
So that the query that shows the winners of each round is simply:
-- Show the players with the highest number of Best Picks for each round
select *
from BestPickCount p
where best_count =
(
select max(other.best_count)
from BestPickCount other
where other.round = p.round
)
order by round
;
SQL Fiddle for players with most Best picks for each round
-- Show the players with the highest number of Best Picks for each round
+-----------+-------+------------+
| member_nr | round | best_count |
+-----------+-------+------------+
| 200 | 1 | 3 |
| 100 | 2 | 2 |
+-----------+-------+------------+
This whole investigation has reminded me how tricky it can be to get SQL to do much manipulation where records need to be selected depending on maximums and sums. Some of these types of queries can be much easier with window functions (the OVER and PARTITION BY clauses), but they are not available in MySQL.
While designing the above queries, I found a few interesting restrictions:
MySQL does not allow joins to subqueries in views definitions.
ANSI SQL does not allow an aggregate in a subquery to reference both a column from the inner query and a column from the outer query. MySQL seems to sometimes allow this, but I couldn't find clear guidance as to when it is allowed, so I chose to code the above queries to avoid this "feature".
scenario 1: NO USERS SELECTED THE CORRECT TEAM
I believe that result in this situation should be empty result because everyone has made a mistake.
SCORE RETURN MEMBERS WHO SELECTED THE CLOSEST TO CORRECT SCORE AND
RESULT
It seems to be already working in your code example except one mistake in select.
abs(p.score-'$winScore') = (SELECT min(abs(p2.score-1))
Instead of constant 1 (one) it should be variable '$winScore'
and to control the number of users you get, you may limit your results so you will get something like this:
$sql="SELECT p.*
FROM Multiple_Picks p
WHERE p.event_id='$matchId' AND
p.pick='$winner' AND
abs(p.score-'$winScore') = (SELECT min(abs(p2.score-'$winner'))
FROM Multiple_Picks p2
Where p2.pick=p.pick AND
p2.event_id = p.event_id)
order by p.id limit '$numberOfMembers'";
SCENARIO 2: SCENARIO 2: MULTIPLE USERS SELECTED CORRECT TEAM BUT
SCORES ARE DIFFERENT RETURN USER(S) WHO GUESSED CLOSEST TO CORRECT
SCORE
Same as in the previous question.
SCENARIO 3: MULTIPLE USERS SELECTED CORRECT TEAM AND SCORE RETURN ALL
USERS WHO SELECTED CORRECT TEAM AND SCORE
You can achieve this using same query just replace the LIMIT with 'rank' function, and also if you will get several closest scores, but you have to limit their number according to their voting order by id, for this purpose I suggest sorting.
So final query will be:
$sql="select * from (SELECT p.*,
abs(p.score-'$winScore') scr_diff,
#rownum := #rownum + 1 rank
FROM Multiple_Picks p,
(SELECT #rownum := 0) rank_gen
WHERE p.event_id='$matchId' AND
p.pick='$winner' AND
abs(p.score-'$winScore') = (SELECT min(abs(p2.score-'$winner'))
FROM Multiple_Picks p2
Where p2.pick=p.pick AND
p2.event_id = p.event_id)
order by p.id
) sq
where sq.scr_diff = 0
or sq.rank < '$numberOfMembers'";
Fiddle.
Best guesser for one match
First find the member(s) who picked the winner and had the closest score guess:
SELECT p.*
FROM
( SELECT MIN(ABS(score-'$winScore')) AS closest
FROM Multiple_Picks
WHERE event_id = '$matchId'
AND pick='$winner'
) AS c
JOIN Multiple_Picks p
WHERE p.event_id = '$matchId'
AND p.pick = '$winner'
AND ABS(score-'$winScore') = c.closest
If that return no results, then what should happen? (It would be because no one picked the winner for a particular event.)
But, I think your question is much more complex. However, the above gives a mapping from (event_id, pick) -> list-of-members who "won". Starting over...
Missing info
There is a mystery -- Where do the event results come from? I will assume this table is already populated:
CREATE TABLE Win (
event_id ..., -- which game
winnner ..., -- who won
score ... -- by what score
)
Best guesser overall
So, create a table of BestGuessers(event_id, member). The details of "all game" and "round" are a bit vague. So I will carry this at least one step further.
CREATE TEMPORARY TABLE BestGuessers(
event_id ...,
member_nr ... -- who guessed the best for that event
)
SELECT p.event_id, p.member_nr
FROM
( SELECT w.event_id, w.winner, MIN(ABS(mp.score-w.score)) AS closest
FROM Multiple_Picks AS mp
JOIN Win AS w ON mp.event_id = w.event_id
AND mp.pick = w.winner
GROUP BY w.event_id, w.winner
) AS c
JOIN Multiple_Picks p
ON p.event_id = c.event_id
AND p.pick = c.pick
AND p.score = c.closest
Now, from that, you can pick the best guesser(s).
SELECT y.member_nr
FROM
( SELECT COUNT(*) AS ct
FROM BestGuessers
GROUP BY member_nr
ORDER BY COUNT(*) DESC
LIMIT 1
) AS x -- the max number of correct guesses
STRAIGHT_JOIN
( SELECT member_nr, COUNT(*) AS ct
FROM BestGuessers
GROUP BY member_nr
) AS y -- the users who guessed correctly that many times
USING (ct);
All this is pretty complex; I may have some typos, even logic errors. But maybe I came close.
It seems an additional table to store the actual results would help here.
E.g let's say this is in a table called results with sample values as follows:
event_id winner result
1 Crusaders 16
2 Waratahs 15
3 Chiefs 4
4 Crusaders 17
5 Reds 12
0 Rebels 14
7 Cheetahs 15
8 Crusaders 14
This can then be JOINed on each row and results compared as follows:
SELECT p.*
, CASE WHEN ABS(p.score - r.result)
- CASE WHEN p.pick = r.winner THEN 999999 ELSE 0 END
= (SELECT MIN(ABS(p2.score - r2.result)
- CASE WHEN p2.pick = r2.winner THEN 999999 ELSE 0 END)
FROM picks p2
JOIN results r2
ON p2.event_id = r2.event_id
WHERE p2.event_id = p.event_id)
THEN 1
ELSE 0
END AS win
FROM picks p
JOIN results r
ON p.event_id = r.event_id;
Explanation
The rightmost win column is 1 if the member is calculated to have won or drawn the event, otherwise it is 0. The method used is similar to the one in your post, with the main difference being the team and score are combined. The main thing to be explained here is the 999999, which is subtracted when a correct team is picked - so this can be sure to eclipse the score difference. (Of course, an even bigger value could be picked if needed).
Demo
SQL Fiddle Demo
I have a challenging MySQL problem that is beyond my basic knowledge, I would really appreciate any help.
I currently have the following query:
select users.userid, CAST(posts.time AS DATE)
FROM users INNER JOIN posts ON users.post_id = posts.id
Sample output:
userid | CAST(posts.time AS DATE)
1............2015-01-05
2............2015-02-06
2............2015-04-07
2............2015-04-07
3............2015-04-07
1............2015-02-06
7............2015-01-05
userid can repeat itself, there could be 10 different rows with userid = 1; same goes for the date column. I would like to count how many rows each userid had for each distinct date. Based on the above data, the output should be:
-----------------------1----------2--------3---------4--------5--------6-------7
2015-01-05.............1..........0........0.........0........0........0.......1
2015-02-06.............1..........1........0.........0........0........0.......0
2015-04-07.............0..........2........1.........0........0........0.......0
I have 7 users in total. I would like to further replace the user id with a name that I define; e.g. I would define 1 in the heading/title to be displayed as Mike, 2 to be displayed as George, and so forth...
Is it possible? Thanks everyone.
If you have 7 users only, and only ever will, pivoting the data is not too difficult:
select date(posts.time),
count(case when userid = 1 then userid end) as `1`,
count(case when userid = 2 then userid end) as `2`,
count(case when userid = 3 then userid end) as `3`,
count(case when userid = 4 then userid end) as `4`,
count(case when userid = 5 then userid end) as `5`,
count(case when userid = 6 then userid end) as `6`,
count(case when userid = 7 then userid end) as `7`
users INNER JOIN posts ON users.post_id = posts.id
group by date(posts.time)
demo here
If your number of users is variable, or prone to change - it becomes annoying and you'd be better off looking to your application language to take care of it.
Here's what I have (I didn't complete it for you):
SELECT date, SUM(id_1) AS Mike, SUM(id_2) AS George FROM (SELECT CASE id WHEN 1 THEN 1 ELSE 0 END as id_1, CASE id WHEN 2 THEN 1 ELSE 0 END as id_2, date FROM test_dates) as tmp GROUP BY date;
+------------+------+--------+
| date | Mike | George |
+------------+------+--------+
| 2015-01-05 | 1 | 0 |
| 2015-02-06 | 1 | 1 |
| 2015-04-07 | 0 | 2 |
+------------+------+--------+
The trick of substituting a summation of 1s when what you want is a count is a common reporting trick that is worth remembering. Blew my mind when I first saw it.
I want to get amount of same role in one sum. When I fetch a records of agent details introduced by some agent (11150000001). It will return lot of agents records with different role(1000,2000,3000,4000,5000,6000).
From that I need to get the agents business amount total. Now I'm getting it. But If there is some agent in same role I want to get that same role agent's amount in single amount, but I'm getting separately.
//get all employee under one employee
$rank1=''; $rank2='';$rank3='';$rank4='';$rank5='';$rank6='';
$get_employee="SELECT emp_code,intro_code FROM emp_details WHERE intro_code='".$emp_code."'";
$exe_employee=mysql_query($get_employee);
while($fetch_emp=mysql_fetch_assoc($exe_employee))
{
$total_amount1=0;
$role='';
$employee_code=$fetch_emp['emp_code'];
//echo $employee_code."<br>";
$get_premium="SELECT emp_code,user_role,premium_amount FROM business_details WHERE emp_code='".$employee_code."'";
//echo $get_premium."<br>";
$exe_premium=mysql_query($get_premium);
while($fetch_amount=mysql_fetch_array($exe_premium))
{
$total_amount1 +=$fetch_amount['premium_amount'];
$role=$fetch_amount['user_role'];
}
}
echo $role."-".$total_amount1."<br>";
// Role-Amount
// 5000-75000
// 5000-105000
//3000-15000
In the above image I'm getting 10,500 in rank 5, but I want to get the 75000+10500 in rank 5 column.
Below is my table structure:
The entire block of code than be replaced by a single SQL query. You want to create a pivot table.
Lets start by a relatively simple query that fetches the total premium_amount of all the employee-role combinations:
SELECT emp_code,
user_role,
SUM(premium_amount) AS total_amount
FROM business_details
GROUP BY emp_code, user_role;
This would yield something like this:
emp_code | user_role | total_amount
-----------------------------------
15040000001 | 3000 | 15000
15040000001 | 5000 | 180000
11130000001 | 4000 | 30000
11130000001 | 1000 | 5000
This already contains all the information you want in your table, but not in the correct layout. Instead of using PHP to reformat this into the rows/columns you want for your table, SQL can do it for us.
Instead of grouping by user_role, we're going to specify the different values user_role can take as different columns:
SELECT emp_code,
SUM(CASE WHEN user_role=1000 THEN premium_amount ELSE 0 END) AS rank1,
SUM(CASE WHEN user_role=2000 THEN premium_amount ELSE 0 END) AS rank2,
SUM(CASE WHEN user_role=3000 THEN premium_amount ELSE 0 END) AS rank3,
SUM(CASE WHEN user_role=4000 THEN premium_amount ELSE 0 END) AS rank4,
SUM(CASE WHEN user_role=5000 THEN premium_amount ELSE 0 END) AS rank5,
SUM(CASE WHEN user_role=5000 THEN premium_amount ELSE 0 END) AS rank6
FROM business_details
GROUP BY emp_code;
The result would be:
emp_code | rank1 | rank2 | rank3 | rank4 | rank5 | rank6
------------------------------------------------------------
15040000001 | 0 | 0 | 15000 | 0 | 180000 | 0
11130000001 | 5000 | 0 | 0 | 30000 | 0 | 0
Bonus advise
The PHP code you've posted contains a glaring security flaw that leaves your database (or probably your entire system) wide open to anyone. SQL injection is not fun to have in your web application! I've written about it in a previous answer.
Try this...
$get_premium="SELECT emp_code,user_role,premium_amount,SUM(premium_amount) FROM business_details WHERE emp_code='".$employee_code."' group by user_role" ;
i have three tables( runners, stages and time)
Runners table:
+--+----+
|id|name|
+--+----+
|1 |Karl|
+--+----+
|2 |Lou |
+--+----+
Stage Table:
+--+-----+-----+---+
|id|name |order|end|
+--+-----+-----+---+
|1 |start| 1 | 0 |
+--+-----+-----+---+
|2 |bike | 2 | 0 |
+--+-----+-----+---+
|3 |run | 3 | 0 |
+--+-----+-----+---+
|4 |end | 4 | 1 |
+--+-----+-----+---+
Runners data(time) Table:
+------+-----+-----+
|runner|stage|time |
+------+-----+-----+
| 1 | 1 |10:00|
+------+-----+-----+
| 1 | 2 |10:30|
+------+-----+-----+
| 1 | 3 |11:00|
+------+-----+-----+
| 2 | 1 |10:00|
+------+-----+-----+
| 2 | 2 |10:43|
+------+-----+-----+
| 2 | 3 |11:56|
+------+-----+-----+
| 1 | 4 |12:14|
+------+-----+-----+
| 2 | 4 |12:42|
+------+-----+-----+
Well ... then what I want now is to get the results as follows( order by total time ):
+------+-----+-----+-----+-----+----------+
|runner|start|bike |run | end | Total |
+------+-----+-----+-----+-----+----------+
| Karl |10:00|10:30|11:00|12:14| 01:44:00 | <--- FIRST( one hour)
+------+-----+-----+-----+-----+----------+
| Lou |10:30|10:30|11:56|12:42| 02:12:00 | <--- SECONDS( two hours )
+------+-----+-----+-----+-----+----------+
Have any idea how I can accomplish this?
Greetings!
the following should work (times are in seconds, not in HH:MM:SS)
select r.name, rd_start.time as start, rd_bike.time as bike, rd_run.time as run, rd_end.time as end, from runner as r, rd_start.time+rd_bike.time+rd_run.time+rd_end.time as total
inner join runnerdata as rd_start on r.id=rd_start.runner and rd_start.stage=1
inner join runnerdata as rd_bike on r.id=rd_bike.runner and rd_start.stage=2
inner join runnerdata as rd_run on r.id=rd_run.runner and rd_start.stage=3
inner join runnerdata as rd_end on r.id=rd_end.runner and rd_start.stage=4
order by (rd_start.time+rd_bike.time+rd_run.time+rd_end.time)
(If you post the create tables or even better use this tool: http://sqlfiddle.com/ it would make it easier for us to test our statements)
The query would look something like this but the method for calculating the total depends on the data type of the time.
select runners.name as runner, starttime.time as start, biketime.time as bike, runtime.time as run, endtime.time as end, endtime.time - starttime.time as Total
from runners
inner join time as starttime on runners.id = starttime.runner
inner join stages as startstages on starttime.stage = startstages.id and startstages.name = 'start'
inner join time as biketime on runners.id = biketime.runner
inner join stages as bikestages on biketime.stage = bikestages.id and bikestages.name = 'bike'
inner join time as runtime on runners.id = runtime.runner
inner join stages as runstages on runtime.stage = runstages.id and runstages.name = 'run'
inner join time as endtime on runners.id = endtime.runner
inner join stages as endstages on endtime.stage = endstages.id and endstages.name = 'end'
order by endtime.time - starttime.time
This requires a join and then conditional aggregation. The final column uses timediff() to subtract the two times:
select r.name,
max(case when rt.stage = 1 then rt.time end) as start,
max(case when rt.stage = 2 then rt.time end) as walk,
max(case when rt.stage = 3 then rt.time end) as bike,
max(case when rt.stage = 4 then rt.time end) as end,
timediff(max(case when rt.stage = 4 then rt.time end),
max(case when rt.stage = 1 then rt.time end)
) as TotalTime
from RunnersTime rt join
Runners r
on rt.runner = r.id
group by r.id
order by TotalTime;
Note that the column names are fixed, so the stages table is not used. Making them dynamic would make the query much more complicated.
You would probably need to do a lot of inner joining, subquerying, and comparing this time vs. that time if you want to go with that schema, and it really won't be pretty. Alternatively if your stages are fixed you could simplify to one table with each column as a stage. If the number and names of stages need to vary (for whatever reason) then I'd suggest storing a start time and end time in your runners date/time table.
If your stages are fixed then getting the result you are looking for straight out of the database will be easy. If the stages can vary (depending on your site users configuring stages for example) then you'll want to cross-tab your data in PHP or look at this SO question if you insist on doing it in the database (which I'd discourage).