I’ve two tables (MySQL): player and scores. In scores table are stored annual scores in this way:
player_id | 2002 | 2003 | 2004
1 | 5 | 6 | 4
2 | 3 | 2 | 5
Etc.
I write the follow code to make a ranking based on last year scores.
$extract = mysql_query("SELECT * FROM players AS p, scores AS s WHERE p.player_id =s.player_id ORDER BY s.2004 desc");
while ($row = mysql_fetch_assoc($extract)) {
$name = $row['name'];
$score = $row['2004'];
if ($row['2004'] < $row['2003']) {
$value = "-";
}
else if ($row['2004'] > $row['2003']) {
$value = "+";
}
else if ($row['2004'] == $row['2003']) {
$value = "=";
}
echo "<b>".$name."</b> | ".$score." ".$value."<br>\n";
}
But this code has two big problems:
1) In the query I have to specify the last year (ORDER BY s.2004), so if I add another column (eg. 2005) into scores table, I have to manually change the code.
2) Same thing for the “$value” var. If I add 2005, the comparison between 2004 and 2003 becomes wrong (it should be between 2005 and 2004).
I know I have to use loops and array but... how?
You should redesign your scores table to store player_id, score and year in one row. So, each score gets its own row. When you find yourself duplicating columns, you are usually heading in the wrong direction.
Then, your query would look like:
Select Year(CURDATE());
select *
from players p
inner join scores s on p.player_id = s.player_id
where s.`Year` = Year(CURDATE()) --or (select max(Year) from Score)
Related
I have a MySQL database table like this
-------------------------------------------
name | distancefromstart | distance
-------------------------------------------
AA | 90 |
BB | 50 |
CC | 100 |
DD | 10 |
First I want to sort this table by value of the distancefromstart column.
After sorting the table, the first value of distancefromstart should substract from second value of distancefromstart and second value of distancefromstart should substract from third value of distancefromstart and third value of
distancefromstart should substract from forth value of distancefromstart and so on.
Then the new values should be added to the column called distance in the database.
Then the updated table should be like this:
name | distancefromstart | distance
-------------------------------------------
AA | 10 | 40
BB | 50 | 40
CC | 90 | 10
DD | 100 |
This will calculate the values you want:
select t1.name, t1.distancefromstart, t2.distancefromstart - t1.distancefromstart distance
from t t1
left join t t2
on t1.distancefromstart < t2.distancefromstart
where not exists (
select 1
from t t3
where t3.distancefromstart > t1.distancefromstart
and t3.distancefromstart < t2.distancefromstart
)
order by t1.distancefromstart asc;
demo fiddle here: http://sqlfiddle.com/#!9/d9f70/1
And if you want to update the source table, its just a matter of joining that query to the table, and updating, like so:
update t inner join (select t1.name, t1.distancefromstart, t2.distancefromstart - t1.distancefromstart distance
from t t1
left join t t2
on t1.distancefromstart < t2.distancefromstart
where not exists (
select 1
from t t3
where t3.distancefromstart > t1.distancefromstart
and t3.distancefromstart < t2.distancefromstart
)
order by t1.distancefromstart asc) r
on t.name = r.name and t.distancefromstart = r.distancefromstart SET t.distance = r.distance;
example: http://sqlfiddle.com/#!9/b6819/1
Let's say we have "test" database, "distance" table with data like here.
Now let's taste some old plane deprecated php code:
<?php
// :) omg, connect to database
mysql_connect('127.0.0.1', 'root', 'root');
mysql_select_db('test');
// extract data to array
$distances = [];
$q = mysql_query("SELECT * FROM distances ORDER BY distancefromstart");
while ($d = mysql_fetch_assoc($q)) $distances[] = $d;
// reset distances cause it may contain rubbish
mysql_query("UPDATE distances SET distance=0");
$d_prev = null; // distance before current
foreach ($distances as $d) {
if (null !== $d_prev) {
// distance is the diff between current distancefromstat and prev
$distance = $d['distancefromstart'] - $d_prev['distancefromstart'];
mysql_query('
UPDATE distances
SET distance='.$distance.'
WHERE name="'.mysql_real_escape_string($d_prev['name']).'"'
);
}
$d_prev = $d; // current will be prev on next loop
}
I'm trying to set up a table with scores from the MODX database, this data has been entered in via tv.variables in the MODX admin panel.
My SQL code retrieves my desired data with:
SELECT sc.pagetitle, cv.value, t.name
FROM pphc_site_tmplvar_contentvalues cv, pphc_site_content sc, pphc_site_tmplvars t
WHERE sc.id = cv.contentid
AND cv.tmplvarid = t.id
ORDER BY cv.value * 1 DESC
My SQL retrieves the team name, range of team scores (plays, wins, losses, draws, total points) and if they won/lost/drew.
Below is a screenshot of what I'm after (with the SQL I can get at the moment)
My code at the moment for dealing with the SQL/loop is:
//$output = $x; //get draws, losses, played, points and wins
$id = $x; //id of current page
function sort_by_value($a, $b) {
return $b["value"] - $a["value"];
}
$sql = "SELECT sc.pagetitle, cv.value, t.name
FROM pphc_site_tmplvar_contentvalues cv, pphc_site_content sc, pphc_site_tmplvars t
WHERE sc.id = cv.contentid
AND cv.tmplvarid = t.id
AND sc.id = $id
ORDER BY cv.value * 1 DESC";
$result = $modx->query($sql)->fetchAll();
usort($result, "sort_by_value");
//print_r($result);die();
$html = '';
foreach ($result as $row) {
$html .= ''. $row['value'] .'<br>';
}
return $html;
At the moment all this code does is retrieve the results of each team - Great!
I can't seem to figure out how to lay out the data in table form AND include the team names
Go back to your SQL & rewrite the query so that you get your results like this:
pagetitle | wins | losses | played | draws | points
Bath HC | 5 | 4 | 2 | 5 | 234
then you will be able to:
foreach ($result as $row) {
$html.= $modx->getChunk($rowTpl,$row);
}
return $html;
where $rowTpl is a chunk you have defined in modx ~ something like:
<tr>
<td>[[+pagetitle]]</td>
<td>[[+wins]]</td>
<td>[[+losses]]</td>
<td>[[+played]]</td>
<td>[[+draws]]</td>
<td>[[+points]]</td>
</tr>
It's just a matter of getting the query returning the data formatted in a more useful way.
I got the two tables(Table1 and Table2):
Table1:
id hits url
1 11 a
2 5 b
3 6 c
4 99 d
5 14 e
Table2:
id url 2014.04.13 2014.04.14
1 a 0 5
2 b 0 1
3 c 0 3
4 d 0 60
5 e 0 10
hi all,
Table1 one contains the actual hits(which are always up-to-date) and Table2 to statistics(which are done every day at midnight). The columns id(unique number) and url are in both tables the same. So they got the same amount of rows.
So i create every day a new column(with the date of today) and copy the column hits from the table 'Table1' into the new created column into the table 'Table2'
First i alter Table2:
$st = $pdo->prepare("ALTER TABLE Table2 ADD `$today_date` INT(4) NOT NULL");
$st->execute();
Then i cache all entries i need from Table1:
$c = 0;
$id = array();
$hits = array();
$sql = "SELECT id, hits FROM Table1 ORDER BY id ASC";
$stmt = $pdo->query($sql);
while($row = $stmt->fetch(PDO::FETCH_ASSOC))
{
$id[$c] = $row['id'];
$hits[$c] = $row['hits'];
$c++;
}
At last i update Table2:
for ($d = 0 ; $d < $c ; $d++)
{
$id_insert = $id[$d];
$sql = "UPDATE DOWNLOADS_TEST SET `$datum_det_dwnloads`=? WHERE id=?";
$q = $pdo->prepare($sql);
$q->execute(array($hits[$d], $id[$d]));
if($q->rowCount() == 1 or $hits[$d] == 0) // success
$hits[$d] = 0;
else // error inserting (e.g. index not found)
$d_error = 1; // error :( //
}
So what i need is to copy(insert) a column from one table to another.
The two tables are having ~2000 elements and the copying as described above takes around 40 sec. The bottleneck is the last part (inserting into the Table2) as i found out.
One thing i found is to do multiple updates in one query. Is there anything i can do besides that?
I hope you realise that at some point your table will have irrational number of columns and will be highly inefficent. I strongly advise you to use other solution, for example another table that holds data for each row for each day.
Let's say you have a table with 2000 rows and two columns: ID and URL. Now you want to know the count of hits for each URL so you add column HITS. But then you realise you will need to know the count of hits for each URL for every date, so your best bet is to split the tables. At this moment you have one table:
Table A (A_ID, URL, HITS)
Now remove HITS from Table A and create Table B with ID and HITS attributes). Now you have:
Table A (A_ID, URL)
Table B (B_ID, HITS)
Next move is to connect those two tables:
Table A (A_ID, URL)
Table B (B_ID, A_ID, HITS)
Where A_ID is foreign key to attribute "A_ID" of Table A. In the end it's the same as first step. But now it's easy to add date attribute to Table B:
Table A (A_ID, URL)
Table B (B_ID, A_ID, HITS, DATE)
And you have your solution for database structure. You will have a lot of entries in table B, but it's still better than a lot of columns. Example of how it would look like:
Table A | A_ID | URL
0 index
1 contact
Table B | B_ID | A_ID | HITS | DATE
0 0 23 12.04.2013
1 1 12 12.04.2013
2 0 219 13.04.2013
3 1 99 13.04.2013
You can also make unique index of A_ID and DATE in Table B, but I prefer to work on IDs even on linking tables.
I have three table Like this:
members_tbl
id | Fullname | Email | MobileNo
attendance_in_tbl
id | member_id | DateTimeIN
attendance_out_tbl
id | member_id | DateTime_OUT
I want to select all members for date: 2014-03-10 by this query:
SELECT
attendance_in.EDatetime,
members_info.mfullname,
attendance_out.ODatetime
FROM
attendance_in
LEFT JOIN members_info ON members_info.id = attendance_in.MemID
LEFT JOIN attendance_out ON attendance_out.MemID = attendance_in.MemID
WHERE date(attendance_in.EDatetime) OR date(attendance_out.ODatetime) = "2014-03-10"
But it give me different results in Attendace_out Results
You have a mistake in your query.
You wrote:
WHERE date(attendance_in.EDatetime) /* wrong! */
OR date(attendance_out.ODatetime) = "2014-03-10"
This is wrong, as the first expression date(attendance_in.EDatetime) always evaluates to true.
You may want
WHERE date(attendance_in.EDatetime) = "2014-03-10"
OR date(attendance_out.ODatetime) = "2014-03-10"
But, this is guaranteed to perform poorly when your attendance_in and attendance_out tables get large, because it will have to scan them; it can't use an index.
You may find that it performs better to write this:
WHERE (attendance_in.EDatetime >='2014-03-10' AND
attendance_in.EDatetime < '2014-03-10' + INTERVAL 1 DAY)
OR (attendance_out.EDatetime >='2014-03-10' AND
attendance_out.EDatetime < '2014-03-10' + INTERVAL 1 DAY)
That will check whether either the checkin our checkout time occurs on the day in question.
I have table:
user_id | song_id| points
--------|----------------
2 | 1 | 0
2 | 2 | 1
2 | 3 | 2
2 | 4 | 3
2 | 5 | 4
And I need to check if the user have changed the points value.
Therefore it should be something like:
while ($row = mysql_fetch_array($query)) {
$userID = $row['user_id'];
$songID = $row['song_id'];
$points = $row['points'];
if($songID-$points==1){
echo $userID."<br>";
}
But this will print out every occasion of userID where the song-id - points=1.
I need to print out only these user_id's that have all the values =1 and the username must echo'd only once.
EDIT:
SELECT DISTINCT user_id WHERE (song_id - points) = 1
This is half way there. This echo's user_ids' where the song_id - points = 1, but if the user is reordered (i use jQuery sortable) the list, then there can be some rows that is "song_id - points = 1".
My script must echo only these user_id-s, where users every song_id - points = 1, not only one
SELECT DISTINCT user_id FROM table WHERE (song_id - points) = 1
After edit:
SELECT table.user_id
FROM table
LEFT JOIN (
SELECT user_id, COUNT(*) AS C FROM table) AS T2
ON table.user_id = T2.user_id
WHERE (table.song_id - table.points) = 1
GROUP BY table.user_id
HAVING COUNT(*) = T2.C
You can first filter the users which has modified point values:
SELECT DISTINCT user_id FROM table
WHERE (song_id - points) != 1
Then you can use fetch the users which doesn't fit the above condition:
SELECT DISTINCT user_id FROM table
WHERE user_id NOT IN (
SELECT DISTINCT user_id FROM table
WHERE (song_id - points) != 1
)
According to your last edit this last SQL statement might work.
You can check a working example.
Here is what you're looking for:
select user_id from (
select user_id, if(song_id - points = 1, 0, 1) flag from t
) as S
group by user_id
having sum(flag) = 0
And here is a working example.
In case I didn't understand the requirements this shows all users who don't even have one row in which song_id - points != 1, i.e, all users who have all rows that match song_id - points = 1
Or maybe, if you prefer a different approach that might be more efficient:
select distinct t1.user_id from t t1
where not exists (
select * from t t2
where t2.song_id - t2.points != 1 and t1.user_id = t2.user_id
)
Here is the working example.
Not sure I understand the why of the situation, but a simple control-break structure will achieve the desired result ...
$old_id = '';
$good = false;
while($row = mysql_fetch_array($query)){
//check to see if we have a new user ...
if($row['user_id'] != $old_id){
//check to see if all values were == 1
if($good){
echo $old_id . '<br />';
}
//re-initialize variables
$good = true;
$old_id = $row['user_id'];
}
//if value != 1, we won't print the user ...
if($row['song_id'] - $row['points'] != 1){
$good = false;
}
}
//final end-of-loop condition ...
if($good){
echo $old_id . '<br />';
}
OK, here's a query that's a lot more simple than the join above:
SELECT user_id, sum(song_id) as song_total, sum(points) as point_total, count(*) AS cnt FROM table GROUP BY user_id
If the difference between song_id and points is 1 for every song, then the difference between the totals will equal the number of rows for that user ... so using this query:
while($row = mysql_fetch_array($query)){
if($row['cnt'] == ($row['song_total'] - $row['point_total'])){
echo $row['user_id'] . '<br />';
}
}