First 7 days sales - sql

I want to check the sum of the amount for an item from its first day of sale next 7 days. Basically, I want to check the sum of sales for the first 7 days.
I am using the below query.
select item, sum(amt)
from table
where first_sale_dt = (first_sale_dt + 6).
When I run this query, I don't get any results.

Your code as it stands will give you no results, because you are looking at each row, and asking is the value first_sale_dt equal to a values it is not +6
You need to use a WINDOW function to look across many rows, OR self JOIN the table and filter the rows that are joined to give the result you want.
so with the CTE of data for testing:
WITH data as (
select * from values
(1, 2, '2022-03-01'::date),
(1, 4, '2022-03-04'::date),
(1, 200,'2022-04-01'::date),
(3, 20, '2022-03-01'::date)
t(item, amt, first_sale_dt)
)
this SQL show the filtered row that we are wanting to SUM, it is using a sub-select (which could be moved into a CTE) to find the "first first sale" to do the date range of.
select a.item, b.amt
from (
select
item,
min(first_sale_dt) as first_first_sale_dt
from data
group by 1
) as a
join data as b
on a.item = b.item and b.first_sale_dt <= (a.first_first_sale_dt + 6)
ITEM
AMT
1
2
1
4
3
20
and therefore with a SUM added:
select a.item, sum(b.amt)
from (
select
item,
min(first_sale_dt) as first_first_sale_dt
from data
group by 1
) as a
join data as b
on a.item = b.item and b.first_sale_dt <= (a.first_first_sale_dt + 6)
group by 1;
you get:
ITEM
SUM(B.AMT)
1
6
3
20
Sliding Window:
This is relying on dense data (1 row for every day), also the sliding WINDOW is doing work that is getting thrown away, which is a string sign this is not the performant solution and I would stick to the first solution.
WITH data as (
select * from values
(1, 2, '2022-03-01'::date),
(1, 2, '2022-03-02'::date),
(1, 2, '2022-03-03'::date),
(1, 2, '2022-03-04'::date),
(1, 2, '2022-03-05'::date),
(1, 2, '2022-03-06'::date),
(1, 2, '2022-03-07'::date),
(1, 2, '2022-03-08'::date)
t(item, amt, first_sale_dt)
)
select item,
first_sale_dt,
sum(amt) over(partition by item order by first_sale_dt rows BETWEEN current row and 6 following ) as s
,count(amt) over(partition by item order by first_sale_dt rows BETWEEN current row and 6 following ) as c
from data
order by 2;
ITEM
FIRST_SALE_DT
S
C
1
2022-03-01
14
7
1
2022-03-02
14
7
1
2022-03-03
12
6
1
2022-03-04
10
5
1
2022-03-05
8
4
1
2022-03-06
6
3
1
2022-03-07
4
2
1
2022-03-08
2
1
thus you need to then filter out some rows.
WITH data as (
select * from values
(1, 2, '2022-03-01'::date),
(1, 2, '2022-03-02'::date),
(1, 2, '2022-03-03'::date),
(1, 2, '2022-03-04'::date),
(1, 2, '2022-03-05'::date),
(1, 2, '2022-03-06'::date),
(1, 2, '2022-03-07'::date),
(1, 2, '2022-03-08'::date)
t(item, amt, first_sale_dt)
)
select item,
sum(amt) over(partition by item order by first_sale_dt rows BETWEEN current row and 6 following ) as s
from data
qualify row_number() over (partition by item order by first_sale_dt) = 1
gives:
ITEM
S
1
14

If you really want to use window function. Here is beginner friendly version
with cte as
(select *, min(sale_date) over (partition by item) as sale_start_date
from data) --thanks Simeon
select item, sum(amt) as amount
from cte
where sale_date <= sale_start_date + 6 --limit to first week
group by item;
On a side note, I suggest using dateadd instead of + on dates

Related

How to make new data is a function of last data

