Selecting time intervals of value live - missing first and last intervals - sql

I've got a table with following structure
| ChangedDate | IDParameter | ChangedTo(bit column) |
So I need to get time intervals when my parameter is True or False, like following
| IDParameter | ChangedToDate1 | ChangedToDate2 | ChangedTo(true to false || false to true)
and I do
With RankedDates As
(
Select T1.[ChangedDate], T1.ID, T1.[ChangedToValue]
, Row_Number() Over( Partition By T1.ID Order By T1.[ChangedDate] ) As Num
From [Changes] As T1
)
SELECT T1.[ID]
,T2.[ChangedToValue]
,T1.[ChangedDate] AS startDate
,T2.[ChangedDate] AS endDate
FROM [RankedDates] AS T1
Join RankedDates As T2
On T2.ID = T1.ID
And T2.Num = T1.Num + 1
And T2.[ChangedToValue] <> T1.[ChangedToValue]
Order By T2.[ChangedDate]
The trouble is that I am missing first and last intervals here. it must be NULL for start date if that is first and NULL for endDate for last interval for each Parameter ID. I guess I need add it with UNION but my trouble I can't understand how to add it for each IDParameter.
I don't know when value were changed first time and I don't know if the value will be changed in any time so I need NULL or some mindate for first intervals and NULL or some maxdate for last intervals.
ms sql server 2008
sorry for such complex question.
Example :
08.03.2011 ID1 0 -> 1
09.03.2011 ID1 1 -> 0
09.03.2011 ID2 0 -> 1
10.03.2011 ID1 0 -> 1
10.03.2011 ID2 1 -> 0
--->
NULL , 08.03.2011 ID1 is 0
NULL , 09.03.2011 ID2 is 0
08.03.2011, 09.03.2011 ID1 is 1
09.03.2011, 10.03.2011 ID2 is 1
09.03.2011, 10.03.2011 ID1 is 0
10.03.2011, NULL ID1 is 1
10.03.2011, NULL ID2 is 0

how about using FULL JOIN instead of JOIN?
Does it solve your problem?
EDIT:
I think this should work as you want.
select isnull(T1.ID, T2.ID) as ID
,isnull(T2.[ChangedToValue], case when T1.[ChangedToValue] = 1 then 0 else 1 end) as [ChangedToValue]
,T1.[ChangedDate] as startdate
,T2.[ChangedDate] as enddate
from [RankedDates] T1
full join [RankedDates] T2
on T2.num = T1.num +1
and T2.ID = T1.ID
and T1.[ChangedToValue] <> T2.[ChangedToValue]
order by
case when T2.[ChangedDate] is null then 1 else 0 end
,T2.[ChangedDate]
You where right about the ChangedToValue, I modified it to show the opposite now, if T2 is null.

Assuming thats how your base table looks:
ChangeDate IDParameter ChangedTo
2011-03-08 ID1 True
2011-03-09 ID1 False
2011-03-09 ID2 True
2011-03-10 ID1 True
2011-03-10 ID2 False
SELECT (SELECT TOP 1 t0.[ChangeDate] FROM [calendardb].[dbo].[Table_1] t0
WHERE t0.IDParameter = t1.IDParameter AND t0.ChangeDate < t1.ChangeDate ORDER
BY t0.ChangeDate DESC),
[ChangeDate]
,[IDParameter]
,[ChangedTo]
FROM [calendardb].[dbo].[Table_1] t1
UNION
SELECT MAX(ChangeDate) as maxd ,NULL,[IDParameter],
(SELECT ChangedTo FROM [calendardb].[dbo].[Table_1] t0 WHERE t0.ChangeDate = (SELECT MAX(ChangeDate) FROM [calendardb].[dbo].[Table_1]
GROUP BY [IDParameter] HAVING IDParameter = t1.IDParameter) AND t1.IDParameter = t0.IDParameter)
FROM [calendardb].[dbo].[Table_1] t1
GROUP BY [IDParameter]
will give you result like this:
NULL 2011-03-08 ID1 1
2011-03-08 2011-03-09 ID1 0
NULL 2011-03-09 ID2 1
2011-03-09 2011-03-10 ID1 1
2011-03-09 2011-03-10 ID2 0
2011-03-10 NULL ID1 1
2011-03-10 NULL ID2 0

