how use column from one subquery to anoter subquery - sql

I have two subquery .i want put p.price from first subquery into secound subquery(place XXX) . but get error => ORA-00904: "P"."PRICE": invalid identifier
select
p.product_id,
p.price,
l.delegation,
l.state
from users
inner join (
select
start_date,
price,
product_id,
row_number() over (partition by serial order by start_date desc ) as rn
from prices
) p on users.serial = p.serial
inner join (
select
sso_id ,
delegation,
state,
updated_at,
row_number() over (partition by state order by updated_at asc) as tl
from payments
where state = 'green' or (state = 'yellow' and delegation > XXX)
) l on users.sso_id= l.sso_id
where
p.rn = 1
and l.tl = 1

Join on the PAYMENTS table and filter the rows to exclude the invalid state/delegation rows and then generate the ROW_NUMBER and filter to find the first row per partition:
Oracle Setup:
CREATE TABLE users ( serial, sso_id ) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 2 FROM DUAL UNION ALL
SELECT 3, 3 FROM DUAL;
CREATE TABLE prices ( serial, start_date, price, product_id ) AS
SELECT 1, DATE '2019-01-01', 20, 1 FROM DUAL UNION ALL
SELECT 1, DATE '2019-02-01', 30, 2 FROM DUAL UNION ALL
SELECT 1, DATE '2019-03-01', 25, 3 FROM DUAL UNION ALL
SELECT 2, DATE '2019-01-01', 20, 1 FROM DUAL UNION ALL
SELECT 2, DATE '2019-02-01', 25, 2 FROM DUAL UNION ALL
SELECT 2, DATE '2019-03-01', 30, 3 FROM DUAL UNION ALL
SELECT 3, DATE '2019-01-01', 40, 3 FROM DUAL;
CREATE TABLE payments ( sso_id, delegation, state, updated_at ) AS
SELECT 1, 20, 'green', DATE '2019-01-01' FROM DUAL UNION ALL
SELECT 1, 30, 'green', DATE '2019-02-01' FROM DUAL UNION ALL
SELECT 1, 27, 'green', DATE '2019-03-01' FROM DUAL UNION ALL
SELECT 1, 22, 'green', DATE '2019-04-01' FROM DUAL UNION ALL
SELECT 1, 26, 'yellow', DATE '2019-05-01' FROM DUAL UNION ALL
SELECT 2, 31, 'yellow', DATE '2019-01-01' FROM DUAL UNION ALL
SELECT 2, 31, 'green', DATE '2019-02-01' FROM DUAL UNION ALL
SELECT 3, 30, 'green', DATE '2019-01-01' FROM DUAL UNION ALL
SELECT 3, 30, 'yellow', DATE '2019-01-01' FROM DUAL UNION ALL
SELECT 3, 50, 'yellow', DATE '2019-02-01' FROM DUAL;
Query:
SELECT product_id,
price,
delegation,
state
FROM (
select p.product_id,
p.price,
l.delegation,
l.state,
row_number() over ( partition by l.sso_id, l.state order by l.updated_at asc) as tl
from users
inner join (
select serial,
start_date,
price,
product_id,
row_number() over (partition by serial order by start_date desc ) as rn
from prices
) p
on ( users.serial = p.serial AND p.rn = 1 )
inner join payments l
on ( users.sso_id = l.sso_id AND ( l.state = 'green' or (l.state = 'yellow' and l.delegation > p.price ) ) )
)
where tl = 1
Output:
PRODUCT_ID | PRICE | DELEGATION | STATE
---------: | ----: | ---------: | :-----
3 | 25 | 20 | green
3 | 25 | 26 | yellow
3 | 30 | 31 | green
3 | 30 | 31 | yellow
3 | 40 | 30 | green
3 | 40 | 50 | yellow
db<>fiddle here

Use it in the main WHERE clause
select
p.product_id,
p.price,
l.delegation,
l.state
from users
inner join (
select
start_date,
price,
product_id,
row_number() over (partition by serial order by start_date desc ) as rn
from prices
) p on users.serial = p.serial
inner join (
select
sso_id ,
delegation,
state,
updated_at,
row_number() over (partition by state order by updated_at asc) as tl
from payments
where state = 'green' or state = 'yellow'
) l on users.sso_id= l.sso_id
where
p.rn = 1
and l.tl = 1
-- add following condition
and l.delegation > p.price

