Select maximum value from all previous rows (oracle sql) - sql

I have a table that looks as follows:
personid opendate fraud
1 20190105 0
1 20190715 1
1 20191011 0
5 20181205 0
5 20190105 0
5 20190705 1
I want to get the maximum value of fraud up until each date grouped by personid. That is, I want to get the following table:
personid opendate fraud ever_fraud
1 20190105 0 0 <- no preceding frauds yet
1 20190715 1 1 <- fraud committed
1 20191011 0 1 <- fraud committed in previous credit
5 20181205 0 0
5 20190105 0 0
5 20190705 1 1

You can use window functions:
select
t.*,
max(fraud) over(partition by personid order by opendate) ever_fraud
from mytable t
Demo on DB Fiddle:
PERSONID | OPENDATE | FRAUD | EVER_FRAUD
-------: | -------: | ----: | ---------:
1 | 20190105 | 0 | 0
1 | 20190715 | 1 | 1
1 | 20191011 | 0 | 1
5 | 20181205 | 0 | 0
5 | 20190105 | 0 | 0
5 | 20190705 | 1 | 1

It seems like suggesting lag() analytic function :
with t2 as
(
select t.*,
lag(fraud,1,0) over (partition by personid order by opendate) as ever_fraud0
from t
)
select personid, opendate, fraud,
case when fraud = 1 then fraud else ever_fraud0 end as ever_fraud
from t2
Demo

Related

how to get balance sheet (debit , credit , balance) from transactions table in SQL?

if I have transactions table like that:
+----+--------+------------+-------------+--------+
| id | userID | debitAccID | creditAccID | amount |
+----+--------+------------+-------------+--------+
| 1 | 1 | 1 | 2 | 500 |
| 2 | 1 | 1 | 3 | 600 |
| 3 | 1 | 3 | 1 | 200 |
+----+--------+------------+-------------+--------+
how what query to use to get a table for account with id 1 like that:
+----+--------+------------+-------------+--------+
| debit | credit |balance |
+----+--------+------------+-------------+--------+
| | 500 | | 500 |
| | 600 | | 1100 |
| | | 200| 900 |
+----+--------+------------+-------------+--------+
900
Assuming the id column shows the correct order of transactions, you can use case and window with the default of rows between unlimited preceding and current row to get your output:
select id, user_id,
case when user_id = debit_acc_id then amount else 0 end as debit,
case when user_id = credit_acc_id then amount else 0 end as credit,
sum(case when user_id = debit_acc_id then amount else 0 end) over w
- sum(case when user_id = credit_acc_id then amount else 0 end) over w as balance
from transactions
where user_id = 1
window w as (partition by user_id order by id)
order by user_id, id;
db<>fiddle here

How to combine data from 2 tables -- which join, what conditions?

Consider the following 2 tables.
TableDE
ID country key1 key2
------------------------
1 US 1 null
1 US 1 null
1 US 1 null
2 US null null
3 US 1 1
4 DE 1 1
5 DE null null
5 DE null null
TableUS
ID key1 key2
--------------
1 null null
2 null 1
4 1 1
8 null 1
2 null 1
2 null 1
9 1 null
I need a distinct overview of all IDs, combining data from both tables:
ID inTableDe country DEkey1 DEkey2 inTableUS USkey1 USKey2
-----------------------------------------------------------------
1 1 US 1 0 1 0 0
2 1 US 0 0 1 0 1
3 1 US 1 1 0 0 0
4 1 DE 1 1 1 1 1
5 1 DE 0 0 0 0 0
8 0 0 0 1 1 0 1
9 0 0 0 1 1 1 0
I hope it speaks for itself:
ID 8 and ID 9 have 0 in the first column bc they aren't in tableDE
ID 8 and ID 9 have 0 in the country column bc this field doesn't exist in tableUS
ID 3 has 0 in inTableUS bc it only exists in tableDE
the key values are copied from the original tables
an ID is not unique: it can appear many times in both tables. However: the values for key1 and key2 will always be the same for each ID within the same table.
I have been messing for hours now with this; I have this now:
select de.[ID],
de.[country],
case when (de.[ID] in (select distinct [ID] from [tableDE]) then 1 else 0 end as [inTableDE],
case when (de.[ID] in (select distinct [ID] from [tableUS]) then 1 else 0 end as [inTableUS],
de.[key1] as [DEKey1],
de.[key2] as [DEKey2],
us.[key1] as [USKey1],
us.[key2] as [USKey2],
from dbo.[tableDE] de
full outer join dbo.[tableUS] us on de.[ID] = us.[ID]
where de.[country] = 'US'
and (de.[key1] = 1 or de.[key2] = 1 or us.[key1] = 1 or us.[key2] = 1)
group by de.[ID], us.[ID]
But this keeps giving me only values that are in both tables.
What am I doing wrong?
You sem to want aggregation on top of the full join:
select
coalesce(de.id, us.id) as id,
case when de.id is null then 0 else 1 end as intablede,
max(de.country) as country,
coalesce(max(de.key1), 0) as dekey1,
coalesce(max(de.key2), 0) as dekey2,
case when us.id is null then 0 else 1 end as intableus,
coalesce(max(us.key1), 0) as uskey1,
coalesce(max(us.key2), 0) as uskey2
from dbo.tablede de
full join dbo.tableus us on de.id = us.id
group by de.id, us.id
order by id
Demo on DB Fiddle:
id | intablede | country | dekey1 | dekey2 | intableus | uskey1 | uskey2
-: | --------: | :------ | -----: | -----: | --------: | -----: | -----:
1 | 1 | US | 1 | 0 | 1 | 0 | 0
2 | 1 | US | 0 | 0 | 1 | 0 | 1
3 | 1 | US | 1 | 1 | 0 | 0 | 0
4 | 1 | DE | 1 | 1 | 1 | 1 | 1
5 | 1 | DE | 0 | 0 | 0 | 0 | 0
8 | 0 | null | 0 | 0 | 1 | 0 | 1
9 | 0 | null | 0 | 0 | 1 | 1 | 0

SQL "Group" and "Count" categories

Edit. This is a follow up from another question. To simplify the question. Assume a table
date | id | type
01/01 | 1 | F
02/01 | 1 | F
02/01 | 1 | F
03/01 | 1 | S
03/01 | 1 | S
04/01 | 1 | F
04/01 | 1 | S
05/01 | 1 | S
I am looking for a way to summarise the above table by combination of transaction types per day. If a person (id) has only one transaction per day it counts as a Single type. If they have more than one it counts as a Multiple one. I've done that with my original query and it works. The output from the above table would be:
date | Single | Multiple
01/01 | 1 | 0
02/01 | 0 | 1
03/01 | 0 | 1
04/01 | 0 | 1
05/01 | 1 | 0
I got that far and it works. What's I'm struggling with (ie. don't have a clue of how to start) is how set up a query to show all possible combinations of Type (SS, FF, FS) instead of just counting the multiple transactions. The desired output would be like:
date | Single | # FF | # FS | # SS
01/01 | 1 | 0 | 0 | 0
02/01 | 0 | 1 | 0 | 0
03/01 | 0 | 0 | 0 | 1
04/01 | 0 | 0 | 1 | 0
05/01 | 1 | 0 | 0 | 0
Any constructive hints or ideas will be much appreciated.
this is assuming that you have max 2 types per date.
You can use the CASE WHEN statement with MIN() and MAX() to check for combination of FF, FS or SS
select [date],
case when count(*) = 1 then 1 else 0 end as Single,
case when count(*) >= 2
and min([type]) = 'F'
and max([type]) = 'F'
then 1
else 0
end as [# FF],
case when count(*) >= 2
and min([type]) = 'F'
and max([type]) = 'S'
then 1
else 0
end as [# FS],
case when count(*) >= 2
and min([type]) = 'S'
and max([type]) = 'S'
then 1
else 0
end as [# SS]
from yourtable
group by [date]
EDIT :
for more then 3 types, just change the count(*) = 2 to count(*) >= 2 as long as the type are either F or S

Sql : Based on column value do sum/substract

I am having data in sqltable like below :
+----+-------+-------------+--------+
| Id | PayId | DeductionId | Amount |
+----+-------+-------------+--------+
| 1 | 1 | 0 | 100 |
| 2 | 2 | 0 | 250 |
| 1 | 0 | 3 | 50 |
| 2 | 0 | 4 | 75 |
+----+-------+-------------+--------+
So in output, need group by on Id and when PayId is non-zero then do sum of amount and when DedctionId is non-zero then do sum of amount and substract of those two values. So need Output like below :
+----+--------+
| Id | Amount |
+----+--------+
| 1 | 50 |
| 2 | 175 |
+----+--------+
How to do that?
Hopefully, both are never non-zero. You just want conditional aggregation:
select id,
sum(case when payid > 0 then amount
when deductionid > 0 then - amount
else 0
end) as amount
from t
group by id;
Use a CASE statement for the SUM:
select
Id,
sum(
case when payid <> 0 then amount else 0 end -
case when deductionid <> 0 then amount else 0 end
) Amount
from tablename
group by id
See the demo.
Results:
> Id | Amount
> -: | -----:
> 1 | 50
> 2 | 175
A simple case expression should work here for you.
select Id
, Amount = sum(case when PayID > 0 then Amount else Amount * -1 end)
from YourTable
group by Id

SQL sum total each column in last row

I wish SQL for SUM each column(IPO and UOR) in TOTAL in second last. And GRAND TOTAL(Sum IPO + UOR) in the last one. Thank you so much
No Code IPO UOR
----------------------
1 D173 1 0
2 D176 3 0
3 D184 1 1
4 D185B 1 0
5 D187 1 2
6 F042 3 0
7 ML004 12 3
8 TTPMC 2 0
9 Z00204 1 0
------------------
TOTAL (NOS) 25 6
-------------------------
GRAND TOTAL (NOS) 31
Here is my code, :
SELECT
SUM(CASE WHEN IPOType = 'IPO' THEN 1 ELSE 0 END) as IPO,
SUM(CASE WHEN IPOType = 'UOR' THEN 1 ELSE 0 END) as UOR
FROM IPO2018
GROUP BY OriProjNo
it can show like this
No Code IPO UOR
----------------------
1 D173 1 0
2 D176 3 0
3 D184 1 1
4 D185B 1 0
5 D187 1 2
6 F042 3 0
7 ML004 12 3
8 TTPMC 2 0
9 Z00204 1 0
------------------
Generally speaking, you want to leave totals and sub-totals to whatever tool you are presenting your data in, as they will be able to handle the formatting with significantly more ease. In addition, your desired output does not have the same number of columns (Grand Total row only has one numeric) so even if you did shoehorn this in to the same dataset, the column headings wouldn't make sense.
That said, you can return group totals via the with rollup statement. This will provide an additional row with the aggregate totals for the group. Where there is more than one group in your data, you will get a sub-total row for each group and a total row for the entire dataset:
declare #t table(c nvarchar(10),t nvarchar(3));
insert into #t values ('D173','IPO'),('D176','IPO'),('D176','IPO'),('D176','IPO'),('D184','IPO'),('D184','UOR'),('D185B','IPO'),('D187','IPO'),('D187','UOR'),('D187','UOR'),('F042','IPO'),('F042','IPO'),('F042','IPO'),('TTPMC','IPO'),('TTPMC','IPO'),('Z00204','IPO'),('ML004','UOR'),('ML004','UOR'),('ML004','UOR'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO');
select row_number() over (order by grouping(c),c) as n
,case when grouping(c) = 1 then 'TOTAL (NOS)' else c end as c
,sum(case when t = 'IPO' then 1 else 0 end) as IPO
,sum(case when t = 'UOR' then 1 else 0 end) as UOR
from #t
group by c
with rollup
order by grouping(c)
,c;
Output:
+----+-------------+-----+-----+
| n | c | IPO | UOR |
+----+-------------+-----+-----+
| 1 | D173 | 1 | 0 |
| 2 | D176 | 3 | 0 |
| 3 | D184 | 1 | 1 |
| 4 | D185B | 1 | 0 |
| 5 | D187 | 1 | 2 |
| 6 | F042 | 3 | 0 |
| 7 | ML004 | 12 | 3 |
| 8 | TTPMC | 2 | 0 |
| 9 | Z00204 | 1 | 0 |
| 10 | TOTAL (NOS) | 25 | 6 |
+----+-------------+-----+-----+