Related

How to get the next non-zero value in table partitioned by id?

Here is a subset of my table:
id
date
value
1
01/01/2022
5
1
02/02/2022
0
1
03/01/2022
0
1
04/02/2022
10
2
01/04/2022
5
2
02/04/2022
3
2
03/04/2022
0
2
04/04/2022
10
Where there are 0s in the value field, i would like to replace them with the non-zero value that occurs after the sequence of 0s are over, partitioned by id.
I have tried to use LAG but im really struggling as it takes the value above the current value in the table.
Any help will be appreciated.
Transformed table to look like
id
date
value
1
01/01/2022
5
1
02/02/2022
10
1
03/01/2022
10
1
04/02/2022
10
2
01/04/2022
5
2
02/04/2022
3
2
03/04/2022
10
2
04/04/2022
10
you can use cross apply;
select T1.id, T1.date, CASE WHEN T1.value = 0 THEN X.value ELSE T1.value END value from TestTable T1
OUTER APPLY (SELECT TOP 1 * FROM TestTable T2
WHERE T1.id = T2.id AND T2.date > T1.date
AND T2.value > 0
ORDER BY T2.date) X
sqlfiddle
Assuming by replace them you mean to update the table, simplest way would be a correlated subquery:
update t set value = (
select top(1) value
from t t2
where t2.id = t.id
and t2.value > 0
and t2.date > t.date
order by t2.date
)
where t.value = 0;
We group every 0 with the first value after it that's not 0 and then we use max() over() to replace the 0s in the group.
select id
,date
,max(value) over(partition by id, grp) as value
from
(
select *
,count(case when value != 0 then 1 end) over(partition by id order by date desc) as grp
from t
) t
order by id, date
id
date
value
1
2022-01-01
5
1
2022-02-02
10
1
2022-03-01
10
1
2022-04-02
10
2
2022-01-04
5
2
2022-02-04
3
2
2022-03-04
10
2
2022-04-04
10
Fiddle
You can do it using outer apply:
select
d.id, d.date_,
case when d.value != 0 then d.value else nz.value end as value
from data d
outer apply (
select min(value) as value
from data dd
where dd.id = d.id
and dd.date_ > d.date_
and dd.value <> 0
) nz
You can test on this db<>fiddle

sql: max value by 2 columns in another table

