I'd like to divide the data into separate groups (chunks) based on the value in the column. If the value increase above certain threshold, the value in the "group" should increase by 1.
This would be easy to achieve in MySQL, by doing CASE WHEN #val > 30 THEN #row_no + 1 ELSE #row_no END however I am using Amazon Redshift where this is not allowed.
Sample fiddle: http://sqlfiddle.com/#!15/00b3aa/6
Suggested output:
ID
Value
Group
1
11
1
2
11
1
3
22
1
4
11
1
5
35
2
6
11
2
7
11
2
8
11
2
9
66
3
10
11
3
A cumulative sum should do what you want:
SELECT *, sum((val>=30)::INTEGER) OVER (ORDER BY id BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM mydata ORDER BY id;
id | val | sum
----+-----+-----
1 | 11 | 0
2 | 11 | 0
3 | 22 | 0
4 | 11 | 0
5 | 35 | 1
6 | 11 | 1
7 | 11 | 1
8 | 11 | 1
9 | 66 | 2
10 | 11 | 2
I have the following table in oracle:
ID field_1 field_2
1 1-5 1-5
1 20-30 55-65
2 1-8 10-17
2 66-72 80-86
I need to convert this table to the following format where field_1 and field_2 must be matched linearly:
ID field_1 field_2
1 1 1
1 2 2
1 3 3
1 4 4
1 5 5
1 20 55
1 21 56
1 22 57
1 23 58
1 24 59
1 25 60
1 26 61
1 27 62
1 28 63
1 29 64
1 30 65
2 1 10
2 2 11
2 3 12
2 4 13
2 5 14
2 6 15
2 7 16
2 8 17
2 66 80
2 67 81
2 68 82
2 69 83
2 70 84
2 71 85
2 72 86
what is the easiest and fastest way to accomplish this, knowing that the original table contains thousands of records
The lateral clause, used below, is available since Oracle 12.1. For older versions, a connect by hierarchical query is still probably the fastest, but it will need to be written with a bit more care (and it will be slower than using connect by in a lateral join).
Of course, the big assumption is that the inputs are always in the form number-dash-number, and that the difference between the upper and the lower bound is the same in the two columns, for each row. I am not even attempting to check for that.
select t.id, l.field_1, l.field_2
from mytable t,
lateral (select to_number(substr(field_1, 1, instr(field_1, '-') - 1))
+ level - 1 as field_1,
to_number(substr(field_2, 1, instr(field_2, '-') - 1))
+ level - 1 as field_2
from dual
connect by level <=
to_number(substr(field_1, instr(field_1, '-') + 1))
- to_number(substr(field_1, 1, instr(field_1, '-') - 1)) + 1
) l
;
One option uses a recursive query. Starting 11gR2, Oracle supports standard recursive common table expressions, so you can do:
with cte(id, field_1, field_2, max_field_1, max_field_2) as (
select
id,
to_number(regexp_substr(field_1, '^\d+')),
to_number(regexp_substr(field_2, '^\d+')),
to_number(regexp_substr(field_1, '\d+$')),
to_number(regexp_substr(field_2, '\d+$'))
from mytable
union all
select
id,
field_1 + 1,
field_2 + 1,
max_field_1,
max_field_2
from cte
where field_1 < max_field_1
)
select id, field_1, field_2 from cte order by id, field_1
This assumes that intervals on the same row have always the same length, as showned in your sample data. If that's not the case, you would to explain how you want to handle that.
Demo on DB Fiddle:
ID | FIELD_1 | FIELD_2
-: | ------: | ------:
1 | 1 | 1
1 | 2 | 2
1 | 3 | 3
1 | 4 | 4
1 | 5 | 5
1 | 20 | 55
1 | 21 | 56
1 | 22 | 57
1 | 23 | 58
1 | 24 | 59
1 | 25 | 60
1 | 26 | 61
1 | 27 | 62
1 | 28 | 63
1 | 29 | 64
1 | 30 | 65
2 | 1 | 10
2 | 2 | 11
2 | 3 | 12
2 | 4 | 13
2 | 5 | 14
2 | 6 | 15
2 | 7 | 16
2 | 8 | 17
2 | 66 | 80
2 | 67 | 81
2 | 68 | 82
2 | 69 | 83
2 | 70 | 84
2 | 71 | 85
2 | 72 | 86
You can use the cross join with generated values as follows:
SELECT ID,
to_number(regexp_substr(field_1, '[0-9]+',1,1)) + column_value - 1 AS FIELD_1,
to_number(regexp_substr(field_2, '[0-9]+',1,1)) + column_value - 1 AS FIELD_2
FROM your_table
cross join
table(cast(multiset(select level from dual
connect by level <=
to_number(regexp_substr(field_1, '[0-9]+',1,2))
- to_number(regexp_substr(field_1, '[0-9]+',1,1))
+ 1
) as sys.odcinumberlist))
ORDER BY 1,2
I have a similar table to this one
ID | CountryID
1 | 22
1 | 22
2 | 19
3 | 0
3 | 14
3 | 18
3 | 21
3 | 22
3 | 23
4 | 19
5 | 9
5 | 9
6 | 14
and I want to group by the first ID column but select only rows, where the CountryID has the same value throughout an ID. The resulting table should look like
ID | CountryID
1 | 22
2 | 19
4 | 19
5 | 9
6 | 14
Any ideas?
I think the following query should work:
SELECT ID, MAX(CountryID)
FROM Table1
GROUP BY ID
HAVING MIN(CountryID) = MAX(CountryID)
SELECT ID, count(distinct CountryID)
FROM Table1
GROUP BY ID
HAVING count(distinct CountryID)=1
How do I select a pair (number, number) where tabid is equal for two numbers from the following table (i.e: number 7 and 11 have the same tabid):
tabid | number
---------+--------
1 | 6
1 | 6
2 | 7
3 | 8
4 | 8
5 | 10
5 | 11
6 | 12
6 | 11
5 | 6
4 | 7
3 | 8
2 | 11
The result of this should be:
number | number
---------+--------
7 | 11
7 | 8
10 | 11
11 | 12
6 | 10
6 | 11
Is this what you're looking for:
select
t1.number, t2.number
from t t1, t t2
where t1.tabid = t2.tabid
and t1.number < t2.number;
produces:
NUMBER NUMBER
---------- ----------
6 10
6 11
7 8
7 11
10 11
11 12
Use array_agg to concatenate the tabid's into an array. Thereafter self join this cte to check if one array is an overlap of the other using the array operator &&.
with concatenated as (
select array_agg(tabid) as arr_tab, num
from t
group by num
)
select c1.num,c2.num
from concatenated c1
join concatenated c2 on c1.num < c2.num
where c2.arr_tab && c1.arr_tab
order by 1,2
Sample Demo
H i have column which consists of 32 rows. like
ColumnA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
while retrieving i want (4 X 8) means 4 columns 8 Rows.The Result should be like this
A B C D
1 9 17 25
2 10 18 26
3 11 19 27
4 12 20 28
5 13 21 29
6 14 22 30
7 15 23 31
8 16 24 32
Give me an Idea.
Something like this using CTE and row_number():
Fiddle demo
declare #numRows int = 8
;with cte as (
select columnA X, row_number() over (order by columnA) rn
from Table1
)
select c1.x A, c2.x B, c3.x C, c4.x D
from cte c1
left join cte c2 on c1.rn = c2.rn-#numRows
left join cte c3 on c1.rn = c3.rn-(#numRows * 2)
left join cte c4 on c1.rn = c4.rn-(#numRows * 3)
where c1.rn <= #numRows
results:
| A | B | C | D |
|---|----|----|----|
| 1 | 9 | 17 | 25 |
| 2 | 10 | 18 | 26 |
| 3 | 11 | 19 | 27 |
| 4 | 12 | 20 | 28 |
| 5 | 13 | 21 | 29 |
| 6 | 14 | 22 | 30 |
| 7 | 15 | 23 | 31 |
| 8 | 16 | 24 | 32 |
I can't see how to do it with a pivot given the lack of extra columns in the query making aggregation difficult. If you do have other columns then pivot would be less code consuming; but I'm not a pivot expert. You can do it easily enough with a few joins … used my tally table to generate the list of integers
SELECT
aa.StaticInteger as A,
bb.StaticInteger as B,
cc.StaticInteger as C,
dd.StaticInteger as D
FROM
tblTally aa
LEFT OUTER JOIN
(
SELECT
StaticInteger
FROM
tblTally
WHERE
StaticInteger BETWEEN 9 AND 16
) bb
ON
aa.StaticInteger = bb.StaticInteger - 8
LEFT OUTER JOIN
(
SELECT
StaticInteger
FROM
tblTally
WHERE
StaticInteger BETWEEN 17 AND 24
) cc
ON
bb.StaticInteger = cc.StaticInteger - 8
LEFT OUTER JOIN
(
SELECT
StaticInteger
FROM
tblTally
WHERE
StaticInteger BETWEEN 25 AND 32
) dd
ON
cc.StaticInteger = dd.StaticInteger - 8
WHERE
aa.StaticInteger BETWEEN 1 AND 8
Returns
A B C D
1 9 17 25
2 10 18 26
3 11 19 27
4 12 20 28
5 13 21 29
6 14 22 30
7 15 23 31
8 16 24 32