Calculate difference between rows with counter values in SQL - sql

I got table T1 where I update some counters values
id, unix_time_stamp, counter1, counter10
1 , 1333435800 , 55 , 80
then i got table T2 where i copy those values
id, unix_time_stamp, counter1, counter10, value1, value10
1 , 1333435800 , 55 , 80 , 0 , 0
2 , 1333435801 , 60 , 87 , 5 , 7
3 , 1333435802 , 70 , 90 , 10 , 3
3 , 1333435804 , 80 , 100 , 5 , 5
this is done with some trigger function
INSERT INTO T2 (unix_time_stamp, counter1, counter10) SELECT unix_time_stamp, counter1, counter10 FROM T1 WHERE id=1
What i want is to calculate value1, value10 as a
(current_counter1 - last_counter1)/(current_time - last_time)
and put them in this insert.
for example value 1 with timestamp 1333435804 will be
value1=(80-70)/(1333435804-1333435802) = 5
other words
insert into t2
(unix_time_stamp, counter1, counter10, value1)
SELECT unix_time_stamp, counter1, counter10,
(counter1 - (select counter1 from T1 order by unix_time_stamp DESC LIMIT 1)/
(unix_time_stamp - (select unix_time_stamp from T1 order by unix_time_stamp DESC LIMIT 1)
FROM T1 WHERE id=1
but i want this in a little shorter version because i got 10 counters :)
Whole situation is little bit complicated, and i got some reason to not do this outside SQL
I am using sqlite
This is just to complicated to me :)
Please help.

