MySQL is not using new data - mysql

The MariaDB / MySQL event scheduler does not use new data. It only works on data that was in the table at the time of creating the event. How do I make it use whatever is in the table at the time?
CREATE EVENT IF NOT EXISTS my_calculation
ON SCHEDULE EVERY 5 SECOND STARTS '2021-01-01 00:00:00'
DO
SET #c = (SELECT COUNT(*) FROM my_table);
SET #rownum = 0;
UPDATE my_table
SET rank = (100 / #c) * (#rownum:= 1 + #rownum)
ORDER BY another_column DESC LIMIT 100000;

its because in your event query you have this line :
ORDER BY progress DESC LIMIT 100000;
So it always process only 100000 first rows that are ordered by progress column , remove LIMIT 100000 and you will be fine.

Related

Minus the values of 2 specific rows in MySQL

Im working on calculating through MySQL. The next values are day values and now I want to see what the difference is between the last row and 1 day (= 96 quarters) earlier. The line below is for the last and newest value:
SET #now = (SELECT * FROM solar.measurements WHERE `tag` LIKE '%18223.Inepro_Total_Forward_kWh[3]%' ORDER BY `measurements`.`timestamp` DESC LIMIT 1);
The next line is the value of 1 day ago. This is the same as 96 quarters.
SET #yesterday = (SELECT * FROM solar.measurements WHERE `tag` LIKE '%18223.Inepro_Total_Forward_kWh[3]%' ORDER BY `measurements`.`timestamp` DESC LIMIT 95,1);
Now I want to minus the newest value with the value 1 day ago so I know the difference between those values. The code below is what ive created right now:
CREATE EVENT Value_Test
ON SCHEDULE EVERY 15 MINUTE
STARTS '2017-08-20 16:36:00'
DO
SET #now = (SELECT * FROM solar.measurements WHERE `tag` LIKE '%18223.Inepro_Total_Forward_kWh[3]%' ORDER BY `measurements`.`timestamp` DESC LIMIT 1);
SET #yesterday = (SELECT * FROM solar.measurements WHERE `tag` LIKE '%18223.Inepro_Total_Forward_kWh[3]%' ORDER BY `measurements`.`timestamp` DESC LIMIT 95,1);
SET #value = (#now - #yesterday);
INSERT INTO solar.measurements (nad, tag, value, timestamp) VALUES (18223, 'c18223.ValueDayTest', #waarde, CURRENT_TIMESTAMP());
When I want to execute this I get the following error: Error Code: 1048. Column 'value' cannot be null. Can someone help me by finding a solution? Thanks!
EDIT:
Answer to a question
Day Ago
Today
A SHOW CREATE EVENT Value_Test would show you that your event wasn't done properly.
Try this:
Delimiter //
CREATE EVENT Value_Test
ON SCHEDULE EVERY 15 MINUTE
STARTS '2018-08-20 16:36:00'
DO
BEGIN
SET #now = (SELECT value FROM solar.measurements WHERE `tag` LIKE
'%18223.Inepro_Total_Forward_kWh[3]%' ORDER BY `measurements`.`timestamp` DESC
LIMIT 1);
SET #yesterday = (SELECT value FROM solar.measurements WHERE `tag` LIKE
'%18223.Inepro_Total_Forward_kWh[3]%' ORDER BY `measurements`.`timestamp` DESC
LIMIT 95,1);
SET #value = (#now - #yesterday);
INSERT INTO solar.measurements (nad, tag, value, timestamp) VALUES (18223,
'c18223.ValueDayTest', #waarde, CURRENT_TIMESTAMP());
END
//
Delimiter ;
In case of the need to use compound statements inside an EVENT you need to put them under Begin/End

SQL: get only sampled data from large dataset

So I get a large amount of data from server using this SQL:
SELECT value,DATE_FORMAT(`time`,'%Y-%m-%dT%H:%i:%sZ') AS `time`
FROM history WHERE :id=reference AND
(time BETWEEN :start AND :end) ORDER BY time LIMIT 100 ";
Limit is set to fixed 100 entries.
But in given time range there could be 5 000 entries.
Here's my goal: I want to sample these entries by time between each entry.
So for example this interval between each entry will be 60 seconds (let's say it is parameter), then I will receive 100 entries (from 5000), but there will be always one minute difference between each one of them.
E.g.
value1,14:40:40
value2,14:41:40
...
value100,16:20:40
Is this doable via SQL? Or do I have to parse through this large data with PHP?
If it is not doable just with SQL, is it possible to get this 100 entries equally spread across this 5000 entries? (so not by time, but I'd get fixed entry id1,id50,id100,id150,...,id5000). Again just with sql.
Thanks!
Just as Kristof sais in his answer: Order the rows and take each nth row by applying a row number. This is how it is done in MySQL:
select
rows.value,
date_format(rows.`time`,'%Y-%m-%dT%H:%i:%sZ') AS `time`
from
(
select
#row_number := #row_number + 1 as row_number,
history.*
from history
cross join (select #row_number := 0) as t
where reference = :id and `time` between :start and :end
order by `time`
) as rows
cross join
(
select count(*) as cnt
from history
where reference = :id and `time` between :start and :end
) as rowcount
where mod(rows.row_number - 1, ceil(rowcount.cnt / 100)) = 0;
And this is how the same would look in another dbms, Oracle for instance, using analytic functions:
select
rows.value,
to_char(rows."time",'yyyy-mm-dd hh24:mi:ss') AS "time"
from
(
select
row_number() over (order by "time") as rown,
count(*) over () as cnt,
history.*
from history
where reference = :id and "time" between :start and :end
) rows
where mod(rows.rown - 1, ceil(rows.cnt / 100)) = 0;
These queries result in 100 records or a little less, depending on how many rows the table contains exactly. You can also use TRUNCATE(rowcount.cnt,0) instead of CEIL(rowcount.cnt) in MySQL, thus getting hundred rows or a little more and additionally apply LIMIT 100 to get exactly 100 rows (provided there are at least 100 rows in the table).
What you could is select the rowNumber and calculate the modulo of that rowNumber.
Not sure how it would be done in mysql but t-sql goes like this :
SELECT ROW_NUMBER() over( order by idField) % 50 as selector, *
FROM history
WHERE selector = 1
This will count the rows and reset the counter every 50th record, giving you a spread out result.

SQL: Skip entries in an order without knowing total entry amount

The title is a bit confusing, but I'm wondering if there is a way to do a query like this:
SELECT * FROM table ORDER BY timestamp LIMIT 10
and then only take the ones after the 10th one (or none if there are less than or equal to 10 entries).
EDIT I guess another way to do this would be to order them by timestamp, descending, and then somehow limit to 0, (total-someNumber).
By specifying an OFFSET you can get the rows after a specified number. You combine this with limit.
In MySQL you achieve this with LIMIT [offset], limit.
Example - get 10 records after the oldest 10 records:
SELECT * FROM table ORDER BY timestamp LIMIT 10, 10; # Retrieve rows 11-20
Example - get 20 records after the newest 5 records:
SELECT * FROM table ORDER BY timestamp DESC LIMIT 5, 20; # Retrieve rows 6-25
If you want to get ALL rows after a certain number (eg. 10) then you pass an arbitrarily big number for the limit since it is required by the clause:
SELECT * FROM table ORDER BY timestamp LIMIT 10,18446744073709551615; # Retrieve rows 11-BIGINT
Note: 18446744073709551615 is the maximum of an unsigned BIGINT and is provided as the solution within the MySQL documentation.
See:
http://dev.mysql.com/doc/refman/5.5/en/select.html
I'd try something like this and then just add a where clause that skips the first n (n=10 in this case) rows.
i.e. using the linked example:
SELECT
*
FROM
(select #n := #n + 1 RowNumber, t.* from (select #n:=0) initvars, tbl t)
WHERE
RowNumber > 10

Simple query taking long time in MySQL

I must be overseeing something, but I have the following query:
SELECT
`Poster`, Round(Sum(If((`Date`>=DATE_ADD(CURDATE(),INTERVAL -1 Month) And `Date`<CURDATE()),1,0))/DATEDIFF(CURDATE(),DATE_ADD(CURDATE(),INTERVAL -1 Month)),0) AS `statistics`
FROM `forenposts`
GROUP BY `Poster`
ORDER BY `statistics` DESC
LIMIT 5
This takes roughly 15 seconds in a database with more than 1.5 million entries.
Is there an easy way to optimize it or is the If function just taking so long?
Can you try this query? I removed some constant calculations out of the query, as operation with every row tends to be very slow. Also, I would use constant, not CURDATE() function inside the query, so DB server can optimise it. I can't try it, let me know if it works and I can explain it with more details.
SET #CURDATE = CURDATE();
SET #DATEADD = DATE_ADD(#CURDATE,INTERVAL -1 Month);
SET #DATEDIFF = DATEDIFF(#CURDATE,#DATEADD);
SELECT
`Poster`,
Round(Sum(If((`Date`>=#DATEADD And `Date`< #CURDATE), 1, 0)) / #DATEDIFF,0) AS `statistics`
FROM `forenposts`
GROUP BY `Poster`
ORDER BY `statistics` DESC
LIMIT 5

Rotate table data with out update data

SELECT * FROM `your_table` LIMIT 0, 10
->This will display the first 1,2,3,4,5,6,7,8,9,10
SELECT * FROM `your_table` LIMIT 5, 5
->This will show records 6, 7, 8, 9, 10
I want to Show data 2,3,4,5,6,7,8,9,10,1 and
next day 3,4,5,6,7,8,9,10,1,2
day after next day 4,5,6,7,8,9,10,1,2,3
IS IT POSSIBLE with out updating any data of this table ???
You can do this using the UNION syntax:
SELECT * FROM `your_table` LIMIT 5, 5 UNION SELECT * FROM `your_table`
This will first select rows within your limit, and then combine the remainder from the second select. Note that you don't need to set a limit on the second select statement:
The default behavior for UNION is that duplicate rows are removed from the result. The optional DISTINCT keyword has no effect other than the default because it also specifies duplicate-row removal. With the optional ALL keyword, duplicate-row removal does not occur and the result includes all matching rows from all the SELECT statements.
I don't think this might be achieved using a simple Select (I may be wrong). I think you'll need a stored procedure.
You've tagged this as Oracle, though your SQL syntax would be invalid for Oracle because it doesn't support LIMIT
However, here's a solution that will work in Oracle:
select *
from ( select rownum as rn,
user_id
from admin_user
order by user_id
) X
where X.rn > :startRows
and X.rn <= :startRows + :limitRows
order by case when X.rn <= :baseRef
then X.rn + :limitRows
else
X.rn
end ASC
;
where :startRows and :limitRows are the values for your LIMIT, and :baseRef is a value between 0 and :limitRows-1 that should be incremented/cycled on a daily basis (ie on day 1 it should be 0; on day 2, 1; on day 10, 9; on day 11 you should revert to 0). You could actually use the current date, converted to Julian and take the remainder when divided by :limitRows to automate calculating :baseRef
(substitute your own column and table names as appropriate)
Well, it might be a little bit late for the author of the question, but could be useful for people.
Short answer: It is possible to do the "spin" like author asked.
Long answer: [I'm going to explain for MySQL first - where I tested this]
Let's imagine that we have table your_table (INT rn, ...). What you want is to sort in specific way ("spin" with beginning at the rn=N). First condition of ordering is rn >= N desc. The idea (at least how I understand this) is we change the order from asc to desc and split our table in two parts (<N and >=N). Then we order this back by rn but asc order. It will execute sorting for each group independently. So here is our query:
select * from your_table where rn between 1 and 10
order by rn >= N desc, rn asc;
If you don't have rn column - you always can use the trick with parameter
select t.*, #rownum := #rownum + 1 AS rn
from your_table t,
(SELECT #rownum := 0) r
where #rownum < 10 /* here be careful - we already increased by 1 the rownum */
order by #rownum >=N - 1 desc, /* another tricky place (cause we already increased rownum) */
#rownum asc;
I don't know if the last one is efficient, though.
For Oracle, you always can use rownum. And I believe that you will have the same result (I didn't test it!).
Hope it helps!