Build a SQL with sum - oracle11g

Here is my table - PK is (Con_num, version, order) :
Con_num version operation amount
15 1 A 1
15 1 B 2
15 1 C 3
15 2 A 4
15 3 A 5
15 3 B 6
15 4 C 7
Con_num is the contract number.
version is the version number.
operation is just an ID for an operation.
amount is the amount of the operation.
I would like to have the total amount per version. The tricky part is that: for version 1, i just have to sum the amount. But for version 2, I need to sum the version 2 line (with operation = A) and to take the two lines from version 1 (with operation != A). Therefore, for version 3, i will take the two lines of version 3, and only the line with operation = C from version 1. Any new operation invalidate the one from the previous versions.
The result will be:
Con_num version amount
15 1 6 (1 + 2 + 3)
15 2 9 (4 + 2 + 3)
15 3 14 (5 + 6 + 3)
15 4 18 (5 + 6 + 7)
How can I do that ?

For each con_num and version add up all records
for the same con_num
with no version greater than the version in question
having the highest version per operation
To get the amount of the record with the highest version can be solved with Oracle's KEEP FIRST/LAST:
select
base.con_num,
base.version,
(
select sum(max(mytable.amount) keep (dense_rank last order by mytable.version))
from mytable
where mytable.con_num = base.con_num
and mytable.version <= base.version
group by mytable.con_num, mytable.operation
) as total
from (select distinct con_num, version from mytable) base;

select
Con_num, version, orderno, a0+a1+a2 as amount
from (
select
Con_num, version, orderno
, lag(amount,2) over(partition by Con_num order by version, orderno) a2
, lag(amount,1) over(partition by Con_num order by version, orderno) a1
, amount a0
, row_number() over(partition by Con_num, version order by orderno desc) as rn
from table1
) d
where rn = 1
You seem to want only the "most recent" combinations of (Con_num, version, orderno) which can be identified using row_number() and the values required established using lag(,1) and lag(,2) but I don't reach the stated result.
result:
| con_num | version | orderno | amount |
|---------|---------|---------|--------|
| 15 | 1 | 3 | 37 |
| 15 | 2 | 1 | 42 |
| 15 | 3 | 2 | 35 |
sqlfiddle example

Using LAST_VALUE analytic function.
select con_num, version, q1+q2+q3
from (
select x.*,
last_value(case when operation = 1 then amount end) ignore nulls over (order by version) q1,
last_value(case when operation = 2 then amount end) ignore nulls over (order by version) q2,
last_value(case when operation = 3 then amount end) ignore nulls over (order by version) q3
from x
)
group by con_num,version, q1, q2, q3
order by con_num,version;

Related

Kusto Join Query - How to join 2 tables on $left.column1 < $right.column2

I have 2 tables in Kusto:
windowScans - each row is from this format : windowStart:long, windowEnd:long
files - each row is from this format: timestamp:long, fileId:string
I would like to join for each file all the windowScan rows that matches: timestamp > windowStart && timestamp < windowEnd.
The result should be a table of all files, and for each file all the matching pairs of windowScans.
It is possible that a windowScan row will appear in many files.
Any idea how to perform the query?
Here is one solution:
let windowsScan = datatable(windowStart:long, windowEnd:long)[1,5, 6,8, 10, 14];
let files = datatable(timestamp:long, fileId:string)[3,"a", 4,"b", 4,"c", 6, "a", 11,"a", 13, "b"];
windowsScan
| extend timestamp = range(windowStart, windowEnd, 1)
| mv-expand timestamp to typeof(long)
| join kind=inner (files) on timestamp
| summarize take_any(windowStart, windowEnd) by fileId, timestamp
Results:
fileId
timestamp
windowStart
windowEnd
a
3
1
5
b
4
1
5
c
4
1
5
a
6
6
8
a
11
10
14
b
13
10
14

sql duplicates, while one value appers once in column 1, and once in column 2

Im trying to check how many times two teams have played against each other, while each appearing once at home and once away.
In the next table, we can see that Team 1 played against Team 2 three times, twice away and once at home, and Team 3 played against Team 4 twice, once away and once at home.
how can I do it using sqlite?
id_home
id_away
date
1
2
2/12
2
1
3/12
3
4
4/12
4
3
5/12
2
1
6/12
You can group by sorted pairings of the id_home and id_away teams:
with matches as (select distinct t1.id_home h, (select t2.id_away from teams t2 where t2.id_home = t1.id_home) a from teams t1), m1 as (select case when h < a then h else a end a1, case when a < h then h else a end a2 from matches)
select m1.a1 team1, m1.a2 team2, (select sum(m1.a1 = t3.id_home) from teams t3) home, (select sum(m1.a1 = t3.id_away) from teams t3) away from m1 group by m1.a1, m1.a2;
Output:
team1 | team2 | home | away
1 2 1 2
3 4 1 1

SQLite: Group data within certain time interval

