SQL - Number of entries needed to reach given value - sql

I need to find how many records it took to reach a given value. I have a table in the below format:
ID Name Time Time 2
1 Campaign 1 7 100
2 Campaign 3 5 165
3 Campaign 1 3 321
4 Campaign 2 610 952
5 Campaign 2 15 13
6 Campaign 2 310 5
7 Campaign 3 0 3
8 Campaign 1 0 610
9 Campaign 1 1 15
10 Campaign 1 54 310
11 Campaign 3 4 0
12 Campaign 2 23 0
13 Campaign 2 8 1
14 Campaign 3 23 1
15 Campaign 3 7 0
16 Campaign 3 5 5
17 Campaign 3 2 66
18 Campaign 3 100 7
19 Campaign 1 165 3
20 Campaign 1 321 13
21 Campaign 1 952 5
22 Campaign 1 13 3
23 Campaign 2 15 610
24 Campaign 2 0 15
25 Campaign 1 100 310
26 Campaign 2 165 0
27 Campaign 3 321 0
28 Campaign 3 952 1
29 Campaign 3 0 1
30 Campaign 3 5 0
I'd like to find out how many entries of 'Campaign 1' there were before the total of Time1 + Time2 was equal to or greater than a given number.
As an example, the result for Campaign 1 to reach 1400 should be 5.
Apologies if I haven't explained this clearly enough - the concept is still a little muddy at the moment.
Thanks

In SQL Server 2012, you can get the row using:
select t.*
from (select t.*, sum(time1 + time2) over (partition by name order by id) as cumsum
from table t
) t
where cumsum >= #VALUE and (cumsum - (time1 + time2)) < #VALUE;
You can get the count using:
select name, count(*)
from (select t.*, sum(time1 + time2) over (partition by name order by id) as cumsum
from table t
) t
where (cumsum - (time1 + time2)) < #VALUE
group by name;
If you are not using SQL Server 2012, you can do the cumulative sum with a correlated subquery:
select name, count(*)
from (select t.*,
(select sum(time1 + time2)
from table t2
where t2.name = t.name and
t2.id <= t.id
) as cumsum
from table t
) t
where (cumsum - (time1 + time2)) < #VALUE
group by name;

A recursive CTE computing a running total should work:
;WITH CTE AS
(
SELECT id,
name,
SUM([time]+[time 2])
OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
AS RunningTotal
FROM Table1
WHERE name = 'Campaign 1'
)
SELECT count(*)+1 AS [Count]
FROM CTE
WHERE RunningTotal < 1400
Note that I added 1 to the count as the query counts the number of rows needed to reach up to, but not including, 1400. Logic dictates that the next row will push the value above 1400.

Related

row_number() but only increment value after a specific value in a column

Query: SELECT (row_number() OVER ()) as grp, * from tbl
Edit: the rows below are returned by a pgrouting shortest path function and it does have a sequence.
seq grp id
1 1 8
2 2 3
3 3 2
4 4 null
5 5 324
6 6 82
7 7 89
8 8 null
9 9 1
10 10 2
11 11 90
12 12 null
How do I make it so that the grp column is only incremented after a null value on id - and also keep the same order of rows
seq grp id
1 1 8
2 1 3
3 1 2
4 1 null
5 2 324
6 2 82
7 2 89
8 2 null
9 3 1
10 3 2
11 3 90
12 3 null
demo:db<>fiddle
Using a cumulative SUM aggregation is a possible approach:
SELECT
SUM( -- 2
CASE WHEN id IS NULL THEN 1 ELSE 0 END -- 1
) OVER (ORDER BY seq) as grp,
id
FROM mytable
If the current (ordered!) value is NULL, then make it 1, else 0. Now you got a bunch of zeros, delimited by a 1 at each NULL record. If you'd summerize these values cumulatively, at each NULL record, the sum increased.
Execution of the cumulative SUM() using window functions
This yields:
0 8
0 3
0 2
1 null
1 324
1 82
1 89
2 null
2 1
2 2
2 90
3 null
As you can see, the groups start with the NULL records, but you are expecting to end it.
This can be achieved by adding another window function: LAG(), which moves the records to the next row:
SELECT
SUM(
CASE WHEN next_id IS NULL THEN 1 ELSE 0 END
) OVER (ORDER BY seq) as grp,
id
FROM (
SELECT
LAG(id) OVER (ORDER BY seq) as next_id,
seq,
id
FROM mytable
) s
The result is your expected one:
1 8
1 3
1 2
1 null
2 324
2 82
2 89
2 null
3 1
3 2
3 90
3 null

how long an amount have been less than 200

I would need to determine how long amounts have been less than 200.
My dataset looks like
Id user time amount max_amount
25 3618 1 0 1
25 3618 1 17040 3
25 3618 1 30 2
27 4281 1 0 1
27 4281 1 14188 3
27 4281 1 17372 4
27 4281 1 190 2
And so on
The code to generate it is the following:
Select t2.id, t2.user, t1.time, sum(t1.amount_amt as float) / (t1.eur_amt as float) as amount,
rank () over (partition by t2.user order by amount) max_amount
From table1
Inner join table2 as t2
on t1.user=t2.user
Group by 1,2,3
My expected output would be
Id user time spent
25 3618 1 2
27 4281 1 2
How could I get this result?
I think you just want filtering and aggregation:
select id, user, time, count(*)
from t
where amount < 200
group by id, user, time;
If that table is generated by the code in the question, you can just use a CTE before the above query:
with t as (
<query>
)

