sql Runtime reports - php

I have a query which displays information from a table like this.
SELECT tag.label, inputs.input_raw, inputs.unix_timestamp, tag.inverted FROM inputs
JOIN tag
ON tag.tag_id = inputs.tag_id
WHERE (inputs.tag_id = 92084)
AND (inputs.date_time > dateadd(day,-1,getdate()))
ORDER BY date_time DESC
I would like to write a query which would do two things.
I need a count of every time input_raw switches from '0' to '1'.
I also need a total time of the pump running, using the unix_timestamp ie. when the input_raw = 1.
Does anyone have any ideas.
I would settle for an algorithm to use php to get the results I need but I've hit a brick wall and haven't been able to figure it out.
Thanks
EDIT: The table also contains a date_time field which matches the value of the unix_timestamp if there is a date_time method that can be used>

You want to use the Lead orLag function to compare the current result with either the previous or next. These functions, however, are introduced in SQL Server 2012.
With the help of Mr. pinaldave I managed to produce the following SQL Fiddle that counts every change from 0 to 1.
;WITH x AS
(
SELECT
1 AS ldOffset, -- equiv to 2nd param of LEAD
1 AS lgOffset, -- equiv to 2nd param of LAG
NULL AS ldDefVal, -- equiv to 3rd param of LEAD
NULL AS lgDefVal, -- equiv to 3rd param of LAG
ROW_NUMBER() OVER (ORDER BY unix_timestamp) AS row,
label,
input_raw,
unix_timestamp,
inverted
FROM inputs
)
SELECT
COUNT(1)
FROM x
LEFT OUTER JOIN x AS xLg
ON x.row = xLg.row + x.lgOffset
WHERE xLg.input_raw = 0 AND x.input_raw = 1;
You can use the same Lag function to calculate the difference between the current timestamp and the previous timestamp.
EDIT:
This SQL Fiddle should show how to get the total time the pump is running (you'll have to define running yourself. This query now assumes that going from 0 to 1 or staying 1 is running. You should also double check my timestamp calculations cause I've never used unix timestamps before.
;WITH x AS
(
SELECT
1 AS ldOffset, -- equiv to 2nd param of LEAD
1 AS lgOffset, -- equiv to 2nd param of LAG
NULL AS ldDefVal, -- equiv to 3rd param of LEAD
NULL AS lgDefVal, -- equiv to 3rd param of LAG
ROW_NUMBER() OVER (ORDER BY unix_timestamp) AS row,
label,
input_raw,
unix_timestamp,
inverted
FROM inputs
)
SELECT
SUM(DATEDIFF(mi,
DATEADD(ss, xLg.unix_timestamp,'01/01/1970'),
DATEADD(ss, x.unix_timestamp,'01/01/1970')))
FROM x
LEFT OUTER JOIN x AS xLg
ON x.row = xLg.row + x.lgOffset
WHERE
(xLg.input_raw = 0 AND x.input_raw = 1)
OR
(xLg.input_raw = 1 AND x.input_raw = 1);
EDIT 2:
I guess the easiest way to check for inverted is the change the WHEREclause to something like:
WHERE
(x.inverted = 1 AND xLg.input_raw = 0 AND x.input_raw = 1)
OR
(x.inverted = 0 AND xLg.input_raw = 1 AND x.input_raw = 0)

Related

How can I make this query run faster

I am running this query on my website in order to find a ToDo list based on specific criteria. But it runs too slow and it is probably possible to write it in another way.
SELECT * FROM lesson WHERE
id IN
(SELECT `lesson_id` FROM `localization_logging`
WHERE `language_id` = 2 AND `action_id` = 1)
AND `id` NOT IN
(SELECT `lesson_id` FROM `localization_logging`
WHERE `language_id` = 2 AND `part_id` = 1 AND `action_id` = 6)
What the query does is that it looks in the lesson table to find all lesson list names and then checks if a specific task is done. If the task is done in one todo than show it in the next. Action 1 is done but not action 6 in this case.
I hope I'm explaining this good enough. On my local machine the query takes 1.8 seconds, and sometimes I have to print multiple lists next to each others and then it takes 1.8 times the lists which makes the page load super slow.
Something like this for mark id as completed:
SELECT l.*, SUM(ll.action_id=6) completed FROM lesson l
INNER JOIN localization_logging ll ON ll.lesson_id = l.id
WHERE ll.language_id = 2 AND
(
ll.action_id = 1
OR
ll.action_id = 6 AND ll.part_id == 1
)
GROUP BY l.id
And now we can wrap it with:
SELECT t.* FROM (...) t WHERE t.completed = 0
You'll usually get faster queries filtering rows with INNER/LEFT JOIN, but you need to test it.
SELECT lesson.* FROM lesson
INNER JOIN localization_logging task1
ON lesson.id = task1.lesson_id
LEFT JOIN localization_logging task2
ON lesson.id = task2.lesson_id
AND task2.language_id = 2
AND task2.part_id = 1
AND task2.action_id = 6
WHERE task1.language_id = 2
AND task1.action_id = 1
AND task2.lesson_id IS NULL
Second table is joined on multiple conditions, but have to list them within ON clause because only results that were in result "force joined" as nulls (left join means left side stays no matter what) are required.
Btw. You'll get multiple rows from lesson if task1 condition is not limiting results to one row - GROUP BY lesson.id then.

Nested count() function with % percentage operation

I’m designing a program for my school to keep student attendance records. So far I have the following query working fine and now I would like to add an IF statement to perform a percentage operation when a certain condition is given. As it is, the query is using INNER JOIN to search for data from two different tables (oxadmain and stuattend) and it’s displaying the results well on a results table:
SELECT o.name
, o.year
, o.photoID
, o.thumbs
, s.ID
, s.studid
, s.date
, s.teacher
, s.subject
, s.attendance
FROM stuattend s
JOIN oxadmain o
ON s.studid = o.stuid
ORDER
BY name ASC
Now I would like to add an “if” statement that
1) finds when stuattend.attendance is = Absent, calculates the percentage of absences the students may have in any given period of time, and then stores that (%) value in “percentage” and
2) ELSE assigns the value of 100% to “Percentage”.
So far I’ve been trying with the following:
<?php $_GET['studentID'] = $_row_RepeatedRS['WADAstuattend']; ?>
SELECT oxadmain.name , oxadmain.year , oxadmain.photoID , oxadmain.thumbs , stuattend.ID , stuattend.studid , stuattend.date , stuattend.teacher, stuattend.subject , stuattend.attendance
CASE
WHEN stuattend.attendance = Absent THEN SELECT Count (studentID) AS ClassDays, (SELECT Count(*) FROM stuattend WHERE studentID = stuattend.studid AND Absent = 1) AS ClassAbsent, ROUND ((ClassAbsent/ClassDays)*100, 2) AS Percentage
ELSE
Percentage = 100
END
FROM stuattend INNER JOIN oxadmain ON stuattend.studid=oxadmain.stuid
ORDER BY name ASC
Any suggestions on how to do this well?
Thank you for your attention
The base idea would be:
select stuattend.studid, sum(stuattend.attendance = `absent`) / count(*)
from stuattend
group by stuaddend.studid;
This very much depends on exactly one entry per student and per day, and of course gets 0 if no absence and 1 if always absent.
To make this a bit more stable I would suggest to write a calendar day table, which simply keeps a list of all days and a column if this is a school day, so workday=1 means they should have been there and workday=0 means sunday or holiday. Then you could left join from this table to the presence and absence days, and even would give good results when presence is not contained in your table.
Just ask if you decide which way to go.