I have a single table which stores data of orders:
Orders Table:
id | order_time | quantity | ...
1 | 1592821854318 | 2
2 | 1592901538199 | 4
3 | 1592966454547 | 1
4 | 1593081282406 | 9
5 | 1593141826330 | 6
order_time table is UNIX timestamp.
Using below query I am able to get available data grouped by days (86400000 = 24 hours):
SELECT order_time+ (86400000 - (order_time % 86400000)) as gap, SUM(quantity) as
totalOrdersBetweenInterval
FROM USAGE_DETAILS ud
WHERE order_time >= 1590969600 AND order_time <= 1593388799000
GROUP BY gap
ORDER BY gap ASC
Suppose for this month of June, I receive order on 1, 4, 6, 7 date then by using above query I am able to retrieve data as follow :
gap | totalOrdersBetweenInterval
1 | 5
4 | 6
6 | 4
7 | 10
I would receive UNIX timestamp in gap column but for the sake of example I have used readable dates.
Above query will only retrieve data for the days which would have received order but I want to split data in range like below which also include days with no orders :
gap | totalOrdersBetweenInterval
1 | 5
2 | 0
3 | 0
4 | 6
5 | 0
6 | 4
7 | 10
8 | 0
9 | 0
. | .
. | .
How do I go about that?
You need a query that returns 30 rows:1,2,...,30 for the days of June.
You could do it with a recursive CTE:
with days as (
select 1 day
union all
select day + 1
from days
where day < 30
)
but I'm not sure if Android uses a version of SQLite that supports CTEs.
If it does support them, all you need to do is join the CTE with a LEFT join to your query:
with
days as (
select 1 day
union all
select day + 1
from days
where day < 30
),
yourquery as (
<your query here>
)
select d.day, coalesce(t.totalOrdersBetweenInterval, 0) totalOrdersBetweenInterval
from days d left join yourquery t
on t.gap = d.day
If Android does not support CTEs you will have to build the query that returns the days with UNION ALL:
select d.day, coalesce(t.totalOrdersBetweenInterval, 0) totalOrdersBetweenInterval
from (
select 1 day union all select 2 union all
select 3 union all select 4 union all
......................................
select 29 union all select 30
) d left join (
<your query here>
) t
on t.gap = d.day
Thanks to #forpas for helping me out.
Just posting in case someone is searching for slicing data by unix time intervals.
with
days as (
select 1590969600000 day --Starting of June 1 2020
union all
select day + 86400000 --equivalent to 1 day
from days
where day < 1593388799000 --Less than 28th of June
),
subquery as (
SELECT order_time+ (86400000 - (order_time % 86400000)) as gap, SUM(quantity) as
totalOrdersBetweenInterval
FROM USAGE_DETAILS ud
WHERE order_time >= 1590969600000 AND order_time <= 1593388799000
GROUP BY gap
)
select d.day, coalesce(t.totalOrdersBetweenInterval, 0) totalOrdersBetweenInterval
from days d left join subquery t
on t.gap = d.day
order by d.day

BigQuery subsselect example (count and sum)

In google BigQuery I have done a simple query to get how many music someone has listened.
What I need is to make a sum for all rows returned from the query below (some type of subquery)?
select count(1) cnt
from OF7.PETERV_TEST
where gender='F'
group by userId
Row f0_
1 14
2 1
3 7
4 18
5 1
6 4
7 2
8 2
expected result:
49
you can use:
SELECT sum(cnt)
FROM
(SELECT count(1) cnt
FROM OF7.PETERV_TEST
WHERE gender='F'
GROUP BY userId )

Ref cursor with dynamic columns

I am using oracle 11g and have written a stored procedure which stores values in temporary table as follows:
id count hour age range
-------------------------------------
0 5 10 61 10-200
1 6 20 61 10-200
2 7 15 61 10-200
5 9 5 61 201-300
7 10 25 61 201-300
0 5 10 62 10-20
1 6 20 62 10-20
2 7 15 62 10-20
5 9 5 62 21-30
1 8 6 62 21-30
7 10 25 62 21-30
10 15 30 62 31-40
now using this temp table i want to return two cursors. one for 61 and one for 62(age).
and for cursors there distinct range will be columns . for example cursor for age 62 should return following as dataset.
user 10-20 21-30 31-40
Count/hour count/hour count/hour
----------------------------------------------
0 5 10 - - - -
1 6 20 8 6 - -
2 7 15 - - - -
5 - - 9 5 - -
7 - - 10 25 - -
10 - - - - 15 30
this column range in temp table is is not a fixed values these are referenced from other table.
edited: i am using PIVOT for above problem, all examples i saw in internet are there for fixed values of column values (range in my case). how can i get dynamic values. following is the ex query:
SELECT *
FROM (SELECT column_2, column_1
FROM test_table)
PIVOT (SUM(column1) AS sum_values FOR (column_2) IN ('value1' AS a, 'value2' AS b, 'value3' AS c));
Instead of using handwritten value i am using following query inside 'IN'
SELECT * from(
with x as (
SELECT DISTINCT range
FROM test_table
WHERE age = 62 )
select ltrim( max( sys_connect_by_path(range, ','))
keep (dense_rank last order by curr),
',') range
from (select range,
row_number() over (order by range) as curr,
row_number() over (order by range) -1 as prev
from x)
connect by prev = PRIOR curr
start with curr = 1 )
it is giving error in this case. But when i using handwritten values its giving right output.
select * from (select user_id, nvl(count,0) count, nvl(hour,0) hour,nvl(range,0) range,nvl(age,0)
age from test_table)
PIVOT (SUM(count) as sum_count, sum(hour) as sum_hour for (range) IN
(
'10-20','21-30','31-40'
)
) where age = 62 order by userid
how can i give values dynamically there?
how can i do it.
Cursors are slow, I would recommend trying to do this in a query unless there's no alternative (or speed doesn't matter). You may want to look into: PIVOT / UNPIVOT which can rotate columns (in this case "range").
Here's some PIVOT / UNPIVOT documentation and examples:
http://www.oracle-developer.net/display.php?id=506
Based on your last edit:
Pretty sure you have two options:
Build dynamic sql based on the distinct values found in the "range" column.
You'll probably be stuck using a cursor again to build the column names but at least it will be limited to just the distinct ranges.
Oracle has a PIVOT XML command that you can use for this.
See: http://www.oracle.com/technetwork/articles/sql/11g-pivot-097235.html
And scroll down to the section: "XML Type"

Resources