Returning a list of rows that are unique by type and returning the first pass

ID UserID TYPE PASS DATE
1 12 TRACK1 1 20140101
2 32 TRACK2 0 20140105
3 43 PULL1 1 20140105
4 66 PULL2 1 20140110
5 54 PULL1 0 20140119
6 54 TRACK1 0 20140120
So users can take multiple attempts for 'Type', so they can take 'TRACK1' multiple times, or 'PULL2' multiple times.
I want to return the first PASS (1) for each unique 'Type' for each user.
I want to return both pass and fail rows, but only the first instance of a pass or fail.
How can I do this?
sample table and output
ID UserID TYPE PASS DATE
1 12 TRACK1 1 20140101
2 12 TRACK2 0 20140105
3 12 PULL1 1 20140105
4 12 PULL2 1 20140110
5 12 PULL1 0 20140119
6 12 TRACK1 0 20140120
7 12 TRACK1 0 20140121
8 12 PULL1 1 20140115
9 12 TRACK2 0 20140125
output:
1 12 TRACK1 1 20140101
2 12 TRACK2 0 20140105
3 12 PULL1 1 20140105
4 12 PULL2 1 20140110
select t1.*
from UserTrackStatus t1
join
(
select userid,
type,
min(date) as min_date
from UserTrackStatus
group by userid, type
) t2 on t1.userid = t2.userid and t1.type = t2.type and t1.date = t2.min_date
SQLFiddle
Just do it with CTE and ROW_NUMBER to identify which records comes first
;
WITH cte
AS (
SELECT *
,ROW_NUMBER() OVER ( PARTITION BY [UserID], [Type] ORDER BY [date] ASC ) AS rn
FROM MyTable
WHERE PASS = 1
)
SELECT *
FROM cte
WHERE rn = 1

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

Week based count

I have a requirement to retrieve the data in the below fashion
Weeks delay_count
0 6
1 0
2 3
3 4
4 0
5 1
6 0
7 0
8 0
9 0
10 2
11 0
12 0
13 0
14 0
15 3
Here weeks is the hard coded column from 0 to 15 and delay_count is the derived column. I have a column delay_weeks. Based on the values in this column I need to populate the values in the delay_count column (derived column)
delay_weeks column values are below.
blank
blank
blank
2
10
5
blank
3
2
10
2
3
3
3
0
0
15
22
29
Conditions:
When delay_weeks is blank or 0 then count in the delay_count column should be 1
When delay_weeks is 3 then in the delay_count column the count should be 1 under week 3
When delay_weeks is 10 then in the delay_count column the count should be 1 under week 10
When delay_weeks is greater than or equal to 15 then in the delay_count column the count should be 1 under week 15.
I wrote code like below
SELECT "Weeks", a."delay_count"
FROM (SELECT LEVEL AS "Weeks"
FROM DUAL
CONNECT BY LEVEL <= 15) m,
(SELECT VALUE, COUNT (VALUE) AS "delay_numbers"
FROM (SELECT CASE
WHEN attr11.VALUE >= 15
THEN '15'
ELSE attr11.VALUE
END
VALUE
FROM docs,
(SELECT object_id, VALUE, attribute_type_id
FROM ATTRIBUTES
WHERE attribute_type_id =
(SELECT attribute_type_id
FROM attribute_types
WHERE name_display_code =
'ATTRIBUTE_TYPE.DELAY IN WEEKS')) attr11
WHERE docs.obj_id = attr11.object_id(+)
GROUP BY VALUE) a
WHERE m."Weeks" = a.VALUE(+)
select
weeks,
nvl(cnt, 0) as delay_count
from
(select level-1 as weeks from dual connect by level < 17)
left join (
select
nvl(least(attr11.value, 15), 0) as weeks,
count(0) as cnt
from
DOCS
left join (
ATTRIBUTES attr11
join ATTRIBUTE_TYPES atr_tp using(attribute_type_id)
)
on atr_tp.name_display_code = 'ATTRIBUTE_TYPE.DELAY IN WEEKS'
and docs.obj_id = attr11.object_id
group by nvl(least(attr11.value, 15), 0)
) using(weeks)
order by 1
Reverse-engineering the relevant parts of the table definitions, I think this gives you what you want:
select t.weeks, count(delay) as delay_count
from (select level - 1 as weeks from dual connect by level <= 16) t
left join (
select case when a.value is null then 0
when to_number(a.value) > 15 then 15
else to_number(a.value) end as delay
from docs d
left join (
select a.object_id, a.value
from attributes a
join attribute_types at on at.attribute_type_id = a.attribute_type_id
where at.name_display_code = 'ATTRIBUTE_TYPE.DELAY IN WEEKS'
) a on a.object_id = d.obj_id
) delays on delays.delay = t.weeks
group by t.weeks
order by t.weeks;
With what I think is matching data I get:
WEEKS DELAY_COUNT
---------- -----------
0 6
1 0
2 3
3 4
4 0
5 1
6 0
7 0
8 0
9 0
10 2
11 0
12 0
13 0
14 0
15 3
But obviously since you haven't given the real table structures I'm guessing a bit on the relationships. Obligatory SQL Fiddle.