In SQL server I have a table just like the following table, original, and I want to update where Index ID>3
and the principle is lastaccmulated*2 + movement.
For example
where Index ID =3 accumulated = 8 * 2 + 2 =18
I tried the lag function but it can only be used in select, which means I cannot finish in one update.
Is there any sharp function to make this happen?
Table orginal
IndexID
accumulated
movement
1
5
2
2
8
2
3
0
2
4
0
2
5
0
2
Table what I want after update
IndexID
accumulated
movement
1
5
2
2
8
2
3
18
2
4
38
2
5
78
2
Just like above mention, it went wrong when I use lag function.
Try this:
DROP TABLE IF EXISTS #YOUR_TABLE
SELECT
id,
accumulated,
movement
INTO #YOUR_TABLE
FROM (
VALUES
(1, 5, 2),
(2, 8, 2),
(3, 0, 2),
(4, 0, 2),
(5, 0, 2),
(6, 0, 2)
) src (id, accumulated, movement)
;WITH
CALCULATION AS (
SELECT
id,
2 * accumulated + movement as accumulated
FROM #YOUR_TABLE
WHERE id = 2
UNION ALL
SELECT
yt.id,
2 * c.accumulated + yt.movement as accumulated
FROM CALCULATION c
JOIN #YOUR_TABLE yt ON yt.id = c.id + 1
)
UPDATE yt SET
yt.accumulated = c.accumulated
FROM #YOUR_TABLE yt
JOIN CALCULATION c ON
c.id = yt.id
WHERE
yt.id >= 3
OPTION (MAXRECURSION 0) -- To prevent recursion limitiations
SELECT * FROM #YOUR_TABLE
We are using recursive CTE here. Before UNION ALL we give values for step zero, after we have calculation based on previous step (yt.id = c.id + 1).

How to select buyers who increased their order quantity with each consecutive order

I have table Buyer
BuyId
QuantityOrdered
dateordered
1
10
2021-11-04
1
20
2022-01-22
2
50
2022-02-20
2
60
2022-05-02
3
10
2022-05-02
4
10
2022-05-02
I need to select all BuyId's who consecutively had higher QuantityOrder in each new record
buyid=1 had first order with quantity=10, second order with quantity=20
buyid=2 had first order with quantity=50, second order with quantity=60
So BuyId=1 and 2 would enter the results, while 3 and 4 would be filtered out because they had only one order or they did not have orders with consecutively higher quantities ordered
I tried with this, and I'm aware that this query gives me only buyid's who have more than one order, but I am missing the rule where I have to filter results out by quantity increased with each new order
select buyid, count(*) as ordered
from buyer
group by buyid
having count(*) >1
How would I write out that rule in a query, to select only BuyId's who had multiple orders, and in each new order they ordered higher quantities than in previous orders?
Taking advantage of the fact that count() doesn't takes into account NULL values:
select buyid from (
select buyid, count(QuantityOrdered) as norders, count(mod) as nqttyincreasing
from (
select d.*,
case when nvl(lag(QuantityOrdered) over(partition by buyid order by dateordered),0) < QuantityOrdered then 1 end as mod
from data d
)
group by buyid
) where norders = nqttyincreasing ;
With this query, it will check if all purchases as bigger in quantity as the last and will not show buyers id. that have only one purchase or sold equal or less than the last time4
CREATE TABLE buyers
([BuyId] int, [QuantityOrdered] int, [dateordered] date)
;
INSERT INTO buyers
([BuyId], [QuantityOrdered], [dateordered])
VALUES
(1, 10, '2021-11-04'),
(1, 20, '2022-01-22'),
(1, 30, '2022-02-22'),
(2, 50, '2022-02-20'),
(2, 60, '2022-05-02'),
(3, 60, '2022-05-02'),
(4, 10, '2022-05-02'),
(4, 6, '2022-06-02')
;
8 rows affected
WITH CTE as (SELECT
[BuyId], [QuantityOrdered], [dateordered],
(SELECT COUNT(*) FROM buyers b1 WHERE b1.[BuyId] = buyers.[BuyId]) _count_
,CASE WHEn [QuantityOrdered] >
LAG([QuantityOrdered]) OVER(PARTITION BY [BuyId] ORDER BY [dateordered])
THEn 1
ELSe
CASE WHEN LAG([QuantityOrdered]) OVER(PARTITION BY [BuyId] ORDER BY [dateordered]) IS NULL
THEN 1 ELSE 0 END
END _bigger_
FROM buyers)
SELECT DISTINCT [BuyId] FROM CTE
WHERE _count_ > 1 AND NOT EXISTS ( SELECT 1 FROM CTE c1
WHERE c1.[BuyId] = CTE.[BuyId] AND c1._bigger_ = 0)
BuyId
1
2
fiddle