SQL count values from same column

Hi I have mysql table named content where i have a column "status" which have 3 values, converted, negotiating and received. now i want to count how many have status received, negotiating, and converted for developing a chart.
here is what i used:
SELECT status,
SUM(CASE WHEN status = 'converted' = 1 THEN 1 ELSE 0 END) AS converted,
SUM(CASE WHEN status = 'negotiating' = 1 THEN 1 ELSE 0 END) AS negotiating,
SUM(CASE WHEN status = 'Received NA' = 1 THEN 1 ELSE 0 END) AS ReceivedNA
FROM content GROUP BY status;
It shows me the result but in a way that i can not use it.
to feed my chart i used this:
$data = array(
array('converted', $converted),
array('negotiating', $negotiating),
array('received', $received)
);
So i guess some thing like this table will solve my problem:
status result
--------------------------- --------
converted 1
negotiating 5
received 4
So can anyone suggest how can modify my sql to get the expected result?
thanks again
Use GROUP By. Try this -
SELECT status, count(status) result FROM content GROUP BY status
To get the distinct count use GROUP BY.
select status,count(1) as result from content GROUP BY status;
Instead of using sum, count is always a better and easier way
EDIT-to answer the comment
The parameter to the COUNT function is an expression that is to be evaluated for each row. The COUNT function returns the number of rows for which the expression evaluates to a non-null value. ( * is a special expression that is not evaluated, it simply returns the number of rows.)
There are two additional modifiers for the expression: ALL and DISTINCT. These determine whether duplicates are discarded. Since ALL is the default, your example is the same as count(ALL 1), which means that duplicates are retained.
Since the expression "1" evaluates to non-null for every row, and since you are not removing duplicates, COUNT(1) should always return the same number as COUNT(*).
Will this work for you?
SELECT status, count(status) FROM content GROUP BY status;

