Calculate time differences between multiple set of data - mysql

This is a data set in a mysql table which is related to a error log of an electronic divice.
i need to calculate the total down time.
time_stamp error_type error_status
1467820110 1 1
1467820120 2 1
1467820130 3 1
1467820140 3 0
1467820150 1 0
1467820160 2 0
1467820180 1 1
1467820185 1 0
1467820191 2 1
1467820300 2 0
1467820302 1 1
1467820404 3 1
1467820408 3 0
1467820409 1 0
error_status 1 = error occored
error_status 0 = error fixed
1st down time 1467820160 - 1467820110 = 50
2nd down time 1467820185 - 1467820180 = 5
3rd down time 1467820300 - 1467820191 = 109
4th down time 1467820409 - 1467820302 = 107
total down time = 50 + 5 + 109 + 107 = 271
How can i write a mySQL compatible SQL statement to achieve this.

Basically, you need to calculate the number of cumulative errors that have occurred. Then identify groups where the values are greater than 0. This can be done by doing a cumulative count of the number of "0"s for the cumulative errors.
There is a challenge getting the final timestamp. One trick is to get the next status 0 timestamp for the error. This acts as an "end".
Finally, an aggregation get the information for each period:
select count(*) as num_errors, max(end_timestamp) - min(timestamp)
from (select t.*,
(#grp := #grp + if(cume_errors = 0, 1, 0)) as grp
from (select t.*,
(select t2.timestamp
from t t2
where t2.error_type = t.error_type and
t2.error_status = 0 and
t2.timestamp > t.timestamp
order by t2.timestamp asc
limit 1
) as end_timestamp,
(#e := #e + if(error_status > 0, 1, -1)) as cume_errors
from t cross join
(select #e := 0) params
order by timestamp
) t cross join
(select #grp := 0) params
order by timestamp
) t
where error_status > 0
group by grp;
You can aggregate over this query to get the total period of downtime.
Here is a SQL Fiddle.

use this you will get the total
SELECT sum(IF(error_status=1,time_stamp*-1,time_stamp)) as total FROM table;
----------example----
mysql> SELECT sum(IF(error_status=1,time_stamp*-1,time_stamp)) as total FROM hh;
+-------+
| total |
+-------+
| 315 |
+-------+
1 row in set (0.06 sec)
mysql> SELECT *,(IF(error_status=1,time_stamp*-1,time_stamp)) as total FROM hh;
+------------+------------+--------------+-------------+
| time_stamp | error_type | error_status | total |
+------------+------------+--------------+-------------+
| 1467820110 | 1 | 1 | -1467820110 |
| 1467820120 | 2 | 1 | -1467820120 |
| 1467820130 | 3 | 1 | -1467820130 |
| 1467820140 | 3 | 0 | 1467820140 |
| 1467820150 | 1 | 0 | 1467820150 |
| 1467820160 | 2 | 0 | 1467820160 |
| 1467820180 | 1 | 1 | -1467820180 |
| 1467820185 | 1 | 0 | 1467820185 |
| 1467820191 | 2 | 1 | -1467820191 |
| 1467820300 | 2 | 0 | 1467820300 |
| 1467820302 | 1 | 1 | -1467820302 |
| 1467820404 | 3 | 1 | -1467820404 |
| 1467820408 | 3 | 0 | 1467820408 |
| 1467820409 | 1 | 0 | 1467820409 |

Related

MySQL - Select Top 5 with Rankings

I'm trying to get a users ranking getting his highest performances in every beatmap.
I get the user highest performance in every beatmap (only taking the top 5 performances) and adding them together, but it fails when the highest performance in one beatmap is repeated... because it counts twice
I'm based in this solution, but it doesn't works well for me...
Using MySQL 5.7
What i'm doing wrong?
Fiddle
Using this code:
SET group_concat_max_len := 1000000;
SELECT #i:=#i+1 rank, x.userID, x.totalperformance FROM (SELECT r.userID, SUM(r.performance) as totalperformance
FROM
(SELECT Rankings.*
FROM Rankings INNER JOIN (
SELECT userID, GROUP_CONCAT(performance ORDER BY performance DESC) grouped_performance
FROM Rankings
GROUP BY userID) group_max
ON Rankings.userID = group_max.userID
AND FIND_IN_SET(performance, grouped_performance) <= 5
ORDER BY
Rankings.userID, Rankings.performance DESC) as r
GROUP BY userID) x
JOIN
(SELECT #i:=0) vars
ORDER BY x.totalperformance DESC
Expected result:
+------+--------+------------------+
| rank | userID | totalperformance |
+------+--------+------------------+
| 1 | 1 | 450 |
+------+--------+------------------+
| 2 | 2 | 250 |
+------+--------+------------------+
| 3 | 5 | 140 |
+------+--------+------------------+
| 4 | 3 | 50 |
+------+--------+------------------+
| 5 | 75 | 10 |
+------+--------+------------------+
| 6 | 45 | 0 | --
+------+--------+------------------+
| 7 | 70 | 0 | ----> This order is not relevant
+------+--------+------------------+
| 8 | 76 | 0 | --
+------+--------+------------------+
Actual Result:
+------+--------+------------------+
| rank | userID | totalperformance |
+------+--------+------------------+
| 1 | 1 | 520 |
+------+--------+------------------+
| 2 | 2 | 350 |
+------+--------+------------------+
| 3 | 5 | 220 |
+------+--------+------------------+
| 4 | 3 | 100 |
+------+--------+------------------+
| 5 | 75 | 10 |
+------+--------+------------------+
| 6 | 45 | 0 | --
+------+--------+------------------+
| 7 | 70 | 0 | ----> This order is not relevant
+------+--------+------------------+
| 8 | 76 | 0 | --
+------+--------+------------------+
As you have mentioned that you are picking only top 5 performances per user across beatmaps then you can try this way:
select #i:=#i+1, userid,performance from (
select userid,sum(performance) as performance from (
select
#row_number := CASE WHEN #last_category <> t1.userID THEN 1 ELSE #row_number + 1 END AS row_number,
#last_category :=t1.userid,
t1.userid,
t1.beatmapid,
t1.performance
from (
select
userid, beatmapid,
max(performance) as performance
from Rankings
group by userid, beatmapid
) t1
CROSS JOIN (SELECT #row_number := 0, #last_category := null) t2
ORDER BY t1.userID , t1.performance desc
) t3
where row_number<=5
group by userid
)
t4 join (SELECT #i := 0 ) t5
order by performance desc
Above query will not consider duplicate Performance Score and pick only top 5 performance values.
DEMO

How to count in a range of result in mysql

l have a record table now, and l must to statistics the result of every month.
here is a test table
+----+------+----------+----------+------+
| id | name | grade1 | grade2 | time |
+----+------+----------+----------+------+
| 1 | a | 1 | 1 | 1 |
| 2 | a | 0 | 1 | 1 |
| 3 | a | 1 | 2 | 2 |
| 4 | b | 1 | 2 | 2 |
| 5 | a | 1 | 1 | 2 |
+----+------+----------+----------+------+
5 rows in set (0.01 sec)
time column means month(the actual is timestamp).
l need to statistics total number those grade1 >=1 && grade2 >=1 in every month
So, l want to get the result like this
+----+------+----------+----------+----------+----------+------+
| id | name | grade1_m1| grade2_m1| grade1_m2| grade2_m2| time |
+----+------+----------+----------+----------+----------+------+
| 13 | a | 1 | 2 | null | null | 1 |
| 14 | a | null | null | 2 | 2 | 2 |
| 15 | b | null | null | 1 | 1 | 2 |
+----+------+----------+----------+----------+----------+------+
3 rows in set (0.00 sec)
fake code of sql seem like this:
select
count(grade1 where time=1 and grade1 >= 1) as grade1_m1,
count(grade2 where time=1 and grade2 >= 1) as grade1_m1,
count(grade1 where time=2 and grade1 >= 1) as grade1_m2,
count(grade2 where time=2 and grade2 >= 1) as grade1_m2,
-- ... 12 months' statistics
from test
group by name
In the fact, l done it, but with temporary table like follow:
select
count(if(m1.grade1>=1, 1, null)) as grade1_m1,
count(if(m1.grade2>=1, 1, null)) as grade2_m1,
count(if(m2.grade1>=1, 1, null)) as grade1_m2,
count(if(m2.grade2>=1, 1, null)) as grade2_m2,
-- ...
from test
left join
(select * from test where time = 1) as m1
on m1.id = test.id
left join
(select * from test where time = 1) as m2
on m2.id = test.id
-- ...
group by name
But this sql is toooooooo long. this test table is just a simple version. Under real situation, l printed my sql and that took up two screens in chrome. So l am seeking a more simple way to complete it
You're original version is almost there. You need case and sum() is more appropriate:
select name,
sum(case when time=1 and grade1 >= 1 then grade1 end) as grade1_m1,
sum(case when time=1 and grade2 >= 1 then grade2 end) as grade2_m1,
sum(case when time=2 and grade1 >= 1 then grade1 end) as grade1_m2,
sum(case time=2 and grade2 >= 1 then grade2 end) as grade2_m2,
-- ... 12 months' statistics
from test
group by name

Sum last record in a specific index

I can't write a query that works fine. If someone can help me, i'm thankful.
There is a table with multiple records of a specific index. I need to sum the last one of each record.
Table example:
+--------+--------+--------+
| id_cop |id_comp | value |
+--------+--------+--------+
| 1 | 1 | 1000 |
| 1 | 1 | 2000 |
| 1 | 1 | 2200 |
| 2 | 2 | 1100 |
| 3 | 2 | 3000 |
| 3 | 2 | 3400 |
| 4 | 3 | 4000 |
| 5 | 4 | 1100 |
+--------+--------+--------+
I need to sum only the last record of each id_cop index WHERE id_comp <= 4.
In this case, the result should be 2200 + 1100 + 3400 + 4000 + 1100.
Any clues?
Thanks.
Please give it a try:
SELECT
SUM(t.`value`) AS summedValue
FROM
(
SELECT
id_cop,
`value`,
id_comp,
IF(#cop_id = id_cop, #rank := #rank + 1, #rank := 0) rankNumber,
#cop_id := id_cop
FROM coptable , (SELECT #cop_id := -1, #rank := 0) var
WHERE id_comp <= 4
ORDER BY id_cop , created_at DESC
) AS t
WHERE t.rankNumber = 0
GROUP BY t.rankNumber;
SQL FIDDLE DEMO
Note: Here I've used table name as coptable. Replace coptable by yours.

SQL - Query multiple rooms with different Adults/Children per hotel

I have a simple query where I select available x Rooms with x Adults + x Children per hotel that matches a date range, but I'm having a hard time trying to figure out how to query a list of rooms per hotel like this:
1 Room with 2 Adults / 0 Children
1 Room with 4 Adults / 2 Children
1 Room with 2 Adults / 1 Children
Here is my query:
SELECT COUNT(pl.day) AS Days,
p.property_ID AS Hotel_ID,
p.name AS Hotel_Name,
r.room_name AS Room_Name,
r.room_type_ID AS Room_ID
FROM property p
INNER JOIN room_type r ON p.property_ID=r.property_ID
AND (r.max_adults >= 3
AND r.max_children >= 0)
INNER JOIN plan pl ON pl.room_type_ID=r.room_type_ID
AND (pl.day >= "2014-07-07"
AND pl.day <= "2014-07-11")
GROUP BY Room_ID,
Hotel_ID HAVING Days = 4
EDIT
How do I add 'No_of_Room' in SELECT that differentiates the room_types by the room number, example result of a single room:
Array
(
[Room_Price] => 160.00
[Days] => 4
[Hotel_ID] => 1
[Hotel_Name] => Hotel Alfa
[Room_Name] => Room type C
[Room_ID] => 3
[Max_Adults] => 3
[Max_Children] => 1
[No_of_Room] => 1 // What number of room does this room_type belongs to
)
Then I can show the results like:
EDIT
Rooms table
Rooms(
ID,
hotel_id
room_name,
max_Adults,
max_Children
);
-- Populate
INSERT INTO Rooms VALUES (1,1,"Room A",2,1),(2,1,"Room B",2,5),(3,1,"Room C",3,0);
INSERT INTO Rooms VALUES (1,2,"Room A",2,1),(2,2,"Room B",2,5),(3,3,"Room C",3,4);
EXAMPLES OF USING VIEWS TO MAKE THINGS NICER.
For this project authors may have aliases, for example one book may have "S. Lang" as the author, another might have "Serge Lang", the primary author is the main form (Serge Lang) and the secondaries are things like "S. Lang".
It is important to relate these, ideally I'd like a table with "AuthorId" and "PrimaryAuthorId" as columns, that way I could just select PrimaryAuthorId from it on AuthorId being equal to something.
To do this the view is defined as:
select
`BookSystem_AuthorList`.`AuthorId` AS `AuthorId`,
if((`BookSystem_AuthorList`.`duplicateOf` = 0),
`BookSystem_AuthorList`.`AuthorId`,
`BookSystem_AuthorList`.`duplicateOf`
) AS `PrimaryAuthorId`
from `BookSystem_AuthorList`;
Then
SELECT PrimaryAuthorId FROM BookSystem_PrimaryAuthorId WHERE AuthorId=10;
gives:
7
Which is much nicer for joining!
I then use this view to define another view (EditionAuthorsWithPrimaryId) - this gets the authors of an edition - and the primary author (I can then join to get names as needed)
select
`BookSystem_EditionAuthors`.`BindingId` AS `BindingId`,
`BookSystem_EditionAuthors`.`EditionId` AS `EditionId`,
`BookSystem_EditionAuthors`.`AuthorId` AS `AuthorId`,
`BookSystem_EditionAuthors`.`Position` AS `Position`,
(select
`BookSystem_PrimaryAuthorId`.`PrimaryAuthorId`
from `BookSystem_PrimaryAuthorId`
where (`BookSystem_PrimaryAuthorId`.`AuthorId` = `BookSystem_EditionAuthors`.`AuthorId`)
) AS `PrimaryAuthorId`
from `BookSystem_EditionAuthors`;
Now I can do:
SELECT * FROM BookSystem_EditionAuthorsWithPrimary WHERE EditionId=10;
BindingId, EditionId, AuthorId, Position, PrimaryAuthorId
10, 10, 10, 0, 7
Much nicer!
this next query is a great example
select
`BookSystem_BookList`.`BookId` AS `Id`,
`BookSystem_BookList`.`Title` AS `Name`,
`BookSystem_BookList`.`UserId` AS `UserId`,
`BookSystem_BookList`.`BookType` AS `Subtype`,
1 AS `IsBook`,0 AS `IsSeries`,
0 AS `IsAuthor`
from `BookSystem_BookList`
union
select
`BookSystem_SeriesList`.`SeriesId` AS `Id`,
`BookSystem_SeriesList`.`SeriesName` AS `Name`,
`BookSystem_SeriesList`.`UserId` AS `UserId`,
'' AS `Subtype`,
0 AS `IsBook`,
1 AS `IsSeries`,
0 AS `IsAuthor`
from `BookSystem_SeriesList`
union
select
`BookSystem_AuthorList`.`AuthorId` AS `Id`,
concat(
`BookSystem_AuthorList`.`AuthorSurname`,', ',`BookSystem_AuthorList`.`AuthorForename`,
ifnull(
(select concat(
' (AKA: ',
group_concat(
concat(
`BookSystem_AuthorList`.`AuthorSurname`,
', ',
`BookSystem_AuthorList`.`AuthorForename`
) separator '; '
),')'
) AS `AKA` from `BookSystem_AuthorList`
where
(`BookSystem_AuthorList`.`duplicateOf` = `Id`)
group by (`BookSystem_AuthorList`.`duplicateOf` = `Id`)
),'')) AS `Name`,
`BookSystem_AuthorList`.`UserId` AS `UserId`,
'' AS `SubType`,
0 AS `IsBook`,
0 AS `IsSeries`,
1 AS `IsAuthor`
from `BookSystem_AuthorList`
where (`BookSystem_AuthorList`.`duplicateOf` = 0) order by `Name`;
IS HUGE!
But now I can get all the things for UserId=1 easily:
mysql> SELECT * FROM BookSystem_Index WHERE UserId = 1;
+----+----------------------------------------+--------+-------------+--------+----------+----------+
| Id | Name | UserId | Subtype | IsBook | IsSeries | IsAuthor |
+----+----------------------------------------+--------+-------------+--------+----------+----------+
| 4 | A First Course in Calculus | 1 | Normal | 1 | 0 | 0 |
| 2 | A First Course in Real Analysis | 1 | Normal | 1 | 0 | 0 |
| 2 | Algebra | 1 | | 0 | 1 | 0 |
| 13 | Analysis II assignments | 1 | Assignments | 1 | 0 | 0 |
| 14 | Author Test | 1 | Normal | 1 | 0 | 0 |
| 8 | b, g | 1 | | 0 | 0 | 1 |
| 7 | b, g (AKA: t, lll; Teal, lll) | 1 | | 0 | 0 | 1 |
| 1 | Calculus of Several Variables | 1 | Normal | 1 | 0 | 0 |
| 4 | DuBois, Paul | 1 | | 0 | 0 | 1 |
| 1 | Lang, Serge (AKA: Lang, S. E. R. G. E) | 1 | | 0 | 0 | 1 |
| 5 | Linear Algebra | 1 | Normal | 1 | 0 | 0 |
| 3 | Morrey, C. B. | 1 | | 0 | 0 | 1 |
| 6 | MySQL | 1 | Normal | 1 | 0 | 0 |
| 7 | Principles of Mathematical Analysis | 1 | Normal | 1 | 0 | 0 |
| 2 | Protter, M. H. | 1 | | 0 | 0 | 1 |
| 5 | Rudin, Walter | 1 | | 0 | 0 | 1 |
| 10 | t | 1 | Normal | 1 | 0 | 0 |
| 3 | Test | 1 | | 0 | 1 | 0 |
| 12 | Test 1 | 1 | Normal | 1 | 0 | 0 |
| 11 | Test 4.4.2014 | 1 | Normal | 1 | 0 | 0 |
| 8 | Topology and Analysis | 1 | Normal | 1 | 0 | 0 |
| 3 | Undergraduate Algebra | 1 | Normal | 1 | 0 | 0 |
| 1 | Undergraduate Texts in Mathematics | 1 | | 0 | 1 | 0 |
| 9 | w | 1 | Normal | 1 | 0 | 0 |
+----+----------------------------------------+--------+-------------+--------+----------+----------+
24 rows in set (0.00 sec)
The optimiser sees the view properly, it wont generate the full view, it effectively substitutes the required selects.
(Taken from a testing DB, not production, hence weird names like "TESTING")
First, the room type selection needs to be framed correctly. The following join would probably work.
EDIT:
The query has been edited to return only properties with all three room types. It has also been joined with the plan table.
SELECT
COUNT(pl.day) AS Days,
p.property_ID AS Hotel_ID,
p.name AS Hotel_Name,
r.room_name AS Room_Name,
r.room_type_ID AS Room_ID,
r.max_adults as Max_Adults,
r.max_children as Max_Children
FROM property p
INNER JOIN room_type r
ON p.property_ID=r.property_ID
INNER JOIN plan pl
ON pl.room_type_ID=r.room_type_ID
AND (pl.day >= '2014-07-07' AND pl.day <= '2014-07-11')
WHERE EXISTS
(SELECT 1
FROM room_type r1
WHERE p.property_ID=r1.property_ID
AND r1.max_adults = 2 AND r1.max_children = 0)
AND EXISTS
(SELECT 1
FROM room_type r2
WHERE p2.property_ID=r2.property_ID
AND r2.max_adults = 4 AND r2.max_children = 2)
AND EXISTS
(SELECT 1
FROM room_type r3
WHERE P.PROPERTY_ID=R3.PROPERTY_ID
AND r3.max_adults = 2 AND r3.max_children = 1)
GROUP BY
p.property_ID,
p.name,
r.room_name,
r.room_type_ID,
r.max_adults,
r.max_children
HAVING
COUNT(pl.day) = 4;

MySQL Nested SQL with limits

I know it's not possible to use limits within nested INs, but I think there is a way to do this, I'm just not sure how.
I have a table that contains both ratings and comments (simplified for explanation)
mySingleTable:
+----+------------------+-----------+-----------+-----------------+
| id | reviewer_comment | is_rating | parent_id | reviewer_rating |
+----+------------------+-----------+-----------+-----------------+
| 1 | well done rateA | 1 | 0 | 5 Stars |
| 2 | commentAonRateA | 0 | 1 | |
| 3 | commentBonRateA | 0 | 1 | |
| 4 | commentConRateA | 0 | 1 | |
| 5 | commentDonRateA | 0 | 1 | |
| 6 | commentEonRateA | 0 | 1 | |
| 7 | commentFonRateA | 0 | 1 | |
| 8 | well done rateB | 1 | 0 | 4 Stars |
| 9 | well done rateC | 1 | 0 | 5 Stars |
| 11 | well done rateD | 1 | 0 | 3 Stars |
| 12 | well done rateE | 1 | 0 | 2 Stars |
| 13 | well done rateF | 1 | 0 | 5 Stars |
| 14 | well done rateG | 1 | 0 | 3 Stars |
| 15 | commentAonRateD | 0 | 11 | |
+----+------------------+-----------+-----------+-----------------+
So,
if is_rating = 1, its a rating.
if is_rating = 0, its a comment on the rating (its parent rating is where parent_id=id)
so this would look like:
well done rateA *****
commentAonRateA
commentBonRateA
commentConRateA
commentDonRateA
commentEonRateA
commentFonRateA
well done rateB ****
well done rateC *****
well done rateD ***
commentAonRateD
well done rateE **
well done rateF *****
well done rateG ***
What I want to do is select the newest five ratings, with the ASSOCIATED newest 5 comments, using only 1 query
So, some how join these two:
SELECT ratings.*
FROM mySingleTable as ratings
WHERE
is_rating = 1
ORDER BY timestamp DESC LIMIT 0, 5
SELECT comments.*
FROM mySingleTable as comments
Where
comments.parent_id = ratings.id
AND is_rating = 0
ORDER BY timestamp DESC LIMIT 0, 5
The 2nd query needs to somehow know about the ratings query
Please try this query which is now simplified and tested.
SELECT *
FROM
(SELECT *,
IF (group_id = #prev,
#n := #n + 1,
#n := 1 AND #prev := group_id) as position
FROM (
SELECT mySingleTable.*, group_id
FROM mySingleTable
INNER JOIN
(SELECT id AS group_id
FROM mySingleTable
WHERE is_rating = 1
ORDER BY timestamp DESC LIMIT 0, 5
) AS top5ratings
ON mySingleTable.id = group_id OR mySingleTable.parent_id = group_id
ORDER BY group_id DESC,
mySingleTable.parent_id ASC,
timestamp DESC) AS all_reviews
JOIN (SELECT #n := 0, #prev := 0) AS setup) AS reviews
WHERE reviews.position < 7
Keep in mind that SELECT * is bad practice. I used it to simplify reading.
TRY THIS QUERY
SELECT comments.*
FROM
(
SELECT ratings.id
FROM mySingleTable as ratings
WHERE
is_rating = 1
ORDER BY timestamp DESC LIMIT 0, 5 ) AS Top5
INNER JOIN mySingleTable AS comments ON comments.parent_id = Top5.id
WHERE comments.is_rating = 0
ORDER BY comments.timestamp DESC LIMIT 0, 5