Sum amounts based on account value - oracle

I want to sum AMT for account = 270400 and account like ‘51%’ only when AMT is positive on account = 270400. However, if there is an account = 513901, I want sum AMT for account = 270400 and account like ‘51%’ whether AMT is positive or negative on account = 270400. How to deploy Oracle lag/lead SQL in this case?
SUM would be = 14 in this data: (SUM 2 for 270400 and 12 for LIKE '51%")
account
amount
210897
1
270400
2
513101
3
513901
4
513901
5
529500
6
SUM would be = 10 in this data: (SUM -2 for 270400 and 12 for LIKE '51%")
account
amount
210897
1
270400
-2
513101
3
513901
4
513901
5
529500
6
SUM would be = 14 in this data: (SUM 2 for 270400 and 12 for LIKE '51%")
account
amount
210897
1
270400
2
513101
3
514101
4
515101
5
529500
6
SUM would be = 12 in this data: (do not SUM -2 for 270400, since it is negative and account 513901 is not in the dataset, but SUM LIKE '51%' = 12)
account
amount
210897
1
270400
-2
513101
3
514101
4
515101
5
529500
6

You appear to want:
SELECT COALESCE(SUM(amount), 0) AS total
FROM table_name
WHERE account LIKE '51%'
OR ( account = 270400
AND ( amount >= 0
OR EXISTS (SELECT 1 FROM table_name WHERE account = 513901)
)
)
Or, using analytic functions rather than EXISTS:
SELECT COALESCE(SUM(amount), 0) AS total
FROM (
SELECT account,
amount,
COUNT(CASE account WHEN 513901 THEN 1 END) OVER () AS can_be_negative
FROM table_name
WHERE account = 270400
OR account LIKE '51%'
)
WHERE account LIKE '51%'
OR amount >= 0
OR can_be_negative > 0
db<>fiddle here

Related

How to count or sum distinct values when there is a risk of intersection?

Imagine I have a table with people and their features:
group Name red_hair tall blue_eyes programmer
1 Mark 1 1 0 1
1 Sean 1 0 1 0
1 Lucas 1 1 1 1
2 Linda 0 1 1 1
I would like to count how many people of specific sets of features are in every group. In other words, I would like to make some bins without counting a person multiple times.
There are 2^4 (16) possible combinations of those sets, but I don't need so much.
For example, if a person has red_hair I don't care whether he or she has blue eyes or he or she a programmer. This person goes to the red hair bin of this group.
If a person is a programmer I don't care whether he or she is tall, but I don't want to count people who are already in a red hair bin. Because I have already counted them.
So I have a priority:
Red hair people counts first
Programmers second
People with blue eyes third
Expected result of this dataset:
group red_hair_persons programmers blue_eyes_persons
1 3 0 0
2 0 1 0
when I do this:
select group, count(case when red_hair = 1 then name end) as red_hair,
count(case when programmer = 1 and red_hair = 0 then name end) as programmers
from table
group by group
I fear that there would be some intersections. Or the logic with CASES would be so complex I could drown in it.
Am I right?
If so how could I avoid them? Maybe I am doing everything wrong and there is a better way to do what I want to. I have an enormous table with many features in it and I don't want to screw up.
Here's how I understood it:
SQL> with test (cgroup, name, red_hair, tall, blue_eyes, programmer) as
2 (select 1, 'mark' , 1, 1, 0, 1 from dual union all
3 select 1, 'sean' , 1, 0, 1, 0 from dual union all
4 select 1, 'lucas', 1, 1, 1, 1 from dual union all
5 select 2, 'linda', 0, 1, 1, 1 from dual
6 ),
7 priority as
8 (select t.*,
9 case when red_hair = 1 then 'A'
10 when programmer = 1 then 'B'
11 when blue_eyes = 1 then 'C'
12 else 'D'
13 end priority
14 from test t
15 )
16 select cgroup,
17 sum(case when priority = 'A' then 1 else 0 end) red_hair,
18 sum(case when priority = 'B' then 1 else 0 end) programmer,
19 sum(case when priority = 'C' then 1 else 0 end) blue_eyes,
20 sum(case when priority = 'D' then 1 else 0 end) other
21 from priority
22 group by cgroup;
CGROUP RED_HAIR PROGRAMMER BLUE_EYES OTHER
---------- ---------- ---------- ---------- ----------
1 3 0 0 0
2 0 1 0 0
SQL>
priority CTE puts every person into its priority group, based on their properties
the final select counts (using SUM + CASE) them per group
With a little bit of simple math involved in the conditional aggregation:
select "group",
sum("red_hair") red_hair_persons,
sum((1 - "red_hair") * "programmer") programmers,
sum((1 - "red_hair") * (1 - "programmer") * "blue_eyes") blue_eyes_persons
from tablename
group by "group"
See the demo.
Results:
> group | RED_HAIR_PERSONS | PROGRAMMERS | BLUE_EYES_PERSONS
> ----: | ---------------: | ----------: | ----------------:
> 1 | 3 | 0 | 0
> 2 | 0 | 1 | 0

BI Answers COUNT the number in cloumn?

Here is the data look like.
Name P_ID NUM
A P1 3
A P2 1
B P3 1
B P4 1
C P5 2
D P7 1
In BI Answers I want the result show like this:
Name NUM_OF_1 NUM_OF_2 NUM_OF_3 SUM
A 1 0 1 2
B 2 0 0 2
C 0 1 0 1
D 1 0 0 1
The column NUM_OF_N is occurrences of a number in a 'name' group.
If you are looking for a SQL query then you can try the following pivot:
SELECT Name,
SUM(CASE WHEN NUM = 1 THEN 1 ELSE 0 END) AS NUM_OF_1,
SUM(CASE WHEN NUM = 2 THEN 1 ELSE 0 END) AS NUM_OF_2,
SUM(CASE WHEN NUM = 3 THEN 1 ELSE 0 END) AS NUM_OF_3,
COUNT(*) AS "SUM"
FROM yourTable
GROUP BY Name
Tim has got it nailed in terms of SQL. In terms of pure OBI dev you should put that logic into logical (measure) columns in your RPD though so the BI server treats them as such and you can use them automatically with all the usual functionalities like drill, aggregate etc

add a new column for unique ID in hive table

i have a table in hive with two columns: session_id and duration_time like this:
|| session_id || duration||
1 14
1 10
1 20
1 10
1 12
1 16
1 8
2 9
2 6
2 30
2 22
i want to add a new column with unique id when:
the session_id is changing or the duration_time > 15
i want the output to be like this:
session_id duration unique_id
1 14 1
1 10 1
1 20 2
1 10 2
1 12 2
1 16 3
1 8 3
2 9 4
2 6 4
2 30 5
2 22 6
any ideas how to do that in hive QL?
thanks!
SQL tables represent unordered sets. You need a column specifying the ordering of the values, because you seem to care about the ordering. This could be an id column or a created-at column, for instance.
You can do this using a cumulative sum:
select t.*,
sum(case when duration > 15 or seqnum = 1 then 1 else 0 end) over
(order by ??) as unique_id
from (select t.*,
row_number() over (partition by session_id order by ??) as seqnum
from t
) t;

Select the sum of occurances of first alphabetical character

Hi what i need to do is create a select statement which outputs the sum of the first character in a field within the table so the output would look something like
A,12
B,0
C,20
D,14
E,0
ect...
The table is called contacts, in the above there was 12 occurrences of people whose names begin with the letter A
I hope i have explained this correctly
Let's understand this with EMP table example.
SQL> with
2 letters
3 as
4 (select chr( ascii('A')+level-1 ) letter
5 from dual
6 connect by level <= 26
7 )
8 SELECT substr(ename, 1, 1) AS init_name,
9 count(*) cnt
10 FROM emp
11 WHERE substr(ename, 1, 1) IN (SELECT letter from letters)
12 GROUP BY substr(ename, 1, 1)
13 UNION
14 SELECT l.letter AS init_name,
15 0 cnt
16 FROM letters l
17 WHERE l.letter NOT IN (SELECT substr(ename, 1, 1) FROM emp)
18 ORDER BY init_name
19 /
I CNT
- ----------
A 2
B 1
C 1
D 0
E 0
F 1
G 0
H 0
I 0
J 2
K 1
L 0
M 2
N 0
O 0
P 0
Q 0
R 0
S 2
T 1
U 0
V 0
W 1
X 0
Y 0
Z 0
26 rows selected.
SQL>
So, it gives the count of each letter of first name, and for the other letters which does not exist in the first name, the count is 0.
Generate the 26 letters using connect then left join to the first letter of the name and count them:
select letter, count(name) count
from (select chr(ascii('A')+level-1) letter from dual connect by level < 27) l
left join emp on substr(name, 1, 1) = letter
group by letter order by 1
See SQLFiddle
Attribution: My technique of generating letters uses elements of Lalit's answer.

How to sum incrementaly in Oracle 8?

I need to do an incremental sum in Oracle.
My situation is the following:
RecordID Value
1 1
2 2
3 5
4 10
And I need to get something like this:
RecordID Sum_incremental
1 (1)
2 (1 + 2)
3 (1 + 2 + 5)
4 (1 + 2 + 5 + 10)
The clues: self join and group by.
The solution:
select a.recordid, sum(b.value) sum_incremental from mytable a, mytable b
where b.recordid <= a.recordid group by a.recordid
select recordid,
sum(value) over (order by recordid)
from some_data

Resources