Generate series of integers with random start and end SQL Oracle - sql

I am trying to generate a series of integers that go from a random start-point until a random (greater) end-point. Furthermore, I would like to do this as a window function, so I could use this in a OVER (PARTITION BY ) statement.
Basically I'm trying to select random hours (from 1-24), but in a random way and also consecutively, and do this for each client (which is why I suggest an OVER (PARTITION BY client) statement, but I'm open for other ideas.
I am trying to use:
SELECT
T1.HOURS
FROM (
SELECT
LEVEL HOURS
FROM DUAL
CONNECT BY
LEVEL <= 24
) T1,
(
SELECT
INIT,
LEAST(INIT + LENGTH, 24) FIN
FROM (
SELECT
ROUND(DBMS_RANDOM.VALUE(1, 24)) INIT,
ROUND(DBMS_RANDOM.VALUE(1, 24)) LENGTH
FROM DUAL
) T0
) T2
WHERE
T1.HOURS >= T2.INIT AND
T1.HOURS <= T2.FIN;
But the result is unfortunately non-consecutive orders.
|hours|
|-----|
|17|
|18|
|20|
|24|
The code is simply nor working because the table T0 (which is the one that filters the initial INIT and final FIN value) is being replicated for each row.
This is a desired result:
client
hours
1
4
1
5
1
6
1
7
2
14
2
15
3
13
3
14
3
15
3
16
3
17
3
18
3
19
3
20
3
21
In the desired result a consecutive list is selected for each client, with a random start and end point.

In Oracle, you can use a correlated LATERAL join:
SELECT c.id,
h.hours
FROM ( SELECT id,
FLOOR(DBMS_RANDOM.VALUE(1, 25)) AS bound1,
FLOOR(DBMS_RANDOM.VALUE(1, 25)) AS bound2
FROM clients
) c
CROSS JOIN LATERAL (
SELECT LEAST(bound1, bound2) + LEVEL - 1 AS hours
FROM DUAL
CONNECT BY LEVEL <= ABS(bound1 - bound2) + 1
) h;
Then, for the sample data:
CREATE TABLE clients (id) AS
SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 3;
May (randomly) output:
ID
HOURS
1
7
1
8
1
9
1
10
2
19
2
20
2
21
2
22
2
23
2
24
3
6
3
7
3
8
3
9
3
10
3
11
3
12
3
13
3
14
3
15
3
16
fiddle

Related

Is it possible to use a aggregate function over partition by as a case condition in SQL?

Problem statement is to calculate median from a table that has two columns. One specifying a number and the other column specifying the frequency of the number.
For e.g.
Table "Numbers":
Num
Freq
1
3
2
3
This median needs to be found for the flattened array with values:
1,1,1,2,2,2
Query:
with ct1 as
(select num,frequency, sum(frequency) over(order by num) as sf from numbers o)
select case when count(num) over(order by num) = 1 then num
when count(num) over (order by num) > 1 then sum(num)/2 end median
from ct1 b where sf <= (select max(sf)/2 from ct1) or (sf-frequency) <= (select max(sf)/2 from ct1)
Is it not possible to use count(num) over(order by num) as the condition in the case statement?
Find the relevant row / 2 rows based of the accumulated frequencies, and take the average of num.
The example and Fiddle will also show you the
computations leading to the result.
If you already know that num is unique, rowid can be removed from the ORDER BY clauses
with
t1 as
(
select t.*
,nvl(sum(freq) over (order by num,rowid rows between unbounded preceding and 1 preceding),0) as freq_acc_sum_1
,sum(freq) over (order by num, rowid) as freq_acc_sum_2
,sum(freq) over () as freq_sum
from t
)
select t1.*
,case
when freq_sum/2 between freq_acc_sum_1 and freq_acc_sum_2
then 'V'
end as relevant_record
from t1
order by num, rowid
Fiddle
Example:
ID
NUM
FREQ
FREQ_ACC_SUM_1
FREQ_ACC_SUM_2
FREQ_SUM
RELEVANT_RECORD
7
8
1
0
1
18
5
10
1
1
2
18
1
29
3
2
5
18
6
31
1
5
6
18
3
33
2
6
8
18
4
41
1
8
9
18
V
9
49
2
9
11
18
V
2
52
1
11
12
18
8
56
3
12
15
18
10
92
3
15
18
18
MEDIAN
45
Fiddle for 1M records
You can find the one (or two) middle value(s) and then average:
SELECT AVG(num) AS median
FROM (
SELECT num,
freq,
SUM(freq) OVER (ORDER BY num) AS cum_freq,
(SUM(freq) OVER () + 1)/2 AS median_freq
FROM table_name
)
WHERE cum_freq - freq < median_freq
AND median_freq < cum_freq + 1
Or, expand the values using a LATERAL join to a hierarchical query and then use the MEDIAN function:
SELECT MEDIAN(num) AS median
FROM table_name t
CROSS JOIN LATERAL (
SELECT LEVEL
FROM DUAL
WHERE freq > 0
CONNECT BY LEVEL <= freq
)
Which, for the sample data:
CREATE TABLE table_name (Num, Freq) AS
SELECT 1, 3 FROM DUAL UNION ALL
SELECT 2, 3 FROM DUAL;
Outputs:
MEDIAN
1.5
(Note: For your sample data, there are 6 items, an even number, so the MEDIAN will be half way between the value of 3rd and 4rd items; so half way between 1 and 2 = 1.5.)
db<>fiddle here

