Convert Range Records - sql

I have the following scenario and would need some help with the SQL
ID Flag ValidFrom Valid To
1 A 2017001 2017005 ( Valid Till end of 2017004)
1 B 2007005 2017008
1 C 2017008 2017012
2 D 2017001 2017006
2 E 2007006 2017008
2 F 2017008 2017012
I need result somthing like this
1 2017001 A
1 2007002 A
1 2017003 A
1 2017004 A
1 2007005 B
1 2017006 B
1 2017007 B
1 2007008 C
1 2017009 C
1 2017010 C
1 2017011 C
2 2017001 D
2 2007002 D
2 2017003 D
2 2017004 D
2 2007005 D
2 2017006 E
2 2017007 E
2 2007008 F
2 2017009 F
2 2017010 F
2 2017011 F
Actually i need the only Min 3 months so i actually need something like this
1 2017001 A
1 2007002 A
1 2017003 A
2 2017001 D
2 2007002 D
2 2017003 D
Thanks in advance

Could you please check following SELECT statement
;with cte as (
select 0 as n union all
select 1 as n union all
select 2 as n
)
select
ID,
Flag,
ValidFrom,
case when r.ValidTo - r.ValidFrom > 2 then r.ValidFrom+2 else r.ValidTo end as ValidTo,
ValidFrom + n as result
from ranges r, cte
where ValidFrom + n <= ValidTo

Related

What is the best way to duplicate rows based on multiple columns?

I have the following table below:
ID Start_Repeat_1 End_Repeat_1 Start_Repeat_2 End_Repeat_2
A 3 7 2 5
B 1 4 2 5
My goal is to duplicate "A" 5 times and "B" 4 times with the output below
ID Repeat_1 Repeat_2
A 3 2
A 4 3
A 5 4
A 6 5
A 7 NULL
B 1 2
B 2 3
B 3 4
B 4 5
The logic is that "A" needs to be duplicated with numbers between 3 and 7 in one column and numbers between 2 and 5 in another column so it needs to be duplicated at least 5 times.
The version below would also do. The order between the two columns does not matter.
ID Repeat_1 Repeat_2
A 3 2
A 7 5
A 6 3
A 5 NULL
A 4 4
Can someone help me with this using SQL Server 2018?
The dataset size is about 10,000 rows and each row is duplicated at most 10 times with a total of 10 columns like this
ID Repeat_1 Repeat_2 Repeat_3 Repeat_4 Repeat_10
A 3 2 1 1 1
B 7 5 1 1 1
You need to make use of a tally table. In the following solution, I have use a recursive cte to generate one.
Alternatively you can use a recursive cte to generate one on the fly
-- Tally Table
create table tally
(
n int
)
-- generate 1000 numbers for tally table
with cte as
(
select n = 0
union all
select n = n + 1
from cte
where n < 1000
)
insert into tally (n)
select n
from cte
The cross apply is to find the maximum difference between the repeat_1 or 2 etc.
-- The query
select t.ID,
Repeat_1 = case when n.n <= (End_Repeat_1 - Start_Repeat_1)
then t.Start_Repeat_1 + n.n
end,
Repeat_2 = case when n.n <= (End_Repeat_2 - Start_Repeat_2)
then t.Start_Repeat_2 + n.n
end,
Repeat_3 = case when n.n <= (End_Repeat_3 - Start_Repeat_3)
then t.Start_Repeat_3 + n.n
end
from tbl t
cross apply
(
select m = max(d)
from (
values
(End_Repeat_1 - Start_Repeat_1 + 1),
(End_Repeat_2 - Start_Repeat_2 + 1),
(End_Repeat_3 - Start_Repeat_3 + 1)
) n (d)
) m
inner join tally n on n.n >= 0
and n.n < m.m
dbfiddel demo
you can use recursive cte :
with cte as (
select * from test
union all
select Id
, case when Start_Repeat_1 + 1 > End_Repeat_1 then null else Start_Repeat_1 + 1 end
,End_Repeat_1
,case when Start_Repeat_2+ 1 > End_Repeat_2 then null else Start_Repeat_2+ 1 end
,End_Repeat_2
from cte
where Start_Repeat_1 <= End_Repeat_1 and Start_Repeat_2 <= End_Repeat_2
)
select ID,Start_Repeat_1,Start_Repeat_2
from cte
where coalesce(Start_Repeat_1,Start_Repeat_2) is not null
order by ID
ID | Start_Repeat_1 | Start_Repeat_2
:- | -------------: | -------------:
A | 3 | 2
A | 4 | 3
A | 5 | 4
A | 6 | 5
A | 7 | null
B | 1 | 2
B | 2 | 3
B | 3 | 4
B | 4 | 5
db<>fiddle here

SQL Order By Custom Sequence