Your question is a little unclear. Is this anywhere close?
DECLARE #Id int = 1
DECLARE #LastCounter1 int,
#LastCounter10 int,
#LastTime timestamp,
SELECT TOP 1 #LastCounter1 = counter1,
#LastCounter10 = counter10,
#LastTime = unix_time_stamp
FROM T2
WHERE id = #Id
ORDER BY unix_time_stamp DESC
INSERT INTO T2 (id, unix_time_stamp, counter1, counter10, value1, value10)
SELECT unix_time_stamp,
counter1,
counter10,
((counter1 - #LastCounter1) / (unix_time_stamp - #LastTime)),
((counter10 - #LastCounter10) / (unix_time_stamp - #LastTime))
Updated answer:
INSERT INTO T2 (id, unix_time_stamp, counter1, counter10, value1, value10)
SELECT T1.id,
T1.unix_time_stamp,
T1.counter1,
T1.counter10,
((T1.counter1 - [Last].counter1) / (T1.unix_time_stamp - [Last].unix_time_stamp)),
((T1.counter10 - [Last].counter10) / (T1.unix_time_stamp - [Last].unix_time_stamp))
FROM T1
INNER JOIN (
SELECT TOP 1 id,
counter1,
counter10,
unix_time_stamp
FROM T2
WHERE id = 1
ORDER BY unix_time_stamp DESC
) [Last] ON T1.id = [Last].id
WHERE T1.id = 1

I guess the following query calculates all data you need for your insert clause:
SELECT 1e0 * (cur.counter1 - prv.counter1)/(cur.unix_time_stamp - prv.unix_time_stamp) AS [value1]
, 1e0 * (cur.counter10 - prv.counter10)/(cur.unix_time_stamp - prv.unix_time_stamp) AS [value10]
, cur.counter1 AS [cur_counter1], cur.counter10 AS [cur_counter10], cur.unix_time_stamp AS [cur_time]
, prv.counter1 AS [prv_counter1], prv.counter10 AS [prv_counter10], prv.unix_time_stamp AS [prv_time]
FROM T1 cur, T1 prv
WHERE cur.counter1 = (SELECT MAX(aux_0.counter1) FROM T1 aux_0)
AND prv.counter1 = (SELECT MAX(aux_1.counter1) FROM T1 aux_1 WHERE aux_1.counter1 < cur.counter1);

Related

SQL query to find all rows with same timestamp + or - one second

Row 3 in the following table is a duplicate. I know this because there is another row (row 5) that was created by the same user less than one second earlier.
row record created_by created_dt
1 5734 '00E759CF' '2020-06-05 19:59:36.610'
2 9856 '1E095CBA' '2020-06-05 19:57:31.207'
3 4592 '1E095CBA' '2020-06-05 19:54:41.930'
4 7454 '00E759CF' '2020-06-05 19:54:41.840'
5 4126 '1E095CBA' '2020-06-05 19:54:41.757'
I want a query that returns all rows created by the same user less than one second apart.
Like so:
row record created_by created_dt
1 4592 '1E095CBA' '2020-06-05 19:54:41.930'
2 4126 '1E095CBA' '2020-06-05 19:54:41.757'
This is what I have so far:
SELECT DISTINCT a1.*
FROM table AS a1
LEFT JOIN table AS a2
ON a1.created_by = a2.created_by
AND a1.created_dt > a2.created_dt
AND a1.created_dt <= DATEADD(second, 1, a2.created_dt)
WHERE a1.created_dt IS NOT NULL
AND a.created_dt IS NOT NULL
This is what finally did the trick:
SELECT
a.*
FROM table a
WHERE EXISTS (SELECT TOP 1
*
FROM table a1
WHERE a1.created_by = a.created_by
AND ABS(DATEDIFF(SECOND, a.created_dt, a1.created_dt)) < 1
AND a.created_dt <> a1.created_dt)
ORDER BY created_dt DESC
You could use exists:
select t.*
from mytable t
where exists(
select 1
from mytable t1
where
t1.created_by = t.created_by
and abs(datediff(second, t.created_dt, t1.created_dt)) < 1
)
How about something like this
SELECT DISTINCT a1.*
FROM #a1 AS a1
LEFT JOIN #a1 AS a2 ON a1.[Created_By] = a2.[Created_By]
AND a1.[Record] <> a2.[Record]
WHERE ABS(DATEDIFF(SECOND, a1.[Created_Dt], a2.[Created_Dt])) < 1
Here is the sample query I used to verify the results.
DECLARE #a1 TABLE (
[Record] INT,
[Created_By] NVARCHAR(10),
[Created_Dt] DATETIME
)
INSERT INTO #a1 VALUES
(5734, '00E759CF', '2020-06-05 19:59:36.610'),
(9856, '1E095CBA', '2020-06-05 19:57:31.207'),
(4592, '1E095CBA', '2020-06-05 19:54:41.930'),
(7454, '00E759CF', '2020-06-05 19:54:41.840'),
(4126, '1E095CBA', '2020-06-05 19:54:41.757')
SELECT DISTINCT a1.*
FROM #a1 AS a1
LEFT JOIN #a1 AS a2 ON a1.[Created_By] = a2.[Created_By]
AND a1.[Record] <> a2.[Record]
WHERE ABS(DATEDIFF(SECOND, a1.[Created_Dt], a2.[Created_Dt])) < 1
I would suggest lead() and lag() instead of self-joins:
select t.*
from (select t.*,
lag(created_dt) over (partition by created_dt) as prev_cd,
lead(created_dt) over (partition by created_dt) as next_cd
from t
) t
where created_dt < dateadd(second, 1, prev_created_dt) or
created_dt > dateadd(second, -1, next_created_dt)

Subtract the difference from the others in the current row from the previous value in the column

Good afternoon! I want to get the following result: to subtract the remainder of the difference that must be shipped. I try through the LAG function. It turns out, but somehow everything is crooked. Tell me how you can write it in SQL more elegantly.
CREATE TABLE TestTable(
[id] INT IDENTITY,
[productid] INT,
[name] NVARCHAR(256),
[ordered] DECIMAL(6,3),
[delivered] DECIMAL(6,3),
[remainder] DECIMAL(6,3));
INSERT INTO TestTable ([productid], [name], [ordered], [delivered], [remainder])
VALUES (712054, 'Product OSFNS', 253, 246.005, 13.255),
(712054, 'Product OSFNS', 186, 183.63, 13.255),
(712054, 'Product OSFNS', 196.8, 193.745, 13.255),
(712054, 'Product OSFNS', 480, 477.025, 13.255)
And the query:
WITH CTE_diff AS
(SELECT
T1.[id]
,T1.[productid]
,T1.[name]
,T1.[ordered]
,T1.[delivered]
,T1.[remainder]
,LAG(T2.[ordered] - T2.[delivered], 1, T1.[ordered] - T1.[delivered])
OVER (ORDER BY T2.[productid]) as R
FROM TestTable T1 JOIN TestTable T2
ON T1.id = T2.id - 1
UNION
SELECT *
FROM (
SELECT TOP(1)
T1.[id]
,T1.[productid]
,T1.[name]
,T1.[ordered]
,T1.[delivered]
,T1.[remainder]
,LEAD(T2.[ordered] - T2.[delivered], 1, T1.[ordered] - T1.[delivered])
OVER (ORDER BY T2.[productid]) as R
FROM TestTable T1 JOIN TestTable T2
ON T1.id = T2.id
ORDER BY T1.id DESC
) as tbl)
SELECT * FROM CTE_diff;
My best guess is that you want cumulative sums:
select tt.*,
remainder + sum(delivered - ordered) over (partition by productid order by id) as net_amount
from testtable tt;
Here is a db<>fiddle.

SQL Anywhere: find rows that are +-2 compared to another row

I have the following table:
ID User Form Depth
1 A ABC 2001
1 A XYZ 1001
1 B XYZ 1003
1 B DEF 3001
1 C XYZ 1000
If ID and Form are identical, I need to identify those rows that are +-2 from User A. Using the example above, the script would return:
ID User Form Depth
1 B XYZ 1003
1 C XYZ 1000
I already have a script which identifies rows with identical ID and Form--I just need the other part, but I'm struggling with figuring out the logic. I was hoping there was some kind of DIFF function I could use, but I can't find one for SQL Anywhere.
Does anyone have any suggestions?
Thanks!
If you're looking for the depth to be exactly +/-2 from A's depth:
select t1.*
from mytab t1,
mytab t2
where t1.id = t2.id
and t1.form = t2.form
and t1.user != 'A'
and t2.user = 'A'
and abs(t1.depth - t2.depth) = 2
go
ID User Form Depth
--- ----- ----- -----
1 B XYZ 1003
If you're looking for the depth to be within 2 of A's depth (ie, diff <= 2):
select t1.*
from mytab t1,
mytab t2
where t1.id = t2.id
and t1.form = t2.form
and t1.user != 'A'
and t2.user = 'A'
and abs(t1.depth - t2.depth) <= 2
go
ID User Form Depth
--- ----- ----- -----
1 B XYZ 1003
1 C XYZ 1000
This is pretty basic SQL so while this fiddle was done with MySQL, you should find the queries work in SQLAnywhere, too: sql fiddle
I think you want exists:
select t.*
from t
where t.user <> 'A' and
exists (select 1
from t t2
where t2.form = t.form and t2.id = t.id and
t2.depth between t.depth - 2 and t.depth + 2
);
A quick and dirty generalized method.
Replace #User with whomever you would like to remove.
DECLARE #table TABLE (
ID Int
,[User] VARCHAR(2)
,Form VARCHAR(3)
,Depth INT
)
DECLARE #User VARCHAR(2) = 'A'
INSERT INTO #table (ID , [User], Form, Depth)
VALUES
(1 , 'A' , 'ABC' , 2001),
(1 , 'A' , 'XYZ' , 1001),
(1 , 'B' , 'XYZ' , 1003),
(1 , 'B' , 'DEF' , 3001),
(1 , 'C' , 'XYZ' , 1000)
SELECT t1.ID, t1.[User], t1.Form, t1.Depth , ROW_NUMBER() OVER(ORDER BY t1.ID, t1.[User], t1.Form, t1.Depth) AS [row_number]
INTO #temp
FROM #table as t1
INNER JOIN (
SELECT t.ID, t.Form, COUNT('8') as [count]
FROM #table as t
GROUP BY ID, Form
HAVING COUNT('8') > 1
) as duplicates
ON duplicates.ID = t1.ID
AND duplicates. Form = t1.Form
ORDER BY ID, User, Form, Depth
-- SELECT * FROM #temp
SELECT [row_number] - 2 as value
INTO #range
FROM #temp as t
WHERE t.[User] = #User
--SELECT * FROM #range
INSERT INTO #range
SELECT [row_number] - 1
FROM #temp as t
WHERE t.[User] = #User
INSERT INTO #range
SELECT [row_number] + 1
FROM #temp as t
WHERE t.[User] = #User
INSERT INTO #range
SELECT [row_number] + 2
FROM #temp as t
WHERE t.[User] = #User
SELECT * FROM #temp
WHERE [row_number] IN (SELECT value FROM #range)
DROP TABLE #temp
DROP TABLE #range

When IDs are identical check that the Ordinal is greater than the previous submission

Example query
USE HES
SELECT T1.ID, T2.DATE, T1.ORDINAL
FROM TABLE1 AS T1
LEFT JOIN TABLE2 AS T2
ON T1.ID = T2.ID AND T1.PARTYEAR = T2.PARTYEAR
WHERE
T1.MONTHYEAR = '201501'
Results from example query
ID Date Ordinal
1 01/01/2016 1
1 02/01/2016 2
1 03/01/2016 3
2 04/01/2016 1
2 05/01/2016 2
3 06/01/2016 1
3 07/01/2016 2
3 08/01/2016 3
4 09/01/2016 1
4 10/01/2016 1
Question
Each user has a unique ID, for each ID how would I to check that each data submission contains an Ordinal that is greater than the one that was previously submitted.
So, in the example query results above, ID 4 contains an issue.
I'm fairly new to SQL, I've been searching for similar examples but with no success.
Any help would be greatly appreciated.
Use LAG with OVER clause:
WITH cte AS
(
SELECT T1.ID, T2.DATE, T1.ORDINAL, LAG(T1.ORDINAL) OVER(PARTITION BY T1.ID ORDER BY T1.ORDINAL) AS LagOrdinal
FROM TABLE1 AS T1
LEFT JOIN TABLE2 AS T2
ON T1.ID = T2.ID AND T1.PARTYEAR = T2.PARTYEAR
WHERE
T1.MONTHYEAR = '201501'
)
SELECT ID, DATE, ORDINAL, CASE WHEN ORDINAL > LagOrdinal THEN 1 ELSE 0 END AS OrdinalIsGreater
FROM cte;
Try this one:
SELECT * INTO #tmp
FROM (VALUES
(1, CONVERT(date, '01/01/2016'), 1),
(1, '02/01/2016', 2),
(1, '03/01/2016', 3),
(2, '04/01/2016', 1),
(2, '05/01/2016', 2),
(3, '06/01/2016', 1),
(3, '07/01/2016', 2),
(3, '08/01/2016', 3),
(4, '09/01/2016', 1),
(4, '10/01/2016', 1)
)T(ID, Date, Ordinal)
WITH Numbered AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Date) R, *
FROM #tmp
)
SELECT N2.ID, N2.Date, N1.Ordinal Prev, N2.Ordinal Curr
FROM Numbered N1
JOIN Numbered N2 ON N1.R+1=N2.R AND N1.ID=N2.ID
WHERE N1.Ordinal >= N2.Ordinal
It can be simplified when SQL Server version >= 2012, #tmp is your current result.
Like #Serg said, you can achieve this using lag
select *
from (
SELECT T1.ID, T2.DATE, T1.ORDINAL,
lag(t1.ordinal) over (partition by t1.id order by t2.date) as prevOrdinal
FROM TABLE1 AS T1
LEFT JOIN TABLE2 AS T2
ON T1.ID = T2.ID AND T1.PARTYEAR = T2.PARTYEAR
WHERE
T1.MONTHYEAR = '201501') as t
where t.prevOrdinal >= t.ordinal;
OUTPUT
ID DATE ORDINAL prevOrdinal
4 2016-10-01 1 1

i want to display data according to type in same query

i have following table "vehicle_data" :
ID ALERT_TYPE VALUE
58 2 1
58 1 1
104 1 1
104 2 1
Here alert_type = 2 is for GPS value and alert_type=1 is for engine_value .
so if alert_type=2 and its value is =1 then it means its value is correct.
when alert_type=2 and its value is =0 then it means its value is wrong.
same for alert_type=1
so now here i want the following output:
ID gps engine_value
58 1 1
104 1 1
how can i perform this query??
You can do it like this.
SELECT ID
,CASE WHEN [ALERT_TYPE]=2 and [value ]=1 THEN 1 ELSE 0 END as gps
,CASE WHEN [ALERT_TYPE]=1 and [value ]=1 THEN 1 ELSE 0 END as engine
FROM vehicle_data
SELECT ID, alert_type=2 AS gps, alert_type=1 AS [engine] FROM vehicle_data WHERE value=1;
EDITED to account for your explanation of VALUE.
Schema
CREATE TABLE table3 (id int, ALERT_TYPE int)
INSERT table3 VALUES (58, 1), (58, 2), (104, 1), (104, 2)
Query
SELECT *
FROM (
SELECT ID
,ROW_NUMBER() OVER (
PARTITION BY id ORDER BY id
) AS row_num
,gps = CASE
WHEN ALERT_TYPE = 2
THEN 1
ELSE 0
END
,engine = CASE
WHEN ALERT_TYPE = 1
THEN 1
ELSE 0
END
FROM table3
) a
WHERE a.row_num = 1
Output
ID gps engine
58 1 0
104 0 1
One possible way using subqueries :
select
Ids.ID
, gps.VALUE 'gps'
, engine_value.VALUE 'engine_value'
from (select distinct ID from vehicle_data) Ids
left join
(select ID, VALUE from vehicle_data where ALERT_TYPE = 2) gps
on gps.ID = Ids.ID
left join
(select ID, VALUE from vehicle_data where ALERT_TYPE = 1) engine_value
on engine_value.ID = Ids.ID
[SQL Fiddle demo]
I hope this should work for you,
Select ID,sum(gps) as gps ,sum(engine) as engine from
(SELECT ID
,CASE WHEN [ALERT_TYPE]=2 and [value ]=1 THEN 1 ELSE 0 END as gps
,CASE WHEN [ALERT_TYPE]=1 and [value ]=1 THEN 1 ELSE 0 END as engine
FROM vehicle_data
)a
group by id
select x.id,x.alert_type as GPS,x.value as engine_value from (
select ID,alert_type,value,ROW_NUMBER() over (partition by id order by alert_type ) as Rnk from mytable
)x
where Rnk=1
Please check this query in SQL :
create table mytable (id int, alert_type int, value int);
insert into mytable (id, alert_type, value)
values (58, 2, 1),
(58, 1, 1),
(104, 1, 1),
(104, 2, 1);
SELECT distinct ID
,(select count (id) from mytable mt where mt.id=mytable.id and mt.[ALERT_TYPE]=2 and mt.[value ]=1) as gps
,(select count (id) from mytable mt where mt.id=mytable.id and mt.[ALERT_TYPE]=1 and mt.[value ]=1) as engine
FROM mytable
BASED ON YOUR QUESTION I BELIEVE YOU WANT THE DATA IN COLUMN AND TO SUIT YOUR REQUIREMENT I HAVE MADE A SQL FIDDLE WORKING - CODE IS ALSO MENTIONED BELOW -
HERE YOU GO WITH THE WORKING FIDDLE -
WORKING DEMO
SQL CODE FOR REFERNECE -
CREATE TABLE ALERTS (ID INT, ALERT_TYPE INT, VALUE INT)
INSERT INTO ALERTS VALUES (58,2,1)
INSERT INTO ALERTS VALUES (58,1,0)
INSERT INTO ALERTS VALUES (104,1,1)
INSERT INTO ALERTS VALUES (104,2,0)
CREATE TABLE ALERTSVALUE (ID INT, gps INT,engine INT)
INSERT INTO ALERTSVALUE VALUES (58,1,0)
INSERT INTO ALERTSVALUE VALUES (104,0,1)
SELECT A.ID,
CASE A.ALERT_TYPE WHEN 2 THEN 1 ELSE 0 END AS GPS,
CASE A.ALERT_TYPE WHEN 1 THEN 1 ELSE 0 END AS ENGINE_VALUE,
A.VALUE FROM ALERTS A WHERE A.VALUE = 1
EDIT BASED ON COMMENT - TO MERGE THE ROWS FOR BOTH GPS AND ENGINE_VALUE:
SELECT X.ID,X.ALERT_TYPE as GPS,X.VALUE as ENGINE_VALUE
FROM (
SELECT ID,ALERT_TYPE ,VALUE ,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY alert_type ) AS [Rank] FROM ALERTS
)X
WHERE [Rank]=1
SQL FIDDLE DEMO