Running Sum that resets to 0 on each new cluster of consecutives

I have tried and failed to adapt several running sum methods (remember I have to use SQL Server 2008, so it's a bit trickier than in 2012).
The goal is to have a running sum of Amount ordered by Date. Any time Category field changes value during that list, the sum should restart.
Table structure:
[Date], [Category], [Amount]
Example:
[Date], [Category], [Amount], [RunSumReset]
-------------------------------------------
1-Jan, catA, 10, 10
2-Jan, catA, 5, 15
3-Jan, catA, 15, 30
15-Jan, catB, 3, 3
1-Feb, catB, 6, 9
11-Feb, catA, 10, 10
12-Feb, catC, 2, 2
1-Apr, catA, 5, 5
Thanks so much for any slick tips or tricks
Using Version 2008 makes things a bit trickier since the window version of SUM with ORDER BY clause is not available.
One way to do it is:
WITH CTE AS (
SELECT [Date], Category, Amount,
ROW_NUMBER() OVER (ORDER BY [Date]) -
ROW_NUMBER() OVER (PARTITION BY Category
ORDER BY [Date]) AS grp
FROM mytable
)
SELECT [Date], Category, Amount, Amount + COALESCE(t.s, 0) AS RunSumReset
FROM CTE AS c1
OUTER APPLY (
SELECT SUM(c2.Amount)
FROM CTE AS c2
WHERE c2.[Date] < c1.[Date] AND
c1.Category = c2.Category AND
c1.grp = c2.grp) AS t(s)
ORDER BY [Date]
The CTE is used to calculate field grp that identifies islands of consecutive records having the same Category. Once Category changes, grp value also changes. Using this CTE we can calculate the running total the way it is normally done in versions prior to SQL Server 2012, i.e. using OUTER APPLY.
Select sum of amounts in current row and up to first row that has different category. In your case you will need to replace NULL with some min date that SQL Server supports, like '17530101':
DECLARE #t TABLE
(
category INT ,
amount INT ,
ordering INT
)
INSERT INTO #t
VALUES ( 1, 1, 1 ),
( 1, 2, 2 ),
( 1, 3, 3 ),
( 2, 4, 4 ),
( 2, 5, 5 ),
( 3, 6, 6 ),
( 1, 7, 7 ),
( 1, 8, 8 ),
( 4, 9, 9 ),
( 1, 10, 10 )
SELECT category ,
amount ,
( SELECT SUM(amount)
FROM #t
WHERE category = t.category
AND ordering <= t.ordering
AND ordering > ( SELECT ISNULL(MAX(ordering), 0)
FROM #t
WHERE category <> t.category
AND ordering < t.ordering
)
) AS sum
FROM #t t
ORDER BY t.ordering
Output:
category amount sum
1 1 1
1 2 3
1 3 6
2 4 4
2 5 9
3 6 6
1 7 7
1 8 15
4 9 9
1 10 10

How to COUNT rows according to specific complicated rules?

I have the following table:
custid custname channelid channel dateViewed
--------------------------------------------------------------
1 A 1 ABSS 2016-01-09
2 B 2 STHHG 2016-01-19
3 C 4 XGGTS 2016-01-09
6 D 4 XGGTS 2016-01-09
2 B 2 STHHG 2016-01-26
2 B 2 STHHG 2016-01-28
1 A 3 SSJ 2016-01-28
1 A 1 ABSS 2016-01-28
2 B 2 STHHG 2016-02-02
2 B 7 UUJKS 2016-02-10
2 B 8 AKKDC 2016-02-10
2 B 9 GGSK 2016-02-10
2 B 9 GGSK 2016-02-11
2 B 7 UUJKS 2016-02-27
And I want the results to be:
custid custname month count
------------------------------
1 A 1 1
2 B 1 1
2 B 2 4
3 C 1 1
6 D 1 1
According to the following rules:
All channel views subscription is billed every 15 days. If the
customer viewed the same channel within the 15 days, he will only be
billed once for that channel. For instance, custid 2, custname B his billing cycle is 19 Jan - 3 Feb (one billing cycle), 4 Feb - 20 Feb (one billing cycle) and so on. Therefore, he is billed only 1 time in Jan since he watch the same channel throughout the billing cycle; and he is billed 4 times in Feb for watching (channelid 7, 8, 9) and channelid 7 watched on 27 Feb (since this falls in another billing cycle, customer B is also charged here). Customer B is not charged on 2 Feb for watching channel 2 since he was already billed in 19 jan - 3 Feb billing cycle.
An invoice is generated every month for each customer, therefore, the
results should show the 'Month' and the 'Count' of the channels
viewed for each customer.
Can this be done in SQL server?
;WITH cte AS (
SELECT custid,
custname,
channelid,
channel,
dateViewed,
CAST(DATEADD(day,15,dateViewed) as date) as dateEnd,
ROW_NUMBER() OVER (PARTITION BY custid, channelid ORDER BY dateViewed) AS rn
FROM (VALUES
(1, 'A', 1, 'ABSS', '2016-01-09'),(2, 'B', 2, 'STHHG', '2016-01-19'),
(3, 'C', 4, 'XGGTS', '2016-01-09'),(6, 'D', 4, 'XGGTS', '2016-01-09'),
(2, 'B', 2, 'STHHG', '2016-01-26'),(2, 'B', 2, 'STHHG', '2016-01-28'),
(1, 'A', 3, 'SSJ', '2016-01-28'),(1, 'A', 1, 'ABSS', '2016-01-28'),
(2, 'B', 2, 'STHHG', '2016-02-02'),(2, 'B', 7, 'UUJKS', '2016-02-10'),
(2, 'B', 8, 'AKKDC', '2016-02-10'),(2, 'B', 9, 'GGSK', '2016-02-10'),
(2, 'B', 9, 'GGSK', '2016-02-11'),(2, 'B', 7, 'UUJKS', '2016-02-27')
) as t(custid, custname, channelid, channel, dateViewed)
), res AS (
SELECT custid, channelid, dateViewed, dateEnd, 1 as Lev
FROM cte
WHERE rn = 1
UNION ALL
SELECT c.custid, c.channelid, c.dateViewed, c.dateEnd, lev + 1
FROM res r
INNER JOIN cte c ON c.dateViewed > r.dateEnd and c.custid = r.custid and c.channelid = r.channelid
), final AS (
SELECT * ,
ROW_NUMBER() OVER (PARTITION BY custid, channelid, lev ORDER BY dateViewed) rn,
DENSE_RANK() OVER (ORDER BY custid, channelid, dateEnd) dr
FROM res
)
SELECT b.custid,
b.custname,
MONTH(f.dateViewed) as [month],
COUNT(distinct dr) as [count]
FROM cte b
LEFT JOIN final f
ON b.channelid = f.channelid and b.custid = f.custid and b.dateViewed between f.dateViewed and f.dateEnd
WHERE f.rn = 1
GROUP BY b.custid,
b.custname,
MONTH(f.dateViewed)
Output:
custid custname month count
----------- -------- ----------- -----------
1 A 1 3
2 B 1 1
2 B 2 4
3 C 1 1
6 D 1 1
(5 row(s) affected)
I don't know why you get 1 in count field for customer A. He got:
ABSS 2016-01-09 +1 to count (+15 days = 2016-01-24)
SSJ 2016-01-28 +1 to count
ABSS 2016-01-28 +1 to count (28-01 > 24.01)
So in January there must be count = 3.
Whenever I am trying to count things with complex criteria, I use a sum and case statement. Something like below:
SELECT custid, custname,
SUM(CASE WHEN somecriteria
THEN 1
ELSE 0
END) As CriteriaCount
FROM whateverTable
GROUP BY custid, custname
You can make that somecriteria variable as complicated a statement as you like, so long as it returns a boolean. If it passes, this row returns a 1. If it fails, the row reutrns a 0, then we sum up the values returned to get the count.
Generally this is how you can get any number (10 in this example) of fixed 15 day intervals starting at the given date (#dd in this example).
DECLARE #dd date = CAST('2016-01-19 17:30' AS DATE);
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1),
E2(N) AS (SELECT 1 FROM E1 a, E1 b),
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10,000 rows max
tally(N) AS (SELECT TOP (10) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4)
SELECT
startd = DATEADD(D,(N-1)*15, #dd),
endd = DATEADD(D, N*15-1, #dd)
FROM tally
Adapt it to the rules defining how start date must be calculated for the user (and probably chanel).
#Sturgus what if I want to define it in the code? Any other
alternatives besides defining it in the table? How to write a query
that can be run every month to generate the monthly invoice. –
saturday 15 mins ago
Well, one way or another, you will have to save each customer's billing start date (minimally). If you want to do this entirely in SQL without 'editing the database', something like the following should work. The drawback to this approach is that you would need to manually edit the "INSERT INTO" statement every month to suit your needs. If you were allowed to edit the already existing customers table or create a new one, then it would reduce this manual effort.
DECLARE #CustomerBillingPeriodsTVP AS Table(
custID int UNIQUE,
BillingCycleID int,
BillingStartDate Date,
BillingEndDate Date
);
INSERT INTO #CustomerBillingPeriodsTVP (custID, BillingCycleID, BillingStartDate, BillingEndDate) VALUES
(1, 1, '2016-01-03', '2016-01-18'), (2, 1, '2016-01-18', '2016-02-03'), (3, 1, '2016-01-15', '2016-01-30'), (6, 1, '2016-01-14', '2016-01-29');
SELECT A.custid, A.custname, B.BillingCycleID AS [month], COUNT(DISTINCT A.channelid) AS [count]
FROM dbo.tblCustomerChannelViews AS A INNER JOIN #CustomerBillingPeriodsTVP AS B ON A.custid = B.CustID
GROUP BY A.custid, A.custname, B.BillingCycleID;
GO
Where are you getting your customers' billing start dates as it is?
I'm not sure how this solution will scale - but with some good index candidates and decent data housekeeping, it'll work..
You're going to need some extra info for starters, and to normalize your data. You will need to know the first charging period start date for each customer. So store that in a customer table.
Here are the tables I used:
create table #channelViews
(
custId int, channelId int, viewDate datetime
)
create table #channel
(
channelId int, channelName varchar(max)
)
create table #customer
(
custId int, custname varchar(max), chargingStartDate datetime
)
I'll populate some data. I won't get the same results as your sample output, because I don't have the appropriate start dates for each customer. Customer 2 will be OK though.
insert into #channel (channelId, channelName)
select 1, 'ABSS'
union select 2, 'STHHG'
union select 4, 'XGGTS'
union select 3, 'SSJ'
union select 7, 'UUJKS'
union select 8, 'AKKDC'
union select 9, 'GGSK'
insert into #customer (custId, custname, chargingStartDate)
select 1, 'A', '4 Jan 2016'
union select 2, 'B', '19 Jan 2016'
union select 3, 'C', '5 Jan 2016'
union select 6, 'D', '5 Jan 2016'
insert into #channelViews (custId, channelId, viewDate)
select 1,1,'2016-01-09'
union select 2,2,'2016-01-19'
union select 3,4,'2016-01-09'
union select 6,4,'2016-01-09'
union select 2,2,'2016-01-26'
union select 2,2,'2016-01-28'
union select 1,3,'2016-01-28'
union select 1,1,'2016-01-28'
union select 2,2,'2016-02-02'
union select 2,7,'2016-02-10'
union select 2,8,'2016-02-10'
union select 2,9,'2016-02-10'
union select 2,9,'2016-02-11'
union select 2,7,'2016-02-27'
And here is the somewhat unweildy query, in a single statement.
The two underlying sub-queries are actually the same data, so there may be more appropriate / efficient ways to generate these.
We need to exclude from billing any channel charged in the same charging period C for the previous Month. This is the essence of the join. I used a right-join so that I could exclude all such matches from the results (using old.custId is null).
select c.custId, c.[custname], [month], count(*) [count] from
(
select new.custId, new.channelId, new.month, new.chargingPeriod
from
(
select distinct cv.custId, cv.channelId, month(viewdate) [month], (convert(int, cv.viewDate) - convert(int, c.chargingStartDate))/15 chargingPeriod
from #channelViews cv join #customer c on cv.custId = c.custId
) old
right join
(
select distinct cv.custId, cv.channelId, month(viewdate) [month], (convert(int, cv.viewDate) - convert(int, c.chargingStartDate))/15 chargingPeriod
from #channelViews cv join #customer c on cv.custId = c.custId
) new
on old.custId = new.custId
and old.channelId = new.channelId
and old.month = new.Month -1
and old.chargingPeriod = new.chargingPeriod
where old.custId is null
group by new.custId, new.month, new.chargingPeriod, new.channelId
) filteredResults
join #customer c on c.custId = filteredResults.custId
group by c.custId, [month], c.custname
order by c.custId, [month], c.custname
And finally my results:
custId custname month count
1 A 1 3
2 B 1 1
2 B 2 4
3 C 1 1
6 D 1 1
This query does the same thing:
select c.custId, c.custname, [month], count(*) from
(
select cv.custId, min(month(viewdate)) [month], cv.channelId
from #channelViews cv join #customer c on cv.custId = c.custId
group by cv.custId, cv.channelId, (convert(int, cv.viewDate) - convert(int, c.chargingStartDate))/15
) x
join #customer c
on c.custId = x.custId
group by c.custId, c.custname, x.[month]
order by custId, [month]

ROW_NUMBER query

I have a table:
Trip Stop Time
-----------------
1 A 1:10
1 B 1:16
1 B 1:20
1 B 1:25
1 C 1:31
1 B 1:40
2 A 2:10
2 B 2:17
2 C 2:20
2 B 2:25
I want to add one more column to my query output:
Trip Stop Time Sequence
-------------------------
1 A 1:10 1
1 B 1:16 2
1 B 1:20 2
1 B 1:25 2
1 C 1:31 3
1 B 1:40 4
2 A 2:10 1
2 B 2:17 2
2 C 2:20 3
2 B 2:25 4
The hard part is B, if B is next to each other I want it to be the same sequence, if not then count as a new row.
I know
row_number over (partition by trip order by time)
row_number over (partition by trip, stop order by time)
None of them will meet the condition I want. Is there a way to query this?
create table test
(trip number
,stp varchar2(1)
,tm varchar2(10)
,seq number);
insert into test values (1, 'A', '1:10', 1);
insert into test values (1, 'B', '1:16', 2);
insert into test values (1, 'B', '1:20', 2);
insert into test values (1 , 'B', '1:25', 2);
insert into test values (1 , 'C', '1:31', 3);
insert into test values (1, 'B', '1:40', 4);
insert into test values (2, 'A', '2:10', 1);
insert into test values (2, 'B', '2:17', 2);
insert into test values (2, 'C', '2:20', 3);
insert into test values (2, 'B', '2:25', 4);
select t1.*
,sum(decode(t1.stp,t1.prev_stp,0,1)) over (partition by trip order by tm) new_seq
from
(select t.*
,lag(stp) over (order by t.tm) prev_stp
from test t
order by tm) t1
;
TRIP S TM SEQ P NEW_SEQ
------ - ---------- ---------- - ----------
1 A 1:10 1 1
1 B 1:16 2 A 2
1 B 1:20 2 B 2
1 B 1:25 2 B 2
1 C 1:31 3 B 3
1 B 1:40 4 C 4
2 A 2:10 1 B 1
2 B 2:17 2 A 2
2 C 2:20 3 B 3
2 B 2:25 4 C 4
10 rows selected
You want to see if the stop changes between one row and the next. If it does, you want to increment the sequence. So use lag to get the previous stop into the current row.
I used DECODE because of the way it handles NULLs and it is more concise than CASE, but if you are following the text book, you should probably use CASE.
Using SUM as an analytic function with an ORDER BY clause will give the answer you are looking for.
select *, dense_rank() over(partition by trip, stop order by time) as sqnc
from yourtable;
Use dense_rank so you get all the numbers consecutively, with no skipped numbers in between.
I think this is more complicated than a simple row_number(). You need to identify groups of adjacent stops and then enumerate them.
You can identify the groups using a difference of row numbers. Then, a dense_rank() on the difference does what you want if there are no repeated stops on a trip:
select t.*,
dense_rank() over (partition by trip order by grp, stop)
from (select t.*,
(row_number() over (partition by trip order by time) -
row_number() over (partition by trip, stop order by time)
) as grp
from table t
) t;
If there are:
select t.*, dense_rank() over (partition by trip order by mintime)
from (select t.*,
min(time) over (partition by trip, grp, stop) as mintime
from (select t.*,
(row_number() over (partition by trip order by time) -
row_number() over (partition by trip, stop order by time)
) as grp
from table t
) t
) t;