SQL for a generated table with column 1 a sequence of numbers and column 2 a running sum

What would be the SQL (standard, or any major variant) to produce a table like the following?
1 1 -- 1
2 3 -- 2+1
3 6 -- 3+2+1
4 10 -- 4+3+2+1
5 15 -- 5+4+3+2+1
6 21 -- 6+5+4+3+2+1
... ...
The second column is the sum of the numbers in the first.
I couldn't get past this:
select rownum from all_objects where rownum <= 10;
Which produces column 1 (PL/SQL)
Tried to think on the following lines but clearly it is wrong, even syntactically:
select rownum, count(t2.rownum)
from
(select sum(rownum) from all_objects where rownum <= 10) t2,
all_objects
where rownum <= 10;
It is simple math:
select rownum, rownum * (rownum + 1) / 2
from all_objects
where rownum <= 10;
You don't need to hit the all_objects view; you could use a hierarchical query:
select level as position, sum(level) over (order by level) as running_sum
from dual
connect by level <= 10;
POSITION RUNNING_SUM
---------- -----------
1 1
2 3
3 6
4 10
5 15
6 21
7 28
8 36
9 45
10 55
or using #forpas' arithmetic-series method:
select level as position, level * (level + 1) / 2 as running_sum
from dual
connect by level <= 10;
POSITION RUNNING_SUM
---------- -----------
1 1
2 3
3 6
4 10
5 15
6 21
7 28
8 36
9 45
10 55
Or recursive subquery factoring (11gR2+):
with rcte (position, running_sum) as (
select 1, 1 from dual
union all
select position + 1, running_sum + position + 1
from rcte
where position < 10
)
select * from rcte
order by position;
POSITION RUNNING_SUM
---------- -----------
1 1
2 3
3 6
4 10
5 15
6 21
7 28
8 36
9 45
10 55
You are looking for a cumulative sum:
select rownum, sum(rownum) over (order by rownum)
from all_objects
where rownum <= 10;
I wasn't sure this would actually work on rownum, but it does.

SQL Recursive CTE unexpectedly returns alternating sets

