Aggregate multiple rows between two numbers - sql

Let's say we have this table:
table1
sensor_id
start_time_index
end_time_index
1
1
4
1
2
6
2
1
3
2
2
4
And another table with:
table2
sensor_id
time_index
value
1
1
'A'
1
2
'B'
1
3
'A'
1
4
'C'
1
5
'D'
1
6
'B'
2
1
'B'
2
2
'C'
2
3
'D'
2
4
'A'
Desired table is:
sensor_id
start_time_index
end_time_index
values_concatenated
1
1
4
"ABAC"
1
2
6
"BACDB"
2
1
3
"BCD"
2
2
4
"CDA"
I didn't know how to aggregate between a range that's specified between two values that are in two columns.

Using "range join" ON col BETWEEN ... AND ... and LISTAGG:
SELECT tab1.sensor_id, tab1.start_time_index, tab1.end_time_index,
LISTAGG(tab2.value) WITHIN GROUP(ORDER BY tab2.time_index) AS values_contatenated
FROM tab1
JOIN tab2
ON tab1.sensor_id = tab2.sensor_id
AND tab2.time_index BETWEEN tab1.start_time_index AND tab1.end_time_index
GROUP BY tab1.sensor_id, tab1.start_time_index, tab1.end_time_index

Related

For every 3 unique ids group together and set groupid without using CASE Expression

Table table_name
id
1
2
3
4
4
5
6
6
7
7
7
8
9
9
10
Following SQL will give me the answer Demo but I need to hardcode each case and I need to know the MAX(id) in the first place.
SELECT id,
CASE
WHEN id > '0' AND id <= '3' THEN 1
WHEN id > '3' AND id <= '6' THEN 2
WHEN id > '6' AND id <= '9' THEN 3
WHEN id > '9' AND id <= '12' THEN 4
END AS groupid
FROM table_name
How to improve it without me hardcode using the CASE. Since id column will continue increasing and it will runout of case sometime later.
(SQL Server 2014 - build v12.0.6108.1)
Result:
id
groupid
1
1
2
1
3
1
4
2
4
2
5
2
6
2
6
2
7
3
7
3
7
3
8
3
9
3
9
3
10
4
SELECT id, CEILING(id/3.0) AS groupid
FROM table_name

SQL: subset data: select id when time_id for id satisfy a condition from another column

I have a data (dt) in SQL like the following:
ID time_id act rd
11 1 1 1
11 2 4 1
11 3 7 0
12 1 8 1
12 2 2 0
12 3 4 1
12 4 3 1
12 5 4 1
13 1 4 1
13 2 1 0
15 1 3 1
16 1 8 0
16 2 8 0
16 3 8 0
16 4 8 0
16 5 8 0
and I want to take the subset of this data such that only ids (and their corresponding time_id, act, rd) that has time_id == 5 is retained. The desired output is the following
ID time_id act rd
12 1 8 1
12 2 2 0
12 3 4 1
12 4 3 1
12 5 4 1
16 1 8 0
16 2 8 0
16 3 8 0
16 4 8 0
16 5 8 0
I know I should use having clause somehow but have not been successful so far (returns me empty outputs). below is my attempt:
SELECT * FROM dt
GROUP BY ID
Having min(time_id) == 5;
This query:
select id from tablename where time_id = 5
returns all the ids that you want in the results.
Use it with the operator IN:
select *
from tablename
where id in (select id from tablename where time_id = 5)
You can use a correlated subquery with exists:
select t.*
from t
where exists (select 1 from t t2 where t2.id = t.id and t2.time_id = 5);
WITH temp AS
(
SELECT id FROM tab WHERE time_id = 5
)
SELECT * FROM tab t join temp tp on(t.id=tp.id);
check this query
select * from table t1 join (select distinct ID from table t where time_id = 5) t2 on t1.id =t2.id;

Select all rows if only one matches

I have two tables: FEATURE and FEATURE_DETAILS. Relation is one(FEATURE) to many(FEATURE_DETAILS).
feature_details_id | feature_id
1 1
1 2
1 4
2 1
2 2
2 4
2 5
3 1
3 5
How could I select all rows which contains e.g. 5?
feature_details_id | feature_id
2 1
2 2
2 4
2 5
3 1
3 5
first get the list of feature_deatails_id for which feature_id is 5, then pass the feature_deatails_id to FEATURE_DETAILS table to get the result
Try something like this
select *
from FEATURE_DETAILS
where feature_deatails_id in(
select feature_deatails_id from FEATURE_DETAILS where feature_id = 5)
or use Max()Over() Window function(preferred approach)
select * from
(
select max(case when feature_id = 5 then 1 else 0 end)over(partition by feature_deatails_id) as cnt,
feature_deatails_id,feature_id
from FEATURE_DETAILS
)s
Where cnt = 1
SELECT * FROM FEATURE_DETAILS WHERE feature_details_id IN (
SELECT feature_details_id from FEATURE_DETAILS WHERE feature_id=5
)

SQL -- Multiple rows, similar value in a row, need to not show specific values

Here is the issue:
Table name = a
1 2 3
123 1 A
123 1 A
123 2 A
332 1 A
332 1 A
321 2 B
321 2 A
321 1 A
So far what I have is this:
select distinct 1,2,3 from a where a.2='1' and a.3='B';
What it returns is each result (except for 321).
I only want to select values column 1 as long as that value is not in a row where there is a 2 in column 2 or a B in column 3. Is this possible?
"not in a row where there is a 2 in column 2 or a B in column 3" can be expressed as
select distinct 1,2,3 from a where a.2!='2' or a.3!='B';
or
select distinct 1,2,3 from a where a.2 <> '2' or a.3 <> 'B';
I would use group by and having:
select col1
from t
group by col1
having sum(case when col2 = 2 then 1 else 0 end) = 0 and
sum(case when col3 = 'B' then 1 else 0 end) = 0;

T-SQL Reverse Pivot on every character of a string

We have a table like below in an sql server 2005 db:
event_id staff_id weeks
1 1 NNNYYYYNNYYY
1 2 YYYNNNYYYNNN
2 1 YYYYYYYYNYYY
This is from a piece of timetabling software and is basically saying which staff members are assigned to an event (register) and the set of weeks they are teaching that register. So staff_id 1 isn't teaching the first 3 weeks of event 1 but is teaching the following 4....
Is there an easy way to convert that to an easier form such as:
event_id staff_id week
1 1 4
1 1 5
1 1 6
1 1 7
1 1 10
1 1 11
1 1 12
1 2 1
1 2 2
1 2 3
1 2 7
1 2 8
1 2 9
2 1 1
2 1 2
2 1 3
2 1 4
2 1 5
2 1 6
2 1 7
2 1 8
2 1 10
2 1 11
2 1 12
WITH cte AS
(
SELECT 1 AS [week]
UNION ALL
SELECT [week] + 1
FROM cte
WHERE [week] < 53
)
SELECT t.event_id, t.staff_id, cte.[week]
FROM your_table AS t
INNER JOIN cte
ON LEN(ISNULL(t.weeks, '')) >= cte.[week]
AND SUBSTRING(t.weeks, cte.[week], 1) = 'Y'
ORDER BY t.event_id, t.staff_id, cte.[week]