Im running an Openhab2 instance and collect data aswell as timestamps for important changes in datacollection. Inside my table item45 I store 2 colums Time and Value witch looks like this.
TABLE (item45) Design Screenshot 1 Screenshot 2
Time (datetime,primary) Value (datetime)
... ...
2018-10-17 03:08:30 2018-10-17 03:08:30
2018-10-19 00:13:13 2018-10-19 00:13:13
2018-10-19 00:27:58 2018-10-19 00:27:57
Its kind of the design of how Openhab stores data so nothing i can do about that.
i now try to use these values in Grafana just as ones like this:
Example of sucessful use
My Problem lies within Grafana im using to plot the querys. It disconnects the graphs if the next older point is outside the viewport. I tried to cope with that with virutally filling up the gaps in data inside the query. Like this:
Currently used Query
SELECT
1 as value,
'Net Reset' as metric,
UNIX_TIMESTAMP(v.gen_date) AS time_sec
from
(select DATE_SUB( FROM_UNIXTIME(1539707286), INTERVAL t3*1000 + t2*100 + t1*10 + t0 HOUR) gen_date from
(select 0 t0 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0
CROSS JOIN (select 0 t1 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1
CROSS JOIN (select 0 t2 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2
CROSS JOIN (select 0 t3 union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3
) v
LEFT JOIN item45
ON DATE_FORMAT( Time, '%Y-%m-%dT%H:00:00') = v.gen_date
where $__timeFilter(v.gen_date)
ORDER BY v.gen_date DESC;
AS u can see im using one of the given "hacks" here to generate hourly fillingdata just set to ones.
I need help to implement this query:
Query to replace FROM_UNIXTIME(...)
SELECT * FROM item45 ORDER BY Time DESC LIMIT 1
into the query 1 where FROM_UNIXTIME(1539707286) to set the actual stop of filling up the future data.
Im running MySQL57-server and currently cant upgrade to version 8 to use the with clause.
Desired Output
Time Value
2018-10-18 21:00:00 1 <- Inserted
2018-10-18 22:00:00 1 <- Inserted
2018-10-18 23:00:00 1 <- Inserted
2018-10-19 00:00:00 1 <- Inserted
2018-10-19 00:13:13 1
2018-10-19 00:27:58 1
Related
is there way to include empty week value from empty result ? or how i can unionn empty missing weeks
there is bit of my query
SELECT
o.user_id , WEEK(FROM_UNIXTIME(o.cdate, '%Y-%m-%d'),7) as week_number,
FROM
(_orders AS `o`)
WHERE
o.cdate BETWEEN '1505409460' AND '1540815218'
GROUP BY
week_number
Result
1
2
4
6
8
requested result
1
2
3
4
5
6
7
8
This is just an example, there are numerous ways to achieve this. The first step is to have, or generate, a set on integers. Having a table of these is very handy actually. Here I use 2 subqueries cross joined to generate 100 rows (with n = 0 to 99)
select
ns.n, sq.*
from (
select
d1.digit + (d10.digit*10) as n
from (
SELECT 0 AS digit UNION ALL
SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL
SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL
SELECT 9
) d1
cross join (
SELECT 0 AS digit UNION ALL
SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL
SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL
SELECT 9
) d10
) ns
left join (
your query goes here
) sq on ns.n = sq.week_number
where n between 1 and 52
order by n
So, where I work, my employees work alternating Saturdays: some employees are assigned to work the 1st and 3rd Saturdays of each month, some are assigned to work the 2nd and 4th Saturdays of each month.
One small problem arises: there are four months in the year which have five Saturdays. Which is easy enough to work around: 1st/3rd Employees work the 1st and 3rd Fifth Saturdays in the year and so on.
Some years have five months with fifth Saturdays, but we're not talking about that right now.
Anyway, to generate schedules for my employees, I first use the following code to generate a list of dates:
select
curdate() - interval (a.a + (10 * b.a) + (100 * c.a)) day as Date
from (
select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
) as a
Then, I select the dates from the subquery, and join them to my Scheduling table to create a list of what each employee works on each date.
All of this works fine until Fifth Saturdays rear their ugly heads. At the moment, I use the following code to mathematically figure out which fifth saturday of the year it is:
mod(((datediff(date_add(a.date, interval (7-dayofweek(a.date)) day),date_add(subdate(a.Date,dayofyear(a.date-1)), interval (1-dayofweek(subdate(a.Date,dayofyear(a.date-1)))) day))+1)/7),4)
Or, to summarize, I find the number of days that have elapsed from the current first Sunday of the year to the current Saturday of this week, divide it by seven to figure out how many Saturdays have passed since then, and then find the modulo of 4 to figure out how many "extra" fifth Saturdays have passed since then.
...and this code will work perfectly fine unless the Fifth Saturday happens to fall on a week which is a multiple of four. Thankfully, that doesn't happen at all this year, but next year I'll need to figure out how to deal with this.
Is there a better way to mathematically figure out which fifth Saturday of the month a given fifth Saturday is?
Oh man. I was way overthinking the SQL piece of this. It was actually pretty easy. Create three temporary tables: one to hold the completed date table, one to hold all of the dates of interest in your period, and a third table specifically to find Fifth Saturdays:
drop temporary table if exists DATES;
create temporary table DATES (AdherenceDates date, WhichSaturday int,
WhichFifthSaturday int);
create temporary table DATES1 (AdherenceDates date, WhichSaturday int);
create temporary table DATES2 (FifthSaturdayDates date, WhichFifthSaturday
int auto_increment, primary key (WhichFifthSaturday));
insert into DATES1 (AdherenceDates, WhichSaturday)
select
a.Date as AdherenceDates,
case
when day(adddate(a.date,(7-dayofweek(a.date)))) <= 7
then 1
when day(adddate(a.date,(7-dayofweek(a.date)))) <= 14
then 2
when day(adddate(a.date,(7-dayofweek(a.date)))) <= 21
then 3
when day(adddate(a.date,(7-dayofweek(a.date)))) <= 28
then 4
when day(adddate(a.date,(7-dayofweek(a.date)))) > 28
then 5
end as WhichSaturday
from
(select
curdate() - interval (a.a + (10 * b.a) + (100 * c.a) + (1000 * d.a)) day
as Date
from (
select 0 as a union all select 1 union all select 2 union all select 3
union all select 4 union all select 5 union all select 6 union all select 7
union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union
all select 3 union all select 4 union all select 5 union all select 6 union
all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union
all select 3 union all select 4 union all select 5 union all select 6 union
all select 7 union all select 8 union all select 9) as c
cross join (select 0 as a union all select 1 union all select 2 union
all select 3 union all select 4 union all select 5 union all select 6 union
all select 7 union all select 8 union all select 9) as d
) as a
where dayofweek(a.Date) <> 1
and a.Date >= '2017-01-01'
order by a.Date asc
;
insert into DATES2 (FifthSaturdayDates)
select
a.Date as AdherenceDates
from
(select
curdate() - interval (a.a + (10 * b.a) + (100 * c.a) + (1000 * d.a)) day
as Date
from (
select 0 as a union all select 1 union all select 2 union all select 3
union all select 4 union all select 5 union all select 6 union all select 7
union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union
all select 3 union all select 4 union all select 5 union all select 6 union
all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union
all select 3 union all select 4 union all select 5 union all select 6 union
all select 7 union all select 8 union all select 9) as c
cross join (select 0 as a union all select 1 union all select 2 union
all select 3 union all select 4 union all select 5 union all select 6 union
all select 7 union all select 8 union all select 9) as d
) as a
where dayofweek(a.Date) = 7
and a.Date >= '2017-01-01'
and day(a.Date) > 28
order by a.date asc;
insert into DATES (AdherenceDates, WhichSaturday, WhichFifthSaturday)
select
DATES1.AdherenceDates,
DATES1.WhichSaturday,
DATES2.WhichFifthSaturday
from
DATES1
left join
DATES2
on DATES1.AdherenceDates = DATES2.FifthSaturdayDates;
drop temporary tables DATES1, DATES2;
select * from DATES
order by AdherenceDates asc;
I have table shown below :
login
date user
2016-11-23 1
2016-11-23 2
2016-11-23 3
2016-11-25 2
2016-11-25 5
2016-11-27 1
from above table what I want to get is like this:
date count(*)
2016-11-21 0
2016-11-22 0
2016-11-23 3
2016-11-24 0
2016-11-25 2
2016-11-26 0
2016-11-27 1
But, because there are only dates 2016-11-23 and 2016-11-25 and 2016-11-27, when I query like this :
select date, count(*)
from login
where date between (current_date()-interval 7 day) and current_date()
group by date
order by date asc
It can't get result like what I really want to get. Is that result possible from my login table?
One way is to generate all days before JOIN
select GenDate, count(Date)
from login
right join
(select a.GenDate
from (
select curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY as GenDate
from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
) a
where a.GenDate between (current_date()-interval 7 day) and current_date())x
ON x.GenDate=login.Date
group by GenDate
order by GenDate asc
Use a derived table with the wanted dates :
SELECT t.date, count(s.date)
FROM (SELECT '2016-11-21' as `date` UNION ALL
SELECT '2016-11-22' as `date` UNION ALL
...) t
LEFT JOIN login s
ON(t.date = s.date)
WHERE
t.date between (current_date()-interval 7 day) and current_date()
GROUP BY t.date
ORDER BY t.date
This is a very well known problem in programming. There are several solutions.
Go over the result with PHP, and fill the missing days in the resulting array.
AS sagi proposed, create a separate table that contains all the dates in the range of days your application works with, then you can JOIN that table with your query. One of the issues is that from time to time you have to add more days to this table, if you suddenly have missing days in future or in past.
I have a database table containing project data with one record for each project. Below some sample data, duration is in months and is currently limited to max. 240, but can, theoretically, be infinite:
id name amount start_date duration
1 Project A 9.000 2013-06-24 3
2 Project B 5.000 2013-07-13 2
3 Project C 15.000 2013-08-06 3
Now I want MySQL to return the amount per month for each project: amount divided by duration for each month starting with the month the start_date is in. So based on the above data, MySQL will return something like below, ordered by month:
id name month amount_this_month
1 Project A 2013-06 3.000
1 Project A 2013-07 3.000
2 Project B 2013-07 2.500
1 Project A 2013-08 3.000
2 Project B 2013-08 2.500
3 Project C 2013-08 5.000
3 Project C 2013-09 5.000
3 Project C 2013-10 5.000
I saw something here for SQL Server, giving the suggestion to use a dummy table filled with numbers. Anyone has any ideas on how to do this without affecting the original database structure?
I need to do this in one query (so no temp (mem) tables).
How is this possible in MySQL? I saw some solutions for Oracle to iterate trough a (virtual) range/sequence, is it possible to do something like this in MySQL?
You can do something like this, joining a dummy range of numbers against itself to get the range of numbers, then adding that to the base month
SELECT id, name, DATE_FORMAT(DATE_ADD(start_date, INTERVAL Units.i + Tens.i * 10 + Hundreds.i * 100 MONTH), '%Y-%m') AS `month`, (amount / duration) AS `amount_this_month`
FROM SomeTable
CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) Units
CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) Tens
CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) Hundreds
WHERE (Units.i + Tens.i * 10 + Hundreds.i * 100) < duration
Before anything else, here is the simplified schema (with dummy records) of the database:
ItemList
ItemID ItemName DateAcquired Cost MonthlyDep CurrentValue
================================================================================
1 Stuff Toy 2011-12-25 100.00 10.00 100.00
2 Mouse 2011-12-23 250.00 50.00 200.00
3 Keyboard 2011-12-17 250.00 30.00 190.00
4 Umbrella 2011-12-28 150.00 20.00 110.00
5 Aircon 2011-12-29 950.00 25.00 925.00
DepreciationTransaction
ItemID DateOfDep MonthlyDep
======================================
2 2012-01-31 250.00
3 2012-01-31 30.00
4 2012-01-31 20.00
5 2012-01-31 25.00
3 2012-02-29 30.00
4 2012-02-29 20.00
I need your suggestions to help me solve this problem. Basically I am creating a depreciation monitoring system of a certain LGU. The problem of the current database is that it lacks some records for a specific date of depreciation, for instance:
Lacking Records (this is not a table from the database)
ItemID LackingDate
============================
1 2012-01-31
1 2012-02-29
2 2012-02-29
5 2012-02-29
And because of the lacking records, I cannot generate the depreciation report for the month of MARCH. Any idea how can I insert missing records on the DepreciationTransaction?
What have I done so far? None. But a simple query that calculates the newly depreciated value (which produces incorrect value because of the missing records)
The problem here is that you will have to generate data. MySQL is not intended to generate data, you should do that at an application level and just tell MySQL to store it. In this case, the application should check wether there are missing records and create them if needed.
Leaving that aside, you can (awfully) create dynamic data with MySQL like this:
select il.itemId, endOfMonths.aDate from ((
select aDate from (
select #maxDate - interval (a.a+(10*b.a)+(100*c.a)+(1000*d.a)) day aDate from
(select 0 as a union all select 1 union all select 2 union all select 3
union all select 4 union all select 5 union all select 6 union all
select 7 union all select 8 union all select 9) a, /*10 day range*/
(select 0 as a union all select 1 union all select 2 union all select 3
union all select 4 union all select 5 union all select 6 union all
select 7 union all select 8 union all select 9) b, /*100 day range*/
(select 0 as a union all select 1 union all select 2 union all select 3
union all select 4 union all select 5 union all select 6 union all
select 7 union all select 8 union all select 9) c, /*1000 day range*/
(select 0 as a union all select 1 union all select 2 union all select 3
union all select 4 union all select 5 union all select 6 union all
select 7 union all select 8 union all select 9) d, /*10000 day range*/
(select #minDate := (select min(dateAcquired) from il),
#maxDate := '2012-03-01') e
) f
where aDate between #minDate and #maxDate and aDate = last_day(aDate)
) endOfMonths, il)
left join dt
on il.itemId = dt.itemId and endOfMonths.aDate = dt.dateOfDep
where dt.itemId is null and last_day(il.dateAcquired) < endOfMonths.aDate
Depending on the length of the date range you can reduce the amount of dynamically generated results (10000 days means over 27 years of records each representing one day) by removing tables (d, c, b and a) and removing them from the upper formula. Setting the #minDate and #maxDate variables will allow you to specify the dates between you want to filter the results. This dates should be the min date from which you have an item and the max date should be march, in your case.
In plain english: If select min(dateAcquired) from il returns a date before '2012-03-01' - 10000 days then you'll have to add another union.
Finally, just add the insert statement (if you really need to insert those records).
You may build a temporary table, which contains the date needed. And use the table to LEFT OUTER JOIN the "DepreciationTransaction" table.
SELECT dt.date_value, dt.itemid, ISNULL(SUM(dt.MonthlyDep), 0)
FROM tmp_date
LEFT OUTER JOIN
DepreciationTransaction AS dt
ON tmp_date.date_value = dt.DateOfDep
GROUP BY dt.date_value, dt.itemid
Of course, if your want that all of the items to be on report, you should make a cartesian product with tmp_date and items_id.