I am trying to get the use recursive CTE to repeat the same pattern over and over, resetting when "Scenario" increases in value. RowNumber repeats 1-21 (as desired), but whenever "Scenario" is an even number, there are too few items in the "Vals" column to feed into "Value". I can't figure out which part of the code is causing me to be 1 short for only even Scenarios.
Below are the results of the code I'm using at the bottom.
Scenario RowNumber Value Vals
1 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 4 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 5 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 6 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 7 A A,A,A,A,A,A,A,A,A,A,A,A,B,C
1 8 A A,A,A,A,A,A,A,A,A,A,A,B,C
1 9 A A,A,A,A,A,A,A,A,A,A,B,C
1 10 A A,A,A,A,A,A,A,A,A,B,C
1 11 A A,A,A,A,A,A,A,A,B,C
1 12 A A,A,A,A,A,A,A,B,C
1 13 A A,A,A,A,A,A,B,C
1 14 A A,A,A,A,A,B,C
1 15 A A,A,A,A,B,C
1 16 A A,A,A,B,C
1 17 A A,A,B,C
1 18 A A,B,C
1 19 A B,C
1 20 B C
1 21 C
2 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 4 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 5 A A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 6 A A,A,A,A,A,A,A,A,A,A,A,B,B,C
2 7 A A,A,A,A,A,A,A,A,A,A,B,B,C
2 8 A A,A,A,A,A,A,A,A,A,B,B,C
2 9 A A,A,A,A,A,A,A,A,B,B,C
2 10 A A,A,A,A,A,A,A,B,B,C
2 11 A A,A,A,A,A,A,B,B,C
2 12 A A,A,A,A,A,B,B,C
2 13 A A,A,A,A,B,B,C
2 14 A A,A,A,B,B,C
2 15 A A,A,B,B,C
2 16 A A,B,B,C
2 17 A B,B,C
2 18 B B,C
2 19 B C
2 20 C
2 21 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,C
3 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 4 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 5 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 6 A A,A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 7 A A,A,A,A,A,A,A,A,A,A,A,B,C,C
3 8 A A,A,A,A,A,A,A,A,A,A,B,C,C
3 9 A A,A,A,A,A,A,A,A,A,B,C,C
3 10 A A,A,A,A,A,A,A,A,B,C,C
3 11 A A,A,A,A,A,A,A,B,C,C
3 12 A A,A,A,A,A,A,B,C,C
3 13 A A,A,A,A,A,B,C,C
3 14 A A,A,A,A,B,C,C
3 15 A A,A,A,B,C,C
3 16 A A,A,B,C,C
3 17 A A,B,C,C
3 18 A B,C,C
3 19 B C,C
3 20 C C
3 21 C
4 1 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 2 A A,A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 3 A A,A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 4 A A,A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 5 A A,A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 6 A A,A,A,A,A,A,A,A,A,A,B,B,B,C
4 7 A A,A,A,A,A,A,A,A,A,B,B,B,C
4 8 A A,A,A,A,A,A,A,A,B,B,B,C
4 9 A A,A,A,A,A,A,A,B,B,B,C
4 10 A A,A,A,A,A,A,B,B,B,C
4 11 A A,A,A,A,A,B,B,B,C
4 12 A A,A,A,A,B,B,B,C
4 13 A A,A,A,B,B,B,C
4 14 A A,A,B,B,B,C
4 15 A A,B,B,B,C
4 16 A B,B,B,C
4 17 B B,B,C
4 18 B B,C
4 19 B C
4 20 C
This is the code I used to generate the above sample. Where am I going wrong?
CREATE TABLE #temp3
(
Scenario INT
,Vals VARCHAR(64)
,LEN INT
)
;
WITH vals AS
(
SELECT
v.*
FROM
(VALUES ('A'), ('B'), ('C')) v(x)
),
CTE AS
(
SELECT CAST('A' AS VARCHAR(MAX)) AS STR, 0 AS LEN
UNION ALL
SELECT (CTE.STR + ',' + vals.x), CTE.LEN + 1
FROM
CTE
JOIN vals
ON vals.x >= RIGHT(CTE.STR, 1)
WHERE CTE.LEN < 19
)
INSERT INTO #temp3
SELECT
ROW_NUMBER() OVER(ORDER BY STR + ',C') AS Scenario
,STR + ',C' AS Vals
,LEN
FROM
CTE
WHERE
STR + 'C' LIKE '%B%'
AND LEN = 19
;
-- Split strings created above into individual characters
WITH cte(Scenario, Value, Vals) AS
(
SELECT
Scenario
,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10)) AS Value
,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '') AS Vals
FROM #temp3
UNION ALL
SELECT
Scenario
,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10))
,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '')
FROM cte
WHERE Vals > ''
)
SELECT
Scenario
,ROW_NUMBER() OVER (PARTITION BY Scenario ORDER BY Scenario) RowNumber
,Value
,Vals
FROM cte t
I'm not exactly sure what the problem you are describing is, but the ROW_NUMBER() should use an ORDER BY clause that completely orders the rows in each partition.
When you use "PARTITION BY Scenario ORDER BY Scenario" the order in which the ROW_NUMBER() values are assigned is undefined. Try something like
WITH cte(Scenario, depth, Value, Vals) AS
(
SELECT
Scenario, 0 depth
,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10)) AS Value
,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '') AS Vals
FROM #temp3
UNION ALL
SELECT
Scenario, depth+1
,CAST(LEFT(Vals, CHARINDEX(',',Vals+',')-1) AS VARCHAR(10))
,STUFF(Vals, 1, CHARINDEX(',',Vals+','), '')
FROM cte
WHERE Vals > ''
)
SELECT
Scenario
,depth
,ROW_NUMBER() OVER (PARTITION BY Scenario ORDER BY depth ) RowNumber
,Value
,Vals
FROM cte t

How to group/List all nodes of a undirected graph using teradata sql