We need to rejoin your users and price table to your payment to get matching p.price
SELECT p.product_id,
p.price,
l.delegation,
l.state
FROM users
INNER JOIN
(SELECT start_date,
price,
product_id,
ROW_NUMBER() OVER (PARTITION BY serial ORDER BY start_date DESC) AS rn
FROM prices) p ON users.serial = p.serial
INNER JOIN
(SELECT y.sso_id,
y.delegation,
y.state,
y.updated_at,
ROW_NUMBER () OVER (PARTITION BY y.state ORDER BY y.updated_at ASC) AS tl
FROM payments y
INNER JOIN users u ON u.sso_id = y.sso_id
INNER JOIN prices p ON p.serial = y.serial
WHERE tl.state = 'green' OR (tl.state = 'yellow' AND tl.delegation > p.price)) l ON users.sso_id = l.sso_id
WHERE
AND p.rn = 1 AND l.tl = 1

Related

Find purchase if same item on different days

I'm trying to find customers that bought the same item more than once in different days. I got it partially working. I can't get the customer first/last name and item_name without adding it to the group by clause. In addition, I want to include a count if how many times the same uten was purchased on different days.
I suspect that group by is probably not the best solution. Would this be better solved using a self JOIN or perhaps a lead?
CREATE TABLE customers
(CUSTOMER_ID, FIRST_NAME, LAST_NAME) AS
SELECT 1, 'Abby', 'Katz' FROM DUAL UNION ALL
SELECT 2, 'Lisa', 'Saladino' FROM DUAL UNION ALL
SELECT 3, 'Jerry', 'Torchiano' FROM DUAL;
CREATE TABLE items
(PRODUCT_ID, PRODUCT_NAME) AS
SELECT 100, 'Black Shoes' FROM DUAL UNION ALL
SELECT 101, 'Brown Shoes' FROM DUAL UNION ALL
SELECT 102, 'White Shoes' FROM DUAL;
CREATE TABLE purchases
(CUSTOMER_ID, PRODUCT_ID, QUANTITY, PURCHASE_DATE) AS
SELECT 1, 100, 1, TIMESTAMP'2022-10-11 09:54:48' FROM DUAL UNION ALL
SELECT 1, 100, 1, TIMESTAMP '2022-10-11 19:04:18' FROM DUAL UNION ALL
SELECT 2, 101,1, TIMESTAMP '2022-10-11 09:54:48' FROM DUAL UNION ALL
SELECT 2,101,1, TIMESTAMP '2022-10-17 19:04:18' FROM DUAL UNION ALL
SELECT 3, 101,1, TIMESTAMP '2022-10-11 09:54:48' FROM DUAL UNION ALL
SELECT 3,102,1, TIMESTAMP '2022-10-17 19:04:18' FROM DUAL;
With CTE as (
SELECT customer_id
,product_id
,trunc(purchase_date)
FROM purchases
GROUP BY customer_id
,product_id
,trunc(purchase_date)
)
SELECT customer_id, product_id
FROM CTE
GROUP BY customer_id ,product_id
HAVING COUNT(1)>1
I would use exists logic here:
SELECT DISTINCT c.first_name, c.last_name
FROM customers c
INNER JOIN purchases p
ON p.customer_id = c.customer_id
WHERE EXISTS (
SELECT 1
FROM purchases p2
WHERE p2.customer_id = p.customer_id AND
p2.product_id = p.product_id AND
TRUNC(p2.purchase_date) <> TRUNC(p.purchase_date)
);
In plain English, the above query says to find all customers who bought the same product but on different dates.
This might be one option: use count function in its analytic form and the fetch rows where that count is larger than 1; according to data you posted, it is Lisa who bought brown shoes on two different dates.
SQL> WITH
2 temp
3 AS
4 ( SELECT c.first_name,
5 i.product_name,
6 TRUNC (p.purchase_date),
7 COUNT (*) OVER (PARTITION BY c.first_name, i.product_name) cnt
8 FROM purchases p
9 JOIN customers c ON c.customer_id = p.customer_id
10 JOIN items i ON i.product_id = p.product_id
11 GROUP BY c.first_name, i.product_name, TRUNC (p.purchase_date))
12 SELECT DISTINCT first_name, product_name, cnt
13 FROM temp
14 WHERE cnt > 1;
FIRST PRODUCT_NAM CNT
----- ----------- ----------
Lisa Brown Shoes 2
SQL>

