I have two database tables containing similar data - one is the amount of work completed by a process, the other is the amount of errors made by that process.
I would like to display the percentage of right first time work.
My query for counting the number of items by process is:
SELECT Counter.Process, count( Counter.Process ) AS count
FROM Counter WHERE (TIME BETWEEN '2012-07-01 00:00:00' AND '2012-07-06 23:59:59')
GROUP BY Counter.Process
ORDER BY count DESC
The above gives me a list similar to:
Process | count
-------------------
Process A | 40
Process B | 32
Process C | 102
Process D | 23
I have a second table with the same fields in it for recording errors, called 'Errors'.
By using the same query (but changing the table names obviously) I can get a list of processes and errors.
What I want to do is create a PHP web page showing the percentage of errors per process.
My problem is if I create the two queries separately, when I loop through them using a PHP Where loop I don't know how to make the calculation to show the percentage.
I tried using a UNION between the two SQL queries, but I can't get it to show the two different count readings against the process.
Ideally, I'd like a way to show the data like this:
Process | Counter.count | Errors.count
Process A 40 2
Process B 32 0
Process C 102 18
Process D 23 8
I would really appreciate any advice/suggestions as to how I can accomplish this - if you need more info on my database tables, please let me know
Thanks for reading
S
I did not run it so there might be mistakes. you can make the two selects, and left join the results:
select s.process, s.success_count, e.error_count
from
(SELECT Counter.Process as process, count( Counter.Process ) AS success_count
FROM Counter
WHERE (TIME BETWEEN '2012-07-01 00:00:00' AND '2012-07-06 23:59:59')
GROUP BY Counter.Process
) s left join
(SELECT Errors.Process, count( Errors.Process ) AS error_count
FROM Errors
WHERE (TIME BETWEEN '2012-07-01 00:00:00' AND '2012-07-06 23:59:59')
GROUP BY Errors.Process
) e
on (s.Process = e.process)
I personally prefer to run situations like this through PHP as it confuses me less, so I will explain how I think I would do it in PHP (I say 'think' because I might not know something about your database structure or system specifications that would make me thing otherwise):
$rows = array();
$res = mysql_query('SELECT Counter.Process, count( Counter.Process ) AS count
FROM Counter WHERE (TIME BETWEEN '2012-07-01 00:00:00' AND '2012-07-06 23:59:59')
GROUP BY Counter.Process
ORDER BY count DESC');
while ($arr = mysql_fetch_assoc($res))
{
$rows[] = $arr;
}
$res2 = mysql_query('SELECT Errors.Process, count( Errors.Process ) AS count
FROM Counter WHERE (TIME BETWEEN '2012-07-01 00:00:00' AND '2012-07-06 23:59:59')
GROUP BY Errors.Process
ORDER BY count DESC');
while ($arr2 = mysql_fetch_assoc($res2)
{
for ($i=0; $i<count($rows); $i++)
{
if ($arr2["Process"] == $data[$i]["process"])
{
break;
}
//Do your functions with $arr and $data[$i] at this point...
}
}
Doing it without a subquery should be simple:
SELECT
Counter.Process
, COUNT(Counter.Process) AS ProcessCount
, COUNT(Errors.Process) AS ProcessErrors
FROM
Counter
LEFT JOIN
Errors
ON Counter.Process = Errors.Process
WHERE
Counter.TIME BETWEEN '2012-07-01 00:00:00' AND '2012-07-06 23:59:59'
GROUP BY
Counter.Process
You can do an explain on both queries to see which is the best for your data.
-edit: I've changed it to a LEFT JOIN so you don't miss rows if a process has no errors.
You could use a sub-query:
SELECT Counter.Process, count( Counter.Process ) AS Count,
(SELECT count(Errors.Process)
FROM Errors
WHERE Process=Counter.Process
AND (TIME BETWEEN '2012-07-01 00:00:00' AND '2012-07-06 23:59:59')) AS Errors
FROM Counter WHERE (TIME BETWEEN '2012-07-01 00:00:00' AND '2012-07-06 23:59:59')
GROUP BY Counter.Process
ORDER BY count DESC
Regards,
Neil.
Related
I would like to better optimize my code. I'd like to have a single query that allows an alias name to have it's own limit and also include a result with no limit.
Currently I'm using two queries like this:
// ALL TIME //
$mikep = mysqli_query($link, "SELECT tasks.EID, reports.how_did_gig_go FROM tasks INNER JOIN reports ON tasks.EID=reports.eid WHERE `priority` IS NOT NULL AND `partners_name` IS NOT NULL AND mike IS NOT NULL GROUP BY EID ORDER BY tasks.show_date DESC;");
$num_rows_mikep = mysqli_num_rows($mikep);
$rating_sum_mikep = 0;
while ($row = mysqli_fetch_assoc($mikep)) {
$rating_mikep = $row['how_did_gig_go'];
$rating_sum_mikep += $rating_mikep;
}
$average_mikep = $rating_sum_mikep/$num_rows_mikep;
// AND NOW WITH A LIMIT 10 //
$mikep_limit = mysqli_query($link, "SELECT tasks.EID, reports.how_did_gig_go FROM tasks INNER JOIN reports ON tasks.EID=reports.eid WHERE `priority` IS NOT NULL AND `partners_name` IS NOT NULL AND mike IS NOT NULL GROUP BY EID ORDER BY tasks.show_date DESC LIMIT 10;");
$num_rows_mikep_limit = mysqli_num_rows($mikep_limit);
$rating_sum_mikep_limit = 0;
while ($row = mysqli_fetch_assoc($mikep_limit)) {
$rating_mikep_limit = $row['how_did_gig_go'];
$rating_sum_mikep_limit += $rating_mikep_limit;
}
$average_mikep_limit = $rating_sum_mikep_limit/$num_rows_mikep_limit;
This allows me to show an all-time average and also an average over the last 10 reviews. Is it really necessary for me to set up two queries?
Also, I understand I could get the sum in the query, but not all the values are numbers, so I've actually converted them in PHP, but left out that code in order to try and simplify what is displayed in the code.
All-time average and average over the last 10 reviews
In the best case scenario, where your column how_did_gig_go was 100% numeric, a single query like this could work like so:
SELECT
AVG(how_did_gig_go) AS avg_how_did_gig_go
, SUM(CASE
WHEN rn <= 10 THEN how_did_gig_go
ELSE 0
END) / 10 AS latest10_avg
FROM (
SELECT
#num + 1 AS rn
, tasks.show_date
, reports.how_did_gig_go
FROM tasks
INNER JOIN reports ON tasks.EID = reports.eid
CROSS JOIN ( SELECT #num := 0 AS n ) AS v
WHERE priority IS NOT NULL
AND partners_name IS NOT NULL
AND mike IS NOT NULL
ORDER BY tasks.show_date DESC
) AS d
But; Unless all the "numbers" are in fact numeric you are doomed to sending every row back from the server for php to process unless you can clean-up the data in MySQL somehow.
You might avoid sending all that data twice if you establish a way for your php to use only the top 10 from the whole list. There are probably way of doing that in PHP.
If you wanted assistance in SQL to do that, then maybe having 2 columns would help, it would reduce the number of table scans.
SELECT
EID
, how_did_gig_go
, CASE
WHEN rn <= 10 THEN how_did_gig_go
ELSE 0
END AS latest10_how_did_gig_go
FROM (
SELECT
#num + 1 AS rn
, tasks.EID
, reports.how_did_gig_go
FROM tasks
INNER JOIN reports ON tasks.EID = reports.eid
CROSS JOIN ( SELECT #num := 0 AS n ) AS v
WHERE priority IS NOT NULL
AND partners_name IS NOT NULL
AND mike IS NOT NULL
ORDER BY tasks.show_date DESC
) AS d
In future (MySQL 8.x) ROW_NUMBER() OVER(order by tasks.show_date DESC) would be a better method than the "roll your own" row numbering (using #num+1) shown before.
I'm having some problems with a query that finds the next ID of an orders with certain filters on it - Like it should be from a specific city, etc.
Currently it's used for a function, where it'll either spit out the previous or the next ID based on the current order. So it can either be min(id) or max(id), where max(id) is obviously faster, since it has to go through less rows.
The query is working just fine, but it's rather slow, since it's going through 123953 rows to find the ID. Is there any way I could optimize this?
Function example:
SELECT $minmax(orders.orders_id) AS new_id FROM orders LEFT JOIN orders_status ON orders.orders_status = orders_status.orders_status_id $where_join WHERE orders_status.language_id = '$languages_id' AND orders.orders_date_finished != '1900-01-01 00:00:00' AND orders.orders_id $largersmaller $ordersid $where;
Live example
SELECT min(orders.orders_id)
FROM orders
LEFT JOIN orders_status ON orders.orders_status = orders_status.orders_status_id
WHERE orders_status.language_id = '4'
AND orders.orders_date_finished != '1900-01-01 00:00:00'
AND orders.orders_id < 4868771
LIMIT 1
so concluding:
SELECT orders.orders_id
FROM orders
JOIN orders_status ON orders.orders_status = orders_status.orders_status_id
WHERE orders_status.language_id = '4'
AND orders.orders_date_finished != '1900-01-01 00:00:00'
AND orders.orders_id < 4868771
ORDER BY orders.orders_id ASC
LIMIT 1
Extra:
to get the MAX value, use DESC where ASC is now.
And looking at your question: be sure to escape the values like $language_id etcetera. I suppose they could come from some html form?
(or use prepared statements)
In attempting to calculate the longest goalscoring streak for a single player, I've hit a stumbling block with the >= PHP operator not performing as intended.
The data is presented in a table as follows:
date gls
------------------------
1980-08-16 2
1980-08-19 1
1980-08-23 1
1980-08-26 0
1980-08-30 1
1980-09-06 2
1980-09-13 0
... and so on
The PHP query I am using is as follows:
SELECT gls, MIN(date) as StartDate, MAX(date) as EndDate, COUNT(*) as Games
FROM (SELECT gls, date,
(SELECT COUNT(*)
FROM goalsengine G
WHERE G.gls <> GE.gls
AND G.date <= GE.date) as RunGroup
FROM goalsengine GE) A
WHERE gls>='1'
GROUP BY gls, RunGroup
ORDER BY Games
I formed the query this way in the belief that >= would tally up streaks where this player had scored one or more goals in a game. From the table above, the first three entries would represent a scoring run of three games, for example.
Instead, the query is returning streaks but only where a certain number of goals are scored i.e. despite scoring in the first three games, the first entry (where two goals were scored, not one) is ignored when the streak is returned.
To return the results, I am using the following:
while($row=mysql_fetch_assoc($result))
{
{$startrundate = date("d F Y",strtotime($row['StartDate']));}
{$endrundate = date("d F Y",strtotime($row['EndDate']));}
echo "<tr>";
echo "<td>".$row['Games']."</td>";
echo "<td class='tableprofile' style='text-align:right;'>".$startrundate." - ".$endrundate."</td>";
echo "</tr>";
$rowCount += 1;
}
By comparison, WHERE gls='0' is giving me the desired non-goalscoring streaks. I feel I have perhaps overlooked something straightforward but cannot see what.
I've managed to work out a solution to this particular issue and it works as far as I can tell, having counted some of the raw data out manually.
SELECT gls, MIN(date) as StartDate, MAX(date) as EndDate, COUNT(*) as Games
FROM (SELECT gls, date,
(SELECT COUNT(*)
FROM goalsengine G
WHERE IF(G.gls IN ('1','2','3','4','5'),1,0) <> IF(GE.gls IN ('1','2','3','4','5'),1,0)
AND G.date <= GE.date) as RunGroup
FROM goalsengine GE) A
WHERE gls IN ('1','2','3','4','5') GROUP BY IF(bg IN ('1','2','3','4','5'),1,0), RunGroup ORDER BY Games DESC LIMIT 3");
I admit it's not exactly pretty, but it's doing the job for now. I have gone to 5 in the calculation as that's the maximum number of goals scored in a single match with regards to this data set.
I have a start date and a end date.
$s="YYYY-MM-DD";
$e="YYYY-MM-DD";
I need to make a query that checks the available rooms for that span.
cObjects , cBookings_details
cObjects_id = cBookings_details_cObjects_id
Im really stuck here, would really appreciate some help.
My Current Query
SELECT cObjects.*
FROM cObjects LEFT JOIN cBookings_details ON cObjects.cObjects_id = cBookings_details.cBookings_details_cObjects_id
WHERE cBookings_details.cBookings_details_arrival NOT BETWEEN '".$s."' AND '".$e."'
AND cBookings_details.cBookings_details_departure NOT BETWEEN '".$s."' AND '".$e."'
AND cBookings_details.cBookings_details_arrival <> '".$s."' AND cBookings_details_departure <> '".$s."'
AND cBookings_details.cBookings_details_arrival <> '".$e."' AND cBookings_details_departure <> '".$e."'
AND cObjects.cObjects_type < '2'
OR cBookings_details.cBookings_details_id is null AND cObjects_type < '2'
This is a common problem.. actually just wrote a guide for this at work:
Selecting rows with start and end dates that feature in a chosen interval
-------------------------------------------------------------------------
Chosen Interval - :start_date (S)
:end_date (E)
Rows - row.start_date (s)
row.end_date (e)
Increasing date-->
S E
| | Chosen Interval
1. s---e Rows we want
2. s---e .
3. s---e .
4. s----------e .
5. s--e Rows we don't want
6. s--e .
LOGIC:
row.end_date >= :start_date AND row.start_date <= :end_date
If you are looking for available rooms, LEFT JOIN the room to the bookings using the logic above and SELECT rows where the booking is NULL.
Applied to your schema I believe this would come out as:
SELECT o.*
FROM cObjects o
LEFT JOIN cBookings_details bd
ON bd.cBookings_details_cObjects_id = o.cObjects_id
AND bd.cBookings_details_arrival <= :end_date
AND bd.cBookings_details_departure >= :start_date
WHERE bd.cBookings_details_id IS NULL
(AND o.cObjects_type < '2') # Double check this is what you want
By the way, I would cry if I had to work with your schema. Why repeat the table name in the column? It just adds noise.
I am trying to calculate the difference of values list coming from a database.
I would like to achieve it using php or mysql, but I do not know how to proceed.
I have a table named player_scores. One of its rows contains the goals scored.
Ex.
pl_date pl_scores
03/11/2014 18
02/11/2014 15
01/11/2014 10
I would like to echo the difference between the goals scored during the matches played in different dates.
Ex:
pl_date pl_scores diff
03/11/2014 18 +3
02/11/2014 15 +5
01/11/2014 10 no diff
How can I obtain the desired result?
You seem to want to compare a score against the score on a previous row.
Possibly simplest if done using a a sub query that gets the max pl_date that is less than the pl_date for the current row, then joining the results of that sub query back against the player_scores table to get the details for each date:-
SELECT ps1.pl_date, ps1.pl_scores, IF(ps2.pl_date IS NULL OR ps1.pl_scores = ps1.pl_scores, 'no diff', ps1.pl_scores - ps1.pl_scores) AS diff
FROM
(
SELECT ps1.pl_date, MAX(ps2.pl_date) prev_date
FROM player_scores ps1
LEFT OUTER JOIN player_scores ps2
ON ps1.pl_date > ps2.pl_date
GROUP BY ps1.pl_date
) sub0
INNER JOIN player_scores ps1
ON sub0.pl_date = ps1.pl_date
LEFT OUTER JOIN player_scores ps2
ON sub0.prev_date = ps2.pl_date
There are potentially other ways to do this (for example, using variables to work through the results of an ordered sub query, comparing each row with the value stored in the variable for the previous row)
SELECT score FROM TABLE WHERE DATE = TheDateYouWant
$score = $data['score'];
SELECT score FROM TABLE WHERE date = dateYouWant
$difference = $score - $data['score'];
Something like this?
You could use two queries, one to get the value to use in the comparison (in the example below is the smaller number of scores) and the second one to get the records with a dedicated column with the difference:
SELECT MIN(pl_scores);
SELECT pl_date, pl_scores, (pl_scores - minScore) as diff FROM player_scores;
Or, using a transaction (one query execution php side):
START TRANSACTION;
SELECT MIN(Importo) FROM Transazione INTO #min;
SELECT Importo, (Importo - #min) as diff FROM Transazione;
select *,
coalesce(
(SELECT concat(IF(t1.pl_scores>t2.pl_scores,'+',''),(t1.pl_scores-t2.pl_scores))
FROM tableX t2 WHERE t2.pl_date<t1.pl_date ORDER BY t2.pl_date DESC LIMIT 1)
, 'no data' ) as diff
FROM tableX t1
WHERE 1
order by t1.pl_date DESC