I have data for many diff. set of undirected graphs in a table (like adjacent list relationship, one node is connected which all node) and I need to group all individual undirected graphs.
Eg: all nodes of the particular undirected graphs will be in a group & group name will be the min. of the node.
sel d.adj_node, min(d.adj_node) Over (Partition By a.node) as grp
table a
left join table b
on a.adj_node=b.node
left join table c
on b.adj_node=c.node
​left join table d
​on c.adj_node=d.node​;
Now, I am doing a self-join for 4,5 times and then on top that query doing partitioning it to get the desired output. But doing self-join 4 5 times is creating performance issue.
So, need some recursive sql, stored procedure or some other logic to do the same for all levels. Input Data & Required Output will be like this link Looking for some suggestions.
Input Table
node adj_node
1 2
2 1
2 3
2 5
2 6
2 7
3 2
3 4
4 3
4 5
4 6
4 7
5 2
5 4
6 2
6 4
6 8
7 2
7 4
8 6
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
10 11
11 10
11 13
11 14
12 13
12 14
13 11
13 12
13 14
14 11
14 12
14 13
10 10
11 11
12 12
13 13
14 14
Output
node grp
1 1
2 1
3 1
4 1
5 1
6 1
7 1
8 1
10 10
11 10
12 10
13 10
14 10
I just remembered that I did something similar before using updates on a temp table.
The best way to implement this would be a Stored Procedure with a loop in it:
CREATE VOLATILE TABLE vt_tab AS
(
SELECT DISTINCT NODE , adj_node, NODE AS grp
FROM tab AS t1
WHERE adj_node <> NODE
) WITH DATA
ON COMMIT PRESERVE ROWS
;
-- REPEAT this update UNTIL activity_count = 0
UPDATE vt_tab FROM
(
SELECT t2.NODE, MIN(t1.grp) AS mingrp
FROM vt_tab AS t1 JOIN vt_tab AS t2
ON t1.adj_node = t2.NODE
AND t1.grp < t2.grp
GROUP BY t2.NODE
) x
SET grp = mingrp
WHERE vt_tab.NODE = x.NODE
;
--get the final result
SEL DISTINCT NODE,grp
FROM vt_tab
ORDER BY 1
;
Recursion might be possible, but there's a high probability that it will blow your spool because you need repeated m:n joins and only the final Select allows to reduce the result rows.
A solution with a recursive CTE:
with cte as
(
select node as node, node as grp
from Tabl_1
Union all
select C.node, T.adj_node
from CTE C inner join Tabl_1 T on C.grp = T.node
where T.adj_node < C.grp
)
select node, MIN(grp) as grp
from cte
group by node
order by node
== EDIT 1 ==
Here is a new version to reflect your point.
with cte as
(
select node as node, node as grp, ',' + CAST(node as varchar(max)) + '-' + CAST(node as varchar(max)) + ',' as pair
from Tabl_1
Union all
select C.node, T.adj_node, C.pair + CAST(C.node as varchar(max)) + '-' + CAST(T.adj_node as varchar(max)) + ','
from CTE C inner join Tabl_1 T on C.grp = T.node
where C.pair not like '%,' + CAST(C.node as varchar(max)) + '-' + CAST(T.adj_node as varchar(max)) + ',%'
)
select node, MIN(grp) as grp
from cte
group by node
order by node

Select Data based on Sum of another columns value

I have a Table with Data as
RowIndex Id TicketCount
1 23 1
2 25 2
3 3 1
4 14 1
5 16 1
6 18 1
7 1 1
8 6 1
9 15 1 ===> at this row the sum of Ticket Count is 10
10 22 1
11 27 1
12 24 1
13 26 2
14 9 1
15 19 1
From this Data I want to Select All Records where The Sum of Ticket Count will be equal to 10(user input value)
In the Given data I want to Select all Records till Row Index 9.
Output should be:
RowIndex Id TicketCount
1 23 1
2 25 2
3 3 1
4 14 1
5 16 1
6 18 1
7 1 1
8 6 1
9 15 1
SQL Server 2008 doesn't have the cumulative sum function. I implement it using a correlated subquery:
select RowIndex, Id, TicketCount
from (select t.*,
(select sum(TicketCount)
from t t2
where t2.RowIndex <= t.RowIndex
) as cumTicketCount
from t
) t
where cumTicketCount <= 10;
In SQL Server 2012, you can phrase this using a window function:
select RowIndex, Id, TicketCount
from (select t.*, sum(TicketCount) over (order by RowIndex) as CumTicketCount
from t
) t
where cumTicketCount <= 10;
You can do it using recursive CTE:
WITH RCTE AS
(
SELECT *, TicketCount AS Total
FROM Table1
WHERE RowIndex = 1
UNION ALL
SELECT t.*, r.Total + t.TicketCount
FROM RCTE r
INNER JOIN Table1 t ON r.RowIndex + 1 = t.RowIndex
WHERE r.Total + t.TicketCount <= 10 --your input value
)
SELECT * FROM RCTE
SQLFiddle DEMO