I have a table:
value
updated_at
ID
5
2022-1-1 12:00:00
1
10
2022-1-1 12:00:30
2
20
2022-1-1 12:02:30
3
What I want to do is to get an average based on the updated_at column difference, and the values of course.
So, I guess the formula should be:
(sumof((value2 - value1) * (date2 - date1))) / (dateLast - dateFirst) where 1 and 2 means for each two rows when we traverse from the first to the last item. eg for this table we'll have:
First and second row: (value2 - value1) * (date2 - date1) = (10 - 5) * (30 (seconds)) = 150
for second and third row: (20 - 10) * 120 = 1200
So the result is:
(1200 + 150) / (2022-1-1 12:02:30 - 2022-1-1 12:00:00) = 9
I probably can get this working with a self JOIN on ID and ID + 1 and I also can do the diff of last and first date, but I can't do them both in the same query! I have no idea how to do that, is this even possible to be done in a single query?
Update
My MySql version is 5.6
For MySql 8.0+ you can use LAG() window function to get each row's previous values and then aggregate:
WITH cte AS (
SELECT *,
value - LAG(value) OVER (ORDER BY updated_at) dif_value,
UNIX_TIMESTAMP(updated_at) - UNIX_TIMESTAMP(LAG(updated_at) OVER (ORDER BY updated_at)) dif_time
FROM tablename
)
SELECT SUM(dif_value * dif_time) /
(UNIX_TIMESTAMP(MAX(updated_at)) - UNIX_TIMESTAMP(MIN(updated_at))) result
FROM cte;
For previous versions and if there are no gaps between the ids, use a self join:
SELECT SUM(dif_value * dif_time) /
(UNIX_TIMESTAMP(MAX(updated_at)) - UNIX_TIMESTAMP(MIN(updated_at))) result
FROM (
SELECT t1.*,
t1.value - t2.value dif_value,
UNIX_TIMESTAMP(t1.updated_at) - UNIX_TIMESTAMP(t2.updated_at) dif_time
FROM tablename t1 LEFT JOIN tablename t2
ON t1.ID = t2.ID + 1
) t;
See the demo.
Related
Hi I have a trouble with SQL UPDATE. I want to insert random unique number from 0-2 to column draft in table draft (where column lobby is 1).
I have this, but it ends with error: #1093 - You can't specify target table 'draft' for update in FROM clause
UPDATE draft
SET draft = (
SELECT FLOOR(RAND() * 3) AS random_num
WHERE "random_num" NOT IN (
SELECT draft FROM draft
)
)
WHERE lobby = 1
RAND range will be generated dynamic on app level so the result should be like this: 3 records = unique random 0-2, 9 records = unique numbers from 0-8 etc.
Start
ID DRAFT LOBBY
1 null 1
2 null 1
3 null 1
Result
ID DRAFT LOBBY
1 1 1
2 2 1
3 0 1
Any help please?
Edit
I updated the query to this:
UPDATE draft
SET draft = (
SELECT FLOOR(RAND() * 3) AS random_num
WHERE "random_num" NOT IN (SELECT draft FROM (SELECT * FROM draft) AS temp)
)
WHERE lobby = 1
No syntax error apears, but doesnt change any row, dont know why.
You could do:
update draft d cross join
(select n.*
from (select 0 as n union all select 1 as n union all select 2) n
where n.n not in (select d2.draft from draft d2)
order by rand()
limit 1
) n
set d.random_number = n.n
where d.lobby = 1
limit 1;
Note the limit 1. This allows you to update one row at a time. If multiple rows meet the condition, then you should repeat the update with appropriate filtering criteria.
In your query, you probably think that this:
WHERE "random_num" NOT IN...
compares the column random_num against the values after IN, but this is not the case.
Why? Because a computed column like random_num cannot be used in the WHERE clause
What it is doing is comparing the string "random_num" which is converted to 0 in order to perform the comparison.
This will work if the ids are (like your sample data) 3 consecutive integers:
update draft d cross join (
select group_concat(t.x order by rand() separator '') col
from (select 0 x union all select 1 union all select 2) t
) r
set d.draft = substr(r.col, d.id % 3 + 1, 1)
where d.lobby = 1;
The column draft will be updated with 3 unique random integers in the range 0-2.
See the demo.
I want to use a selected row on mysql like this.
I have a query about value.
Select maxValue from tableOne
And i want to use this value on my query like that.
Select (Select maxValue from tableOne where tableTwo.Aa=tableOne.Aa LIMIT 1) as maxValue,CASE tableTwo.Param WHEN 1 Then maxValue -50
When 2 Then maxValue-100
When 3 Then maxValue-400
When 4 Then maxValue-500
ELSE
maxValue
END ,
from tableTwo
How can i use like this ?
One way to do this is
Select
CASE tableTwo.Param
WHEN 1 Then t.maxValue - 50
When 2 Then t.maxValue - 100
When 3 Then t.maxValue - 400
When 4 Then t.maxValue - 500
ELSE t.maxValue
END
from tableTwo
JOIN
(
SELECT
Aa
, MAX(maxValue)
FROM tableOne
GROUP BY Aa
) t
ON t.Aa = tableTow.Aa
You need one column, so you don't need to join, just select from table that has the column:
(Select maxValue from tableOne where tableTwo.Aa=tableOne.Aa LIMIT 1)
should be
(Select maxValue from tableWithMaxValue LIMIT 1)
and if you want to join tables, you can do this in outer query, so the whole query becomes:
SELECT CASE tableTwo.Param WHEN 1 Then maxValue -50
WHEN 2 Then maxValue-100
WHEN 3 Then maxValue-400
WHEN 4 Then maxValue-500
ELSE maxValue END
FROM tableOne t1
JOIN tableTwo t2 ON t1.Aa = t2.Aa
LIMIT 1;
I have a table as follows:
id - rowid - value - date
and in each row I have:
1 - 9 - 123 - 03/2013
2 - 10 - 456 - 03/2013
I want to join both rows into one table like this:
id - rowid - value1 value2 - date
1 - 9 - 123 - 456 - 03/2013
I only need from the first table, the rowid 9 as in the example and the value and date.
From the second row I only need the value. I tried union all and multiple selection but with no success effort.
Help would be highly appreciated. Thanks in advance!
--Assumes there are ONLY 2 records in table with same date.
--If not accurate you will end up with only 1 less record per date than you currently have.
--Assumes you only what the lowest RowId recorded.
create table myNewtable as
(SELECT A.RowID, A.Value, B.Value, A.Date
FROM Oldtable A INNER JOIN Oldtable B
on A.Date=B.Date
and A.RowID < B.RowId)
--Now that we know date is date time and is likely different...
but that RowID is always +1 for the ones you want to join
create table myNewtable as
(SELECT A.RowID, A.Value, B.Value, A.Date
FROM Oldtable A
INNER JOIN Oldtable B
on A.RowID = B.RowId-1)
I have the following table:
CREATE TABLE dummy (
thousand INT(10) UNSIGNED,
UNIQUE(thousand)
);
Is there sql syntax I can use to insert every thousandth positive integer, starting from 1 and up until 1 million? I can achieve this in php, but I was wondering if this was possible without using a stored procedure.
thousand
1
1001
2001
3001
4001
...
998001
999001
insert into dummy
( thousand )
select
PreQuery.thousand
from
( select
#sqlvar thousand,
#sqlvar := #sqlvar + 1000
from
AnyTableWithAtLeast1000Records,
( select #sqlvar := 1 ) sqlvars
limit 1000 ) PreQuery
You can insert from a select statement. Using MySQL Variables, start with 1. Then, join to ANY table in your system that may have 1000 (or more) records just to generate a row. Even though not getting any actual column from such table, we just need it for the record position. Then the #sqlvar starts at 1 and is returned in column named thousand. Then, immediately add 1000 to it for the next record in the "AnyTable..."
Unfortunately, mysql doesn't support this with any special SQL function.
You have to populate a table, which isn't such a big deal - it would only be 1000 rows.
You can also hack together a temporary table with unions, but that's hardly elegant - might as well use a table.
Other databases do support it, eg with postgres' generate_series() function, but that is little consolation.
As a side note, I often find it handy to have a table populated with consecutive numbers from 1 up to a large numebr for just such as occasion, and I would just select 1000 * num from numbers where num <= 1000.
For a one statement query, i.e. without introducing any additional tables and supporting statement, I'd use this approach in 'pseudo' SQL:
SELECT (D1.Digit + D2.Digit + D3.Digit)* 1000 + 1
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
) AS D1
CROSS JOIN
(
SELECT 0 AS Digit UNION ALL
SELECT 10 UNION ALL
SELECT 20 UNION ALL
SELECT 30 UNION ALL
SELECT 40 UNION ALL
SELECT 50 UNION ALL
SELECT 60 UNION ALL
SELECT 70 UNION ALL
SELECT 80 UNION ALL
SELECT 90
) AS D2
CROSS JOIN
(
SELECT 0 AS Digit UNION ALL
SELECT 100 UNION ALL
SELECT 200 UNION ALL
SELECT 300 UNION ALL
SELECT 400 UNION ALL
SELECT 500 UNION ALL
SELECT 600 UNION ALL
SELECT 700 UNION ALL
SELECT 800 UNION ALL
SELECT 900
) AS D3
WHERE ((D1.Digit + D2.Digit + D3.Digit)* 1000 + 1) < 1000000
I'm not 100% sure but it should run fine in mysql or perhaps require some minor change.
If you're able to reuse parts of the query, it becomes much prettier, for example in SQL Server, I'd write it as follows:
WITH Digits AS
(
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
)
SELECT (D1.Digit + D2.Digit + D3.Digit)* 1000 + 1
FROM (SELECT Digit FROM Digits) AS D1
CROSS JOIN (SELECT Digit * 10 AS Digit FROM Digits) AS D2
CROSS JOIN (SELECT Digit * 100 AS Digit FROM Digits) AS D3
WHERE ((D1.Digit + D2.Digit + D3.Digit)* 1000 + 1) < 1000000
Keep an eye on where multiplication happens, it might be more efficient to multiply in sub-queries, rather than in the resulting expression.
Here is a trick, that I usually use for those sort of problems, similar to #sergeBelov solution:
Create an anchor table, a temp table, and fill it with values from 0 to 9, like so:
CREATE TABLE TEMP (Digit int);
INSERT INTO Temp VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
Then you can do this:
INSERT INTO dummy(thousand)
SELECT 1 + (id - 1) * 1000 AS n
FROM
(
SELECT t3.digit * 100 + t2.digit * 10 + t1.digit + 1 AS id
FROM TEMP AS t1
CROSS JOIN TEMP AS t2
CROSS JOIN TEMP AS t3
) t;
SQL Fiddle Demo
How does this work?
The sequence numbers(1, 1001, 2001, ... , 998001, 999001) 1000 terms, that you are looking for, is what they called Arithmetic progression, and in your case the nth term of the sequence (an) is given by:
A + (n - 1) * d
In you sequence: a = 1, d = 1000
Where A is the first term of the sequence, n is the term and d is the difference between each two terms(it is the same for each two successive terms).
The subquery:
SELECT t3.digit * 100 + t2.digit * 10 + t1.digit + 1 AS id
FROM TEMP AS t1
CROSS JOIN TEMP AS t2
CROSS JOIN TEMP AS t3;
Will generate a list of numbers from 1 to 1000(the total number of terms in your sequence), after that we got each term in the sequence from these number by 1 + (id - 1) * 1000 in the outer select.
My records are in a temporary table having three columns :
Column1 : ID (Bigint)
Column2 : CreationDateTime (dateTime)
Column3 : Volume (Float)
The records are sorted based on CreationDateTime.
I need to pick the records from the table where Sum of Volume is equal to THRESHOLD1 and then the same for Threshold2.
One way is to add a new Column to the table which has the sum of Volume for the previous records. for example :
ID - CreationDateTime - Volume - SUM
1 - 20/07/2012 - 10 - 10
2 - 21/07/2012 - 12 - 22
3 - 22/07/2012 - 7 - 29
and then Select * from temp where Sum >= Threshold But the calculation of the sum is not the fastest way.
I was wondering if anyone can suggest a better way for doing the above.
I'm using SQL server 2008 and I can also use CLR if required.
try this solution:
you can find the running total just by self joining tables and group by
with cte as(
select T2.ID, T2.CreationDateTime,SUM(T1.Volume) [SUM]
from test_table T1 join test_table T2
on T1.id<=T2.id
group by T2.id, T2.CreationDateTime)
select * from cte where [SUM]>= Threshold
Here's an approach using a recursive CTE, which will likely be the fastest:
select #i=min(ID) from #temp
;with a as
(
select ID, Volume, Volume as RunningTotal
from #temp
where ID=#i
union all
select b.ID, b.Volume, b.Volume + a.RunningTotal as RunningTotal
from #temp b
inner join a
on b.ID=a.ID+1
)
select * from a
Some links related to running totals:
http://www.sqlusa.com/bestpractices/runningtotal/
http://www.databasejournal.com/features/mssql/article.php/3112381/SQL-Server-Calculating-Running-Totals-Subtotals-and-Grand-Total-Without-a-Cursor.htm
http://www.mssqltips.com/sqlservertip/1686/calculate-running-totals-using-sql-server-cross-joins/
http://social.msdn.microsoft.com/Forums/eu/transactsql/thread/1b4d87cb-ec77-4455-af48-bf7dae50ab87
Computed Column using a function:
create function dbo.fn_VolumeRunningTotal
{
#dt datetime
}
returns int
as
begin
declare #total int
select #total = sum(volume)
from dbo.MyVolumeTable
where CreationDateTime <= #dt
return #total
end
Computed Column formula:
dbo.fn_VolumeRunningTotal(CreationDateTime)
Select statements:
select * from dbo.MyVolumnTable where RunningTotal <= #Threshold1