How to calculate difference between values coming from the same row in mysql

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

Addition of time differences with where clause in mysql

I have a data table with hundreds of thousands of rows which represent requests to servers to get data. Each record has a timestamp, the server ID and a binary value (tinyint) of whether the server responded correctly. The query times are not constant.
I am trying to get a total amount of time that the server was deemed to be 'online' by adding up the times between the queries where the server was online (very highly preferable a mysql query).
Eg.
server | time | status
1 | 1/1/2012 11:00 online
1 | 1/1/2012 11:02 online
1 | 1/1/2012 11:05 offline
2 | 1/1/2012 11:10 online
1 | 1/1/2012 11:30 online
Time now: 11:40
Server 1 Online Time = 2+3+10 = 15 minutes
Is it possible to do this in mysql? I would much prefer it over getting all the rows to php and calculating it or averaging anything.
This could be done using UNIX timestamp conversion and variable assignment on a properly sorted row set. By "properly sorted" I mean the rows must be sorted by server, then by time. Here's how you could use variables to get the online time (interval) in seconds since the previous event for every row in your table (called server_status for the purpose of this answer):
SELECT
*,
#currenttime := UNIX_TIMESTAMP(`time`),
#lasttime := CASE
WHEN server <> #lastserver OR #laststatus = 'offline'
THEN #currenttime
ELSE #lasttime
END,
#currenttime - #lasttime AS seconds_online,
#lasttime := #currenttime,
#lastserver := server,
#laststatus := status
FROM
server_satus s,
(SELECT #lastserver := 0) x
ORDER BY
s.server,
s.`time`
As you can see, a temporary variable (#currenttime) is initialised with the UNIX timestamp equivalent of time, another one is used to hold the previous timestamp so that the difference between the two could be calculated. Other variables are used to remember the previous server ID and the previous status, so that, when necessary, the difference was returned as 0 (which is done for every row which records a server's first event as well as those that come after offline events).
You could now just group the result set produced by the above query, SUM() the seconds_online values and divide them by 60 to get minutes (if you aren't happy with seconds), like this:
SELECT
server,
SUM(seconds_online) DIV 60 AS minutes
FROM (
the query above
) s
Note, however, that the first query doesn't really calculate the servers' seconds spent online since their respective last events. That is, the current time might very well differ from that in any of the latest event records, and it wouldn't be taken into account, because the query calculates the seconds per row since the previous row.
One way to solve this would be to add one row per server containing the current timestamp and the same status as in the last record. So, instead of just server_status you would have the following as the source table:
SELECT
server,
`time`,
status
FROM server_status
UNION ALL
SELECT
s.server,
NOW() AS `time`,
s.status
FROM server_status s
INNER JOIN (
SELECT
server,
MAX(`time`) AS last_time
FROM server_status
GROUP BY
server
) t
ON s.server = t.server AND s.`time` = t.last_time
The left part of the UNION ALL just returns all rows from server_status. The right part first gets the last time per server, then joins the result set to server_status to get hold of the corresponding statuses, substituting time with NOW() along the way.
Now that the table is completed with the "fake" event rows reflecting the current time, you can apply the method used in the first query. Here's what the final query looks like:
SELECT
server,
SUM(seconds_online) DIV 60 AS minutes_online
FROM (
SELECT
*,
#currenttime := UNIX_TIMESTAMP(`time`),
#lasttime := CASE
WHEN server <> #lastserver OR #laststatus = 'offline'
THEN #currenttime
ELSE #lasttime
END,
#currenttime - #lasttime AS seconds_online,
#lasttime := #currenttime,
#lastserver := server,
#laststatus := status
FROM
(
SELECT
server,
`time`,
status
FROM server_status
UNION ALL
SELECT
s.server,
NOW() AS `time`,
s.status
FROM server_status s
INNER JOIN (
SELECT
server,
MAX(`time`) AS last_time
FROM server_status
GROUP BY
server
) t
ON s.server = t.server AND s.`time` = t.last_time
) s,
(SELECT #lastserver := 0) x
ORDER BY
s.server,
s.`time`
) s
GROUP BY
server
;
And you can try it (as well as play with it) at SQL Fiddle too.
Here is the sample table structure I created:
-- SQL EXAMPLE
CREATE TABLE IF NOT EXISTS `stack_test` (
`server` int(11) NOT NULL,
`rtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`status` tinyint(4) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO `stack_test` (`server`, `rtime`, `status`) VALUES
(1, '2012-01-01 11:00:24', 1),
(1, '2012-01-01 11:02:24', 1),
(1, '2012-01-01 11:05:24', 0),
(2, '2012-01-01 11:10:24', 1),
(1, '2012-01-01 11:30:24', 1);
-- SQL EXAMPLE END
This is the PHP code:
<?php
$query = 'SELECT DISTINCT(`server`) `server` FROM stack_test';
$res = sql::exec($query); // replace with your function/method to execute SQL
while ($row = mysql_fetch_assoc($res)) {
$server = $row['server'];
$uptimes = sql::exec('SELECT * FROM stack_test WHERE server=? ORDER BY rtime DESC',$server);
$online = 0;
$prev = time();
$prev = strtotime('2012-01-01 11:40:00'); // just to show that it works given the example
while ($uptime = mysql_fetch_assoc($uptimes)) {
if ($uptime['status'] == 1) {
echo date('g:ia',$prev) . ' to ' . date('g:ia',strtotime($uptime['rtime'])) . ' = '.(($prev-strtotime($uptime['rtime']))/60).' mins<br />';
$online += $prev-strtotime($uptime['rtime']);
}
$prev = strtotime($uptime['rtime']);
}
echo 'Server '.$server.' is up for '.($online/60).' mins.<br />';
}
?>
This is the output I get:
11:40am to 11:30am = 10 mins
11:05am to 11:02am = 3 mins
11:02am to 11:00am = 2 mins
Server 1 is up for 15 mins.
11:40am to 11:10am = 30 mins
Server 2 is up for 30 mins.

Categories