I have 2 tables and for every id in the first table I need to find max value in the date_2 column that would be lower than a value in the date_1 column.
Tables:
table 1
id
date_1
1
01.01.2020
1
11.01.2020
2
02.11.2020
2
02.12.2020
3
12.12.2020
3
31.01.2021
table 2
id
date_2
1
30.12.2019
1
05.01.2020
2
01.11.2020
2
30.10.2020
3
10.11.2020
3
31.12.2020
outcome needed:
id
date_1
max(date_2) within id,date_1
1
01.01.2020
30.12.2019
1
11.01.2020
05.01.2020
2
02.11.2020
01.11.2020
2
02.12.2020
01.11.2020
3
12.12.2020
10.11.2020
3
31.01.2021
31.12.2020
appreciate your help with this!
you could rank each row (I'm doing it here with row_number() function) then match on the id and the ranking.
with t1 as (select id, date_1,
row_number() over (partion by id order by date1) as rn
from table1),
t2 as (select id, date_2,
row_number() over (partion by id order by date2) as rn
from table2 ),
select id, date1, date2
from t1 inner join t2 on t1.id = t2.id and t1.rn = t2.rn
You can pretty much write a simple correlated query using exists that mirrors the English narrative:
select id, (
select Max(date_2) /* find max value in the date_2 column */
from t2
where t2.id = t1.id /* for every id in the first table */
and t2.date_2 < t1.date_1 /* lower than a value in the date_1 column */
) as "max(date_2) within id,date_1"
from t1;

Match nearest timestamp in Redshift SQL

I have two tables, t1 and t2. For each id in t1 I have multiple records in t2. I want to match the closest timestamp of t2 to each record of t1. In t1 there is a flag, if it's 1 I want to match the closest timetamp of t2 that's smaller and if it's 0 I want to match the closest timestamp that is larger than that in t1.
So alltogether I have the following table:
T1
id, flag, timestamp
T2
id, timestamp
Is there an efficient way to do that?
Edit, here is some example:
T1
customer_id
timestamp_t1
flag
1
01.01.21 12:00
1
2
01.01.21 13:00
0
T2
customer_id
timestamp_t2
additional attributes
1
01.01.21 11:00
attribute1
1
01.01.21 10:00
attribute2
1
01.01.21 13:00
attribute3
2
01.01.21 11:00
attribute4
2
01.01.21 12:00
attribute5
2
01.01.21 14:00
attribute6
2
01.01.21 15:00
attribute7
Result:
customer_id
timetsamp_t1
timestamp_t2
flag
additional attributes
1
01.01.21 12:00
01.01.21 11:00
1
attribute1
2
01.01.21 13:00
01.01.21 14:00
0
attribute6
I hope this helps. As you can see. In the result, we matched 11:00 of T2 with 12:00 of T1 because the flag was 1 we chose the closest timestamp that was smaller than 12:00. We also matched 14:00 with 13:00, because the flag was 0 (so we matched the closest timestamp with id 2 that is larger than 13:00).
You could use correlated sub-queries to find the rows before/after the timestamp, and then use a CASE expression to pick which to join on...
SELECT
*
FROM
t1
INNER JOIN
t2
ON t2.id = CASE WHEN t1.flag = 1 THEN
(
SELECT t2.id
FROM t2
WHERE t2.customer_id = t1.customer_id
AND t2.timestamp_t2 <= t1.timestamp_t1
ORDER BY t2.timestamp DESC
LIMIT 1
)
ELSE
(
SELECT t2.id
FROM t2
WHERE t2.customer_id = t1.customer_id
AND t2.timestamp_t2 >= t1.timestamp_t1
ORDER BY t2.timestamp ASC
LIMIT 1
)
END
Oh, you haven't included an id column in your example, this works similarly...
SELECT
*
FROM
t1
INNER JOIN
t2
ON t2.customer_id = t1.customer_id
AND t2.timestamp_t2
=
CASE WHEN t1.flag = 1 THEN
(
SELECT MAX(t2.timestamp_t2)
FROM t2
WHERE t2.customer_id = t1.customer_id
AND t2.timestamp_t2 <= t1.timestamp_t1
)
ELSE
(
SELECT MIN(t2.timestamp_t2)
FROM t2
WHERE t2.customer_id = t1.customer_id
AND t2.timestamp_t2 >= t1.timestamp_t1
)
END

MSSQL get rows which only differ at 2 columns

I have a task on which I have no idea how that could even work out.
I have to find records, which have a time difference of X and where a boolean is ON/OFF. I tried to use a LEFT OUTER JOIN and used the conditions in the ON clause, but it gave me the wrong result.
So my question is, how can I select rows, which have the same value in 2 columns, but different values in other 2 columns?
Edit:
My problem is, that for some reason my actual query returns the same entry multiple times. I checked if the entry exists multiple times, but it doesn't
Data for reference:
ID1 ID2 Boolean Time
1 1 0 2018-03-06 11:31:39
1 1 1 2018-03-06 11:33:39
2 1 0 2018-03-06 11:31:39
2 2 1 2018-03-06 11:40:39
The desired output from the query would be
ID1 ID2 Boolean Time
1 1 0 2018-03-06 11:31:39
1 1 1 2018-03-06 11:33:39
because ID1 and ID2 are the same, the Boolean is different and the time difference is in the specified range (lets say 5 minutes). The other 2 entries are not valid, because ID2 differs and the time difference is too big.
My current query:
select
t1.id1,
t1.id2,
t1.boolean,
t1.time
from t1 t1
left outer join t1 t2
on t1.boolean != t2.boolean and datediff(minute, t1.time, t2.time)<=5
where t1.id1 = t2.id1
and t1.id2 = t2.id2
Your query looks fine, I found few small issues
1- Table alias used is wrong instead of t it should be t1
2- Order or data is wrong
3- Changed left join to inner join
4- Modified ON and Where condition for better readability and performance
Check following corrected query.
WITH t1 AS
(
SELECT * FROM (VALUES
(1 , 1 , 0 , '2018-03-06 11:31:39'),
(1 , 1 , 1 , '2018-03-06 11:33:39'),
(2 , 1 , 0 , '2018-03-06 11:31:39'),
(2 , 2 , 1 , '2018-03-06 11:40:39')
) T( ID1, ID2 , Boolean, Time)
)
select
t1.id1,
t1.id2,
t1.boolean,
t1.time
from t1 t1
inner join t1 t2
on t1.id1 = t2.id1 and t1.id2 = t2.id2
where
t1.boolean != t2.boolean and datediff(minute, t1.time, t2.time)<=5
ORDER BY [TIME]
Output
+-----+-----+---------+---------------------+
| id1 | id2 | boolean | time |
+-----+-----+---------+---------------------+
| 1 | 1 | 0 | 2018-03-06 11:31:39 |
+-----+-----+---------+---------------------+
| 1 | 1 | 1 | 2018-03-06 11:33:39 |
+-----+-----+---------+---------------------+
To avoid duplicate value use GROUP BY
SELECT t1.id1
,t1.id2
,t1.boolean
,t1.TIME
FROM t1 t1
INNER JOIN t1 t2 ON t1.boolean != t2.boolean
AND datediff(minute, t1.TIME, t2.TIME) <= 5
WHERE t1.id1 = t2.id1
AND t1.id2 = t2.id2
GROUP BY t1.id1
,t1.id2
,t1.boolean
,t1.TIME
SELECT
D1.*
FROM
Data AS D1
WHERE
EXISTS (
SELECT
1
FROM
Data AS D2
WHERE
D1.ID1 = D2.ID2 AND
~D1.Boolean = D2.Boolean AND
ABS(DATEDIFF(MINUTE, D1.Time, D2.Time)) <= 5)
ORDER BY
D1.ID1,
D1.Boolean,
D1.Time

Getting Records from SQL Table by checking Multiple Columns

I have the table structure as follows
ID DefID AttrID ValInt ValReal ValDate ValStr
1 1 1 NULL NULL NULL hi
2 1 1 NULL NULL NULL hi
3 1 1 NULL NULL NULL hi
4 1 1 NULL NULL NULL hi
1 1 1 0 NULL NULL NULL
2 1 1 1 NULL NULL NULL
3 1 1 0 NULL NULL NULL
4 1 1 0 NULL NULL NULL
This is my table named Table, Now I want to get the ID by query only having ValStr='h1' and ValInt=1, meaning only those ID whose ValStr is hi and also ValInt = 1 belonging to the same ID column. Please help.
Here what I have did till Now.
select ID from Table where DefID=1 and ValStr='hi' and ValInt=1
My Actual answer should be
ID
2
but I am getting this from the above query
ID
1
2
3
4
One more option
select t1.ID from Table t1
where EXISTS (
SELECT *
FROM Table t2
WHERE t2.DefID = 1 AND t2.ValStr = 'hi'
) and t1.ValInt = 1
SELECT ID
FROM TableName
WHERE (DefID = 1 AND ValInt = 1) OR
(DefID = 1 AND ValStr = 'hi')
GROUP BY ID
HAVING COUNT(*) = 2
SQLFiddle Demo
How about
SELECT distinct t1.ID
FROM Table t1
JOIN Table t2 on t1.id = t2.id
WHERE t1.DefID=1 and t2.DefID = 1
AND t1.ValStr='hi' and t2.ValInt=1
or (depending on taste)
SELECT distinct t1.ID
FROM Table t1
JOIN Table t2 on t1.id = t2.id AND t1.DefID=1 = t2.DefID
WHERE t1.DefID=1 AND t1.ValStr='hi' and t2.ValInt=1