How to select all data before current_state in history data in Oracle SQL?

So I have data look like in Picture.
Column name Track is to show the step of the state.
Column name Current_state is the status of the app right now.
Column name Current_state_hist is the history of the status.
So right now the current status now is AP.
I want to Select all the status before the last status right now (AP in Track 13 & 14) without remove the status AP in track 5 - 8.
Can somebody help me for this case? Thank you
Example of the data
You can use EXISTS for that:
Schema and insert statements:
create table table1(id int, track int, current_state varchar(10), current_state_hist varchar(10), total_unit int);
insert into table1 values(1,1,'AP','OD',1)
insert into table1 values(1,2,'AP','OD',1)
insert into table1 values(1,3,'AP','OD',1)
insert into table1 values(1,4,'AP','OD',1)
insert into table1 values(1,5,'AP','AP',1)
insert into table1 values(1,6,'AP','AP',1)
insert into table1 values(1,7,'AP','AP',1)
insert into table1 values(1,8,'AP','AP',1)
insert into table1 values(1,9,'AP','OD',1)
insert into table1 values(1,10,'AP','OD',1)
insert into table1 values(1,11,'AP','OD',1)
insert into table1 values(1,12,'AP','OD',1)
insert into table1 values(1,13,'AP','AP',1)
insert into table1 values(1,14,'AP','AP',1)
Query:
SELECT ID,TRACK,CURRENT_STATE,CURRENT_STATE_HIST,TOTAL_UNIT
FROM TABLE1 T1
WHERE EXISTS
(
SELECT 1 FROM TABLE1 T2
WHERE CURRENT_STATE<>CURRENT_STATE_HIST
AND T1.TRACK<=T2.TRACK
)
Output:
ID
TRACK
CURRENT_STATE
CURRENT_STATE_HIST
TOTAL_UNIT
1
1
AP
OD
1
1
2
AP
OD
1
1
3
AP
OD
1
1
4
AP
OD
1
1
5
AP
AP
1
1
6
AP
AP
1
1
7
AP
AP
1
1
8
AP
AP
1
1
9
AP
OD
1
1
10
AP
OD
1
1
11
AP
OD
1
1
12
AP
OD
1
db<>fiddle here
You can find the latest status without having to query the table twice using the ROW_NUMBER analytic function:
SELECT id, track, current_state, current_state_hist, total_unit
FROM (
SELECT t.*,
ROW_NUMBER() OVER (ORDER BY track DESC)
- ROW_NUMBER() OVER (
PARTITION BY current_state_hist ORDER BY track DESC
) AS rn
FROM table_name t
)
WHERE rn > 0;
Or, from Oracle 12:
SELECT *
FROM table_name
MATCH_RECOGNIZE(
ORDER BY track DESC
ALL ROWS PER MATCH
PATTERN ( ^ {- same_hist+ -} any_hist* )
DEFINE
same_hist AS FIRST(current_state_hist) = current_state_hist
)
Which, for the sample data:
CREATE TABLE table_name (id, track, current_state, current_state_hist, total_unit) AS
SELECT 1, 1, 'AP', 'OD', 1 FROM DUAL UNION ALL
SELECT 1, 2, 'AP', 'OD', 1 FROM DUAL UNION ALL
SELECT 1, 3, 'AP', 'OD', 1 FROM DUAL UNION ALL
SELECT 1, 4, 'AP', 'OD', 1 FROM DUAL UNION ALL
SELECT 1, 5, 'AP', 'AP', 1 FROM DUAL UNION ALL
SELECT 1, 6, 'AP', 'AP', 1 FROM DUAL UNION ALL
SELECT 1, 7, 'AP', 'AP', 1 FROM DUAL UNION ALL
SELECT 1, 8, 'AP', 'AP', 1 FROM DUAL UNION ALL
SELECT 1, 9, 'AP', 'OD', 1 FROM DUAL UNION ALL
SELECT 1, 10, 'AP', 'OD', 1 FROM DUAL UNION ALL
SELECT 1, 11, 'AP', 'OD', 1 FROM DUAL UNION ALL
SELECT 1, 12, 'AP', 'OD', 1 FROM DUAL UNION ALL
SELECT 1, 13, 'AP', 'AP', 1 FROM DUAL UNION ALL
SELECT 1, 14, 'AP', 'AP', 1 FROM DUAL;
Both output:
ID
TRACK
CURRENT_STATE
CURRENT_STATE_HIST
TOTAL_UNIT
1
12
AP
OD
1
1
11
AP
OD
1
1
10
AP
OD
1
1
9
AP
OD
1
1
8
AP
AP
1
1
7
AP
AP
1
1
6
AP
AP
1
1
5
AP
AP
1
1
4
AP
OD
1
1
3
AP
OD
1
1
2
AP
OD
1
1
1
AP
OD
1
I want the ouput is to select all except the last 2 row... because it's current status...
If you just want to ignore the last 2 rows then: order the rows, then assign a ROWNUM pseudo-column to the ordered rows, then filter on the ROWNUM to exclude the latest two rows:
SELECT *
FROM (
SELECT t.*,
ROWNUM AS rn
FROM (
SELECT *
FROM table_name
ORDER BY track DESC
) t
)
WHERE rn >= 3;
Or, using the ROW_NUMBER analytic function:
SELECT *
FROM (
SELECT t.*,
ROW_NUMMBER() OVER (ORDER BY track DESC) AS rn
FROM table_name t
)
WHERE rn >= 3;
Or, from Oracle 12:
SELECT *
FROM table_name
ORDER BY track DESC
OFFSET 2 ROWS
FETCH FIRST 100 PERCENT ONLY;
db<>fiddle here

