I need to either have a masive select statement or multiple queries. I need to break down data into specific timeframes during the day. This one works great for the first interval, but after that I'm stumped. If I have multiple queries, I'm having trouble with my "mysql_fetch_array", as to the syntax (using something like 'else' and going through them).
SELECT U.user_name,ROUND(SUM((TA.task_average*TC.completed)/60),2) AS equiv1, S.submit_date,
SUM(TC.completed) AS ttasks1,
FROM `summary` S
JOIN users U ON U.user_id = S.user_id
JOIN tasks TA ON TA.task_id = S.task_id
JOIN tcompleted TC ON TC.tcompleted_id = S.tcompleted_id
JOIN minutes M ON M.minutes_id = S.minutes_id
WHERE DATE(submit_date) = curdate( )
AND TIME(submit_date) BETWEEN '06:00:00' and '07:59:59'
GROUP BY U.user_name
LIMIT 0 , 30
My fetch array ( I would need to have a bunch more, but how to I combine them?)
<?php
while($rows=mysql_fetch_array($result1)){
?>
ok, given that you want the data across regular 2 hourly intervals, you could try something like this:
SELECT FLOOR(hour(S.submit_date)/2)*2, U.user_name,ROUND(SUM((TA.task_average*TC.completed)/60),2) AS equiv1, S.submit_date
SUM(TC.completed) AS ttasks1,
FROM `summary` S
JOIN users U ON U.user_id = S.user_id
JOIN tasks TA ON TA.task_id = S.task_id
JOIN tcompleted TC ON TC.tcompleted_id = S.tcompleted_id
JOIN minutes M ON M.minutes_id = S.minutes_id
WHERE DATE(submit_date) = curdate( )
GROUP BY U.user_name, FLOOR(hour(S.submit_date)/2)
LIMIT 0 , 30
where FLOOR(hour(S.submit_date)/2)*2 will map each hour to the first (even) hour of every 2 and you can group by this value. ie.
0, 1 -> 0
2, 3 -> 2
4, 5 -> 4
etc...
update with php included:
some notes:
i've used mysqli
i've left joined the original query with an hours derived table to ensure there are no
'gaps' in the time intervals (assumed 6:00 - 20:00)
i've ordered by user, so we as we loop through the results we can print table cells for a given user and then print a new table row when the user changes.
here's the code:
echo '<table border=1><tr><td></td>';
for ($i=6; $i<=18; $i=$i+2) {
echo '<td colspan=2>'.$i.' - '.($i+2).'</td>';
}
$mysqli = new mysqli('MY_HOST', 'MY_USER', 'MY_PASSWORD', 'MY_DATABASE');
$sql = "
SELECT user_name, IFNULL(equiv1,0) AS equiv1, IFNULL(ttasks1,0) AS ttasks1, hour
FROM
(
SELECT 6 AS hour
UNION SELECT 8
UNION SELECT 10
UNION SELECT 12
UNION SELECT 14
UNION SELECT 16
UNION SELECT 18
) hours LEFT JOIN
(
SELECT FLOOR(hour(S.submit_date)/2)*2 as task_hour, U.user_name, ROUND(SUM((TA.task_average*TC.completed)/60),2) AS equiv1, S.submit_date
SUM(TC.completed) AS ttasks1
FROM `summary` S
JOIN users U ON U.user_id = S.user_id
JOIN tasks TA ON TA.task_id = S.task_id
JOIN tcompleted TC ON TC.tcompleted_id = S.tcompleted_id
JOIN minutes M ON M.minutes_id = S.minutes_id
WHERE DATE(submit_date) = curdate( )
GROUP BY U.user_name, FLOOR(hour(S.submit_date)/2)
LIMIT 0 , 30
) task_summary
ON hours.hour = task_summary.task_hour
ORDER BY user_name, hour
";
$result = $mysqli->query($sql);
$user_name = '';
while ($row = $result->fetch_assoc()) {
if ($user_name <> $row['user_name']){
echo '</tr><tr><td>'.$row['user_name'].'</td>'; //start a new row if user changes
$user_name = $row['user_name']; //update user variable for checking on next iteration
}
echo '<td>'.$row['equiv1'].'</td>';
echo '<td>'.$row['ttasks1'].'</td>';
}
echo '</tr><table>';
This is in regard to your question "how to combine the fetch array".
Why not have an array to store all the values coming from your fetch array like the one below.
<?php
$customArray = array();
while($rows=mysql_fetch_array($result1)){
$customArray['field1'] = $row['field1'];
$customArray['field2'] = $row['field2'];
}
//another fetch array
while($rows=mysql_fetch_array($result2)){
$customArray['field1'] = $row['field1'];
$customArray['field2'] = $row['field2'];
}
//now $customArray will have all the required values you need.
//This is not a great option as it is making the logic expensive.
//Are you going to use this in a cron job ?
?>
Related
I have 2 MYSQL base queries which dependent on each other, here are my quires
#$query = "SELECT * FROM coins_tokens";
$row = $db->Execute($query);
foreach ($row as $rowItem) {
$name = $rowItem['ct_id'];
#$sql1 = "SELECT * FROM historical_data WHERE `name` = '".$name."' GROUP BY name LIMIT 30";
$row2 = $db->Execute($sql1);
foreach ($row2 as $rowItem2){
$market_cap = $rowItem2['market_cap'];
if($market_cap >= 500000000){
}
}
}
It slow down my whole process and take lot of time to execute, as there are more then 1400 results in coins_tokens, then there are more then 600000 records again 1st table, in both table ct_id and name are conman.
And what I am trying to do is to get the currencies which have more then 500million market_cap in last 7 days. So am fetching the currencies from 1st table and there historical data from 2nd table and checking if market_cap there increased in last 7 days.
Here is the structure and data of historical_data table:
SELECT
c.*,
d.`date`,
d.market_cap
FROM coins_tokens AS c
LEFT JOIN historical_data AS d ON c.ct_id = d.name
WHERE d.market_cap >= '$mketcapgrter'
AND DATE(d.`date`) >= CURRENT_DATE() - INTERVAL 30 DAY
GROUP BY d.name
ORDER BY d.market_cap DESC LIMIT 100
I am having a problem getting data from a large amount MySQL database.
With the below code it is ok to get the list of 10K patients and 5K appointments which is our test server.
However, on our live server, the number of patients is over 100K and the number of appointments is over 300K and when I run the code after a while it gives 500 error.
I need the list of the patients whose patient_treatment_status is 1 or 3 and has no appointment after one month from their last appointment. (The below code is working for small amount of patients and appointments)
How can I optimise the first database query so there will be no need the second database query in the foreach loop?
<?php
ini_set('memory_limit', '-1');
ini_set('max_execution_time', 0);
require_once('Db.class.php');
$patients = $db->query("
SELECT
p.id, p.first_name, p.last_name, p.phone, p.mobile,
LatestApp.lastAppDate
FROM
patients p
LEFT JOIN (SELECT patient_id, MAX(start_date) AS lastAppDate FROM appointments WHERE appointment_status = 4) LatestApp ON p.id = LatestApp.patient_id
WHERE
p.patient_treatment_status = 1 OR p.patient_treatment_status = 3
ORDER BY
p.id
");
foreach ($patients as $row) {
$one_month_after_the_last_appointment = date('Y-m-d', strtotime($row['lastAppDate'] . " +1 month"));
$appointment_check = $db->single("SELECT COUNT(id) FROM appointments WHERE patient_id = :pid AND appointment_status = :a0 AND (start_date >= :a1 AND start_date <= :a2)", array("pid"=>"{$row['id']}","a0"=>"1","a1"=>"{$row['lastAppDate']}","a2"=>"$one_month_after_the_last_appointment"));
if($appointment_check == 0){
echo $patient_id = $row['id'].' - '.$row['lastAppDate'].' - '.$one_month_after_the_last_appointment. '<br>';
}
}
?>
First off, this subquery likely does not do what you think it does.
SELECT patient_id, MAX(start_date) AS lastAppDate
FROM appointments WHERE appointment_status = 4
Without a GROUP BY clause, that subquery will simply take the maximum start_date of all appointments with appointment_status=4, and then arbitrarily pick one patient_id. To get the results you want you'll need to GROUP BY patient_id.
For your overall question, try the following query:
SELECT
p.id, p.first_name, p.last_name, p.phone, p.mobile,
LatestApp.lastAppDate
FROM
patients p
INNER JOIN (
SELECT patient_id,
MAX(start_date) AS lastAppDate
FROM appointments
WHERE appointment_status = 4
GROUP BY patient_id
) LatestApp ON p.id = LatestApp.patient_id
WHERE
(p.patient_treatment_status = 1
OR p.patient_treatment_status = 3)
AND NOT EXISTS (
SELECT 1
FROM appointments a
WHERE a.patient_id = p.patient_id
AND a.appointment_status = 1
AND a.start_date >= LatestApp.lastAppDate
AND a.start_date < DATE_ADD(LatestApp.lastAppDate,INTERVAL 1 MONTH)
)
ORDER BY
p.id
Add the following index, if it doesn't already exist:
ALTER TABLE appointments
ADD INDEX (`patient_id`, `appointment_status`, `start_date`)
Report how this performs and if the data appears correct. Provide SHOW CREATE TABLE patient and SHOW CREATE TABLE appointments for further assistance related to performance.
Also, try the query above without the AND NOT EXISTS clause, together with the second query you use. It is possible that running 2 queries may be faster than trying to run them together, in this situation.
Note that I used an INNER JOIN to find the latest appointment. This will result in all patients that have never had an appointment to not be included in the query. If you need those added, just UNION the results those found by selecting from patients that have never had an appointment.
I have in my page 5 queries that im wishing to have just one query if its possible , i dont know if there is a better strategy to what im doing or its ok .
this is my main query to load the books
$mess = $mysqli->prepare('SELECT * from ( SELECT m.id, cm.voteup,
cm.votedown,cr.book_descr FROM books m
INNER JOIN books_vote cm ON cm.bookid = m.id
INNER JOIN book_info cr ON cr.bookid = m.id
WHERE bookid = ? ORDER BY m.date DESC LIMIT 30)ddd
ORDER BY m.date ASC');
$mess->bind_param("i", $book);
$mess->execute();
$mess->store_result();
$mess->bind_result($id,$voteup,$votedown);
while($row = $mess->fetch()){
// im fetching here in this <div class='areabooks'>
}
Then i reset the query to fetch in other div with IF condition
$mess->data_seek(0);
while($row = $mess->fetch()){
if($voteup - $votedown >= 5){
//I fetch here in this <div class='areabooks2'>
}
}
Then im cheking the top MAX(voteup - votedown ) with this query
$messtop = $mysqli->prepare('SELECT ..........
INNER JOIN ...
INNER JOIN ( select id ,MAX(voteup - votedown ) as maxe
from books
where voteup - votedown >= 5
group by id ) tt
WHERE bookid = ?
ORDER BY maxe DESC,cm.votedown asc,cm.voteup DESC
limit 1');
$messtop->bind_param("i", $book);
$messtop->execute();
$messtop->store_result();
$messtop->bind_result($id,$voteup,$votedown);
$messtop->fetch();
// then i fetch in this <div class='topbook' >
Then i reset the first query again to fetch in other div with IF condition
$mess->data_seek(0);
while($row = $mess->fetch()){
if($votedown - $voteup >= 5){
//I fetch here in this <div class='areabooks3' >
}
}
Then im getting the top MAX(votedown - voteup ) with this query
$messtop = $mysqli->prepare('SELECT ..........
INNER JOIN ...
INNER JOIN ( select id ,MAX(votedown - voteup) as maxe
from books where votedown - voteup >= 5
group by id ) tt
WHERE bookid = ?
ORDER BY maxe DESC,cm.voteup ASC,cm.votedown DESC
limit 1');
$messdown->bind_param("i", $book);
$messdown->execute();
$messdown->store_result();
$messdown->bind_result($id,$voteup,$votedown);
$messdown->fetch();
// then i fetch in this <div class='topbottonbook' >
What im looking for is if there is a strategy to use just one query and fetch it in all these divs , those divs are separated like that .
<first div>
<second div>
<3rd div>
<4th div>
5th div
How to simplify this thanks ?
I doubt if the 3 queries can be combined into one.
First query:
Postpone the JOIN until you have the 30 'latest' bookids. That is, move the JOINs out to the outer SELECT.
Also have INDEX(id, date). But I am confused. Ordinarily id is the PRIMARY KEY, hence unique. Yet the query implies that one book has multiple dates. What gives? (Please provide SHOW CREATE TABLE.)
The mysql commandline tool (or Workbench or phpmyadmin) is your friend. Manually run the queries using one of them. I think that you will find errors. Please fix.
I have a table of available teams teams, with 24 different options.
I have another table entries, where each row is an allocation of one team to a user.
When an entry is created, a random team that has not been picked is allocated. However, if all the teams have been allocated (this can happen multiple times), only teams not yet allocated in this round of allocation are available.
For example, if my teams are A, B, C and D:
If there is an entry for A in entries, only B, C and D are available
If A, B, C and D have been picked, they are all available again
IF A has 3 entries, B has 3 entries, C has 2 entries and D has 2 entries, only C and D are available, until they all have the same number of entries
My code for this is convoluted:
//Make array of teams
for($i=1;$i<=24;$i++) $team[$i] = 1;
//Get entries from database
$stmt = $dbh->prepare("SELECT `team` FROM `entries`");
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
//Create array of available teams
$numRows = $stmt->rowCount();
while($numRows >= 24) {
for($i=1;$i<=24;$i++) {
$team[$i] = $team[$i]+1;
}
$numRows = $numRows - 24;
}
//Remove entries for teams in array
foreach($rows as $row) $team[$row["team"]] = $team[$row["team"]]-1;
foreach($team as $i => $v) if($v > 0) $available[] = $i;
There must be a more straightforward method to accomplish this; how can this be done?
The following gives you the number of assignments for each team:
SELECT team, COUNT(*) FROM entries GROUP BY team;
This gives you the minimum count for any team:
SELECT MIN(count) FROM (
SELECT COUNT(*) as count FROM entries GROUP BY team
)
To get the teams with the minimum count - those being available - but those two queries together into one:
SELECT teamcounts.team
FROM
(SELECT team, COUNT(*) as num FROM entries GROUP BY team) as teamcounts
WHERE
teamcounts.num = (
SELECT MIN(num) FROM (
SELECT COUNT(*) as num FROM entries GROUP BY team
) as tcounts
)
To get also those teams not yet included in entries we have to use the team table as well, removing all teams not currently available for selection:
SELECT teams.name
FROM teams
WHERE teams.name NOT IN (
SELECT teamcounts.team
FROM
(SELECT team, COUNT(*) as num FROM entries GROUP BY team) as teamcounts
WHERE
teamcounts.num != (
SELECT MIN(num) FROM (
SELECT COUNT(*) as num FROM entries GROUP BY team
) as tcounts
)
)
I haven't found a solution that works solely in SQL, however I've created the following query:
SELECT `id`, `num_selected` FROM
(SELECT `id`, SUM(is_selected) AS `num_selected` FROM
(SELECT t.`id`, CASE WHEN e.`team` IS NULL THEN 0 ELSE 1 END AS is_selected FROM `entries` e RIGHT JOIN `teams` t ON t.`id` = e.`team`)
AS `table1`
GROUP BY `id`)
AS `table2` GROUP BY `id` ORDER BY `num_selected` ASC, `id` ASC
This includes all the team rows that have NO entries yet, and the result is a table that has every team in one column, and alongside them is the number of selections.
Then, in PHP, I simply take the lowest value of selections (this will be the first row, as I've ordered by num_selected ASC) and only use other rows with that value as possible options:
$baseNum = $rows[0]["num_selected"];
foreach($rows as $row){
if($row["num_selected"]===$baseNum) $availableTeams[] = $row["id"];
}
However, ideally I'd have a solution that takes place solely in the SQL query!
I am making a stats page about golf for the people I play with. I am trying to pull out of the database the number of times out of all our scorecards that we received birdies (which is -1 under par). It does pull out the -1s per hole, however I noticed that you if you had 2 birdies on a scorecard, it still only counts as 1 birdie instead of 2. I want it to keep counting, so if someone gets 9 birdies, those 9 are added to the total.
$query_p321 = "SELECT t1.*,COUNT(t1.player_id),t2.* FROM scorecards t1 LEFT JOIN courses t2 ON t1.course_id=t2.course_id
WHERE t1.hole1<t2.hole1_par AND t1.hole1>t2.hole1_par-2
OR t1.hole2<t2.hole2_par AND t1.hole2>t2.hole2_par-2
OR t1.hole3<t2.hole3_par AND t1.hole3>t2.hole3_par-2
OR t1.hole4<t2.hole4_par AND t1.hole4>t2.hole4_par-2
OR t1.hole5<t2.hole5_par AND t1.hole5>t2.hole5_par-2
OR t1.hole6<t2.hole6_par AND t1.hole6>t2.hole6_par-2
OR t1.hole7<t2.hole7_par AND t1.hole7>t2.hole7_par-2
OR t1.hole8<t2.hole8_par AND t1.hole8>t2.hole8_par-2
OR t1.hole9<t2.hole9_par AND t1.hole9>t2.hole9_par-2
OR t1.hole10<t2.hole10_par AND t1.hole10>t2.hole10_par-2
OR t1.hole11<t2.hole11_par AND t1.hole11>t2.hole11_par-2
OR t1.hole12<t2.hole12_par AND t1.hole12>t2.hole12_par-2
OR t1.hole13<t2.hole13_par AND t1.hole13>t2.hole13_par-2
OR t1.hole14<t2.hole14_par AND t1.hole14>t2.hole14_par-2
OR t1.hole15<t2.hole15_par AND t1.hole15>t2.hole15_par-2
OR t1.hole16<t2.hole16_par AND t1.hole16>t2.hole16_par-2
OR t1.hole17<t2.hole17_par AND t1.hole17>t2.hole17_par-2
OR t1.hole18<t2.hole18_par AND t1.hole18>t2.hole18_par-2
GROUP BY t1.player_id ORDER BY count(t1.player_id) DESC";
$result_p321 = mysql_query($query_p321);
$number = 1;
while ($row_p321 = mysql_fetch_array($result_p321)) {
$player_id2 = $row_p321["player_id"];
}
and so on..
You'll notice the "-2" in there. That is taking the par minus 2, as I don't want to record if the person is 2 strokes under. Just one stroke under. Any help is appreciated. Thank you.
Oh, also, GROUP BY needs to be used as I don't want to list the player name more than once. Just want it to count all the birdies. I guess my big problem is its not counting more than 1 per row. Thanks.
The problem is the where clause. You need to do the comparisons in the select clause in order to count them:
SELECT t1.*,
sum((t1.hole1 = t2.hole1_par - 1) +
(t1.hole2 = t2.hole2_par - 1) +
. . .
(t1.hole18 = t2.hole18_par - 1)
) as birdies
FROM scorecards t1 LEFT JOIN
courses t2 ON t1.course_id=t2.course_id
GROUP BY t1.player_id
ORDER BY birdies DESC
This uses the MySQL convention that true is 1 and false 0 to add the numbers up. An alternative formulation using standard SQL is:
sum((case when t1.hole1 = t2.hole1_par - 1) then 1 else 0 end) +
Try something like that:
SELECT t1.*, SUM( IF(t1.hole1 = t2.hole1_par-1,1,0) +
IF(t1.hole2 = t2.hole2_par-1,1,0) +
IF(t1.hole3 = t2.hole3_par-1,1,0) +
IF(t1.hole4 = t2.hole4_par-1,1,0) +
-- etc.
IF(t1.hole18 = t2.hole18_par-1,1,0) ) AS birdies
FROM scorecards t1
LEFT JOIN courses t2 ON t1.course_id=t2.course_id
GROUP BY t1.player_id
ORDER BY birdies DESC