I have a data in this order
Id Value
-- ----
1 a
1 b
1 c
2 a
2 c
3 b
4 c
4 b
4 a
I want to sort data in this order
Id Value
-- ----
1 a
2 a
3 b
4 c
1 b
2 c
4 b
1 c
4 a
You seem to want to intersperse the numbers. For this purpose, you can use row_number():
order by row_number() over (partition by id order by value),
id

Number the rows and reset the counter back to 1 on certain condition

How can I reset a counter in SQL Server on a keyword? In the following data, everytime the string 'A' is found, the counter needs to be reset to 1:
Item Date
A 01.01.2019
B 02.01.2019
C 03.01.2019
D 04.01.2019
A 05.01.2019
B 06.01.2019
A 07.01.2019
B 08.01.2019
C 09.01.2019
D 10.01.2019
E 11.01.2019
A 12.01.2019
A 13.01.2019
A 14.01.2019
B 15.01.2019
And I need to reset the counter everytime A is found:
Count Item Date
1 A 01.01.2019
2 B 02.01.2019
3 C 03.01.2019
4 D 04.01.2019
1 A 05.01.2019
2 B 06.01.2019
1 A 07.01.2019
2 B 08.01.2019
3 C 09.01.2019
4 D 10.01.2019
5 E 11.01.2019
1 A 12.01.2019
1 A 13.01.2019
1 A 14.01.2019
2 B 15.01.2019
Something like:
WITH cte AS (
SELECT *, COUNT(CASE WHEN Item = 'A' THEN 1 END) OVER (ORDER BY Date) AS GroupNum
FROM t
)
SELECT *, ROW_NUMBER() OVER (PARTITION BY GroupNum ORDER BY Date) AS [Count]
FROM cte
ORDER BY Date
The cte assigns a running count to each row that increments whenever A is encountered. Rows are the assigned a ROW_NUMBER() based on this counter.
Demo on db<>fiddle

Count occurrences of field values as they are displayed in order

thanks in advance for the help and sorry for how the "table" looks. Here's my question...
Let's say I have a subquery with this table (imagine the bold as column headers) as its output -
id 1 1 2 3 3 3 3 4 5 6 6 6
action o c o c c o c o o c c c
I would like my new query to output -
id 1 1 2 3 3 3 3 4 5 6 6 6
action o c o c c o c o o c c c
ct 1 2 1 1 2 3 4 1 1 1 2 3
#c 0 1 0 1 2 2 3 0 0 1 2 3
#o 1 1 1 0 0 1 1 1 1 0 0 0
where ct stands for count. Basically, I want to count (for each id) the occurrences of consecutive id and action as they happen. Let me know if this makes sense, and if not, how I can clarify my question.
Note: I realize the lag/lead functions may be helpful in this situation, along with the row_number() function. Looking for as many creative solutions as possible!
You are looking for the row_number() analytic function:
select id, action, row_number() over (partition by id order by id) as ct
from table t;
For #c and #o, you want cumulative sum:
select id, action, row_number() over (partition by id order by id) as ct,
sum(case when action = 'c' then 1 else 0 end) over
(partition by id order by <some column here>) as "#c",
sum(case when action = 'c' then 1 else 0 end) over
(partition by id order by <some column here>) as "#o"
from table t;
The one caveat is that you need a way to specify the order of the rows -- an id or date time stamp or something. SQL result sets and tables are inherently unordered, so there is no idea that one row comes before or after another.
SQL> select id, action,
2 row_number() over(partition by id order by rowid) ct,
3 sum(decode(action,'c',1,0)) over(partition by id order by rowid) c#,
4 sum(decode(action,'o',1,0)) over(partition by id order by rowid) o#
5 from t1
6 /
ID A CT C# O#
---------- - ---------- ---------- ----------
1 o 1 0 1
1 c 2 1 1
2 o 1 0 1
3 c 1 1 0
3 c 2 2 0
3 o 3 2 1
3 c 4 3 1
4 o 1 0 1
5 o 1 0 1
6 c 1 1 0
6 c 2 2 0
6 c 3 3 0
P.S. Sorry Gordon, didn't see your post.

oracle sql query question(grouping by 2 columns)

I have a table called testgroup in my database, which is like following:
I J
---------------------- ----------------------
1 a
1 a
2 a
1 b
1 c
2 b
3 d
2 b
2 b
3 d
Now, I want the result as below:
I J COUNT(J) in I
---------------------- ---------------------- ----------------------
1 a 2
2 a 1
1 b 1
1 c 1
2 b 3
3 d 2
...where count(j) in I is the number of each J related to the I.
For example: with I = 1, there are 2 a in column J, so the third column would be equal 2.
select I, J, count(*) as JinI
FROM atable
GROUP BY I, J
In fact the question is about counting I and J pairs:
select I, J, count(*) from tblName group by I, J