Oracle SQL - Assign queue of customers to list of Employees

I need to distribute list of employees to customers.
For example:
Table 1: List of Employees: A, B & C
Table 2: List of Customers: 1, 2, 3, 4, 5, 6, 7, 8, 9
The needed result:
|------------|------------|
| Customers | Employees |
|------------|------------|
| 1 | A |
| 2 | B |
| 3 | C |
| 4 | A |
| 5 | B |
| 6 | C |
| 7 | A |
| 8 | B |
| 9 | C |
|------------|------------|
You can use ROW_NUMBER() to assign a number on the fly to the employees, and the MOD() to do the rolling join. For example:
select
c.id,
e.name
from (
select t.*,
row_number() over(order by name) as rn
from employees t
) e
join customers c on e.rn =
mod(rn, (select count(*) from customers)) + 1
Number all rows, then use a modulo function for the join:
with e as
(
select employee, row_number() over (order by employee) as rn
from employees
)
, c as
(
select customer, row_number() over (order by customer) as rn
from customers
)
select c.customer, e.employee
from c
join e on e.rn - 1 = mod(c.rn - 1, (select count(*) from e))
order by c.customer;
Demo: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=43a6ca7469dff023d5513fa209e33ea7
You can use MOD function for this purpose. Try below code.
CREATE TABLE EMP
AS
SELECT 'A' AS EMP FROM DUAL
UNION ALL
SELECT 'B' AS EMP FROM DUAL
UNION ALL
SELECT 'C' AS EMP FROM DUAL;
CREATE TABLE CUST
AS
SELECT '1' AS CUST FROM DUAL
UNION ALL
SELECT '2' AS CUST FROM DUAL
UNION ALL
SELECT '3' AS CUST FROM DUAL
UNION ALL
SELECT '4' AS CUST FROM DUAL
UNION ALL
SELECT '5' AS CUST FROM DUAL
UNION ALL
SELECT '6' AS CUST FROM DUAL
UNION ALL
SELECT '7' AS CUST FROM DUAL
UNION ALL
SELECT '8' AS CUST FROM DUAL
UNION ALL
SELECT '9' AS CUST FROM DUAL;
SELECT CUST, EMP
FROM (SELECT ROW_NUMBER () OVER (PARTITION BY 1 ORDER BY EMP) AS ID, EMP
FROM EMP) EMP
INNER JOIN CUST ON MOD (TO_NUMBER (CUST.CUST) - 1, 3) = EMP.ID - 1
ORDER BY 1;

Counting one field of table in other table

I wrote a script in oracle. But it does not give me the result that i want.
I need this one, imagine i have two table. Order_table and book table.
My order table is like this
ORDER_TABLE Table
ID TYPE_ID VALUE_ID
1 11 null
2 11 null
3 11 null
4 12 null
5 11 null
Book Table
ID ORDER_TYPE DELETED
1 1 F
2 null F
3 5 F
4 5 F
5 4 F
6 4 F
7 3 T
My script is like this
Select *
From (
Select Newtable.Counter As Value_id,
o.Id As Id,
o.Type_id As Type_id
From (
Select (Count B.Order_Type) As Counter,
B.Order_Type As Id
From Book B
Where B.Deleted = 'F'
Group By B.Order_Type
Order By Count(B.Order_Type) Desc
) newtable,
order_table o
where o.id = newtable.id
and o.type_id = 11
)
order by id asc;
Result is like this.
Value_ID TYPE_ID ID
2 11 5
2 11 4
1 11 1
It is not showing that second and third id has 0 count, Have can i show 0 count too ?
Result should be like this.
Value_ID TYPE_ID ID
2 11 5
2 11 4
1 11 1
0 11 2
0 11 3
First, do not use implicit JOIN syntax(comma separated), that's one of the reason this mistakes are hard to catch! Use the proper JOIN syntax.
Second, your problem is that you need a left join, not an inner join , so try this:
Select *
From (Select coalesce(Newtable.Counter,0) As Value_id,
o.Id As Id,
o.Type_id As Type_id
From order_table o
LEFT JOIN (Select Count(B.Order_Type) As Counter, B.Order_Type As Id
From Book B
Where B.Deleted = 'F'
Group By B.Order_Type
Order By Count(B.Order_Type) Desc) newtable
ON(o.id = newtable.id)
WHERE o.type_id = 11)
order by id asc;
Oracle Setup:
CREATE TABLE order_table ( id, type_id, value_id ) AS
SELECT 1, 11, CAST( NULL AS INT ) FROM DUAL UNION ALL
SELECT 2, 11, CAST( NULL AS INT ) FROM DUAL UNION ALL
SELECT 3, 11, CAST( NULL AS INT ) FROM DUAL UNION ALL
SELECT 4, 12, CAST( NULL AS INT ) FROM DUAL UNION ALL
SELECT 5, 11, CAST( NULL AS INT ) FROM DUAL;
CREATE TABLE book ( id, order_type, deleted ) AS
SELECT 1, 1, 'F' FROM DUAL UNION ALL
SELECT 2, NULL, 'F' FROM DUAL UNION ALL
SELECT 3, 5, 'F' FROM DUAL UNION ALL
SELECT 4, 5, 'F' FROM DUAL UNION ALL
SELECT 5, 4, 'F' FROM DUAL UNION ALL
SELECT 6, 4, 'F' FROM DUAL UNION ALL
SELECT 7, 3, 'T' FROM DUAL;
Query:
SELECT COUNT( b.order_type ) AS value_id,
o.id,
o.order_type
FROM order_table o
LEFT OUTER JOIN
book b
ON ( o.id = b.order_type AND b.deleted = 'F' )
WHERE o.type_id = 11
GROUP BY o.id, o.type_id
ORDER BY value_id DESC, id DESC;
Output:
VALUE_ID ID TYPE_ID
-------- -- -------
2 5 11
1 1 11
0 3 11
0 2 11
However, if you did want to use the legacy Oracle comma-join syntax then you can get the same result with:
SELECT COUNT( b.order_type ) AS value_id,
o.id,
o.order_type
FROM order_table o,
book b
WHERE o.type_id = 11
AND b.order_type (+) = o.id
AND b.deleted (+) = 'F'
GROUP BY o.id, o.type_id
ORDER BY value_id DESC, id DESC;
But please don't as the ANSI/ISO joins are much easier to comprehend the join conditions.
You could also do this with a scalar subquery, which may or may not be more performant than the left join versions described in the other answers. (Quite possibly, the optimizer may rewrite it to be a left join anyway!):
with order_table ( id, type_id, value_id ) as (select 1, 11, cast( null as int ) from dual union all
select 2, 11, cast( null as int ) from dual union all
select 3, 11, cast( null as int ) from dual union all
select 4, 12, cast( null as int ) from dual union all
select 5, 11, cast( null as int ) from dual),
book ( id, order_type, deleted ) as (select 1, 1, 'F' from dual union all
select 2, null, 'F' from dual union all
select 3, 5, 'F' from dual union all
select 4, 5, 'F' from dual union all
select 5, 4, 'F' from dual union all
select 6, 4, 'F' from dual union all
select 7, 3, 'T' from dual)
-- end of mimicking your tables; you wouldn't need the above subqueries as you already have the tables.
-- See SQL below:
select (select count(*) from book bk where bk.deleted = 'F' and bk.order_type = ot.id) value_id,
ot.type_id,
ot.id
from order_table ot
order by value_id desc,
id desc;
VALUE_ID TYPE_ID ID
---------- ---------- ----------
2 11 5
2 12 4
1 11 1
0 11 3
0 11 2

SQL Grouping by Ranges

I have a data set that has timestamped entries over various sets of groups.
Timestamp -- Group -- Value
---------------------------
1 -- A -- 10
2 -- A -- 20
3 -- B -- 15
4 -- B -- 25
5 -- C -- 5
6 -- A -- 5
7 -- A -- 10
I want to sum these values by the Group field, but parsed as it appears in the data. For example, the above data would result in the following output:
Group -- Sum
A -- 30
B -- 40
C -- 5
A -- 15
I do not want this, which is all I've been able to come up with on my own so far:
Group -- Sum
A -- 45
B -- 40
C -- 5
Using Oracle 11g, this is what I've hobbled togther so far. I know that this is wrong, by I'm hoping I'm at least on the right track with RANK(). In the real data, entries with the same group could be 2 timestamps apart, or 100; there could be one entry in a group, or 100 consecutive. It does not matter, I need them separated.
WITH SUB_Q AS
(SELECT K_ID
, GRP
, VAL
-- GET THE RANK FROM TIMESTAMP TO SEPARATE GROUPS WITH SAME NAME
, RANK() OVER(PARTITION BY K_ID ORDER BY TMSTAMP) AS RNK
FROM MY_TABLE
WHERE K_ID = 123)
SELECT T1.K_ID
, T1.GRP
, SUM(CASE
WHEN T1.GRP = T2.GRP THEN
T1.VAL
ELSE
0
END) AS TOTAL_VALUE
FROM SUB_Q T1 -- MAIN VALUE
INNER JOIN SUB_Q T2 -- TIMSTAMP AFTER
ON T1.K_ID = T2.K_ID
AND T1.RNK = T2.RNK - 1
GROUP BY T1.K_ID
, T1.GRP
Is it possible to group in this way? How would I go about doing this?
I approach this problem by defining a group which is the different of two row_number():
select group, sum(value)
from (select t.*,
(row_number() over (order by timestamp) -
row_number() over (partition by group order by timestamp)
) as grp
from my_table t
) t
group by group, grp
order by min(timestamp);
The difference of two row numbers is constant for adjacent values.
A solution using LAG and windowed analytic functions:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE TEST ( "Timestamp", "Group", Value ) AS
SELECT 1, 'A', 10 FROM DUAL
UNION ALL SELECT 2, 'A', 20 FROM DUAL
UNION ALL SELECT 3, 'B', 15 FROM DUAL
UNION ALL SELECT 4, 'B', 25 FROM DUAL
UNION ALL SELECT 5, 'C', 5 FROM DUAL
UNION ALL SELECT 6, 'A', 5 FROM DUAL
UNION ALL SELECT 7, 'A', 10 FROM DUAL;
Query 1:
WITH changes AS (
SELECT t.*,
CASE WHEN LAG( "Group" ) OVER ( ORDER BY "Timestamp" ) = "Group" THEN 0 ELSE 1 END AS hasChangedGroup
FROM TEST t
),
groups AS (
SELECT "Group",
VALUE,
SUM( hasChangedGroup ) OVER ( ORDER BY "Timestamp" ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS grp
FROM changes
)
SELECT "Group",
SUM( VALUE )
FROM Groups
GROUP BY "Group", grp
ORDER BY grp
Results:
| Group | SUM(VALUE) |
|-------|------------|
| A | 30 |
| B | 40 |
| C | 5 |
| A | 15 |
This is typical "star_of_group" problem (see here: https://timurakhmadeev.wordpress.com/2013/07/21/start_of_group/)
In your case, it would be as follows:
with t as (
select 1 timestamp, 'A' grp, 10 value from dual union all
select 2, 'A', 20 from dual union all
select 3, 'B', 15 from dual union all
select 4, 'B', 25 from dual union all
select 5, 'C', 5 from dual union all
select 6, 'A', 5 from dual union all
select 7, 'A', 10 from dual
)
select min(timestamp), grp, sum(value) sum_value
from (
select t.*
, sum(start_of_group) over (order by timestamp) grp_id
from (
select t.*
, case when grp = lag(grp) over (order by timestamp) then 0 else 1 end
start_of_group
from t
) t
)
group by grp_id, grp
order by min(timestamp)
;