How to replace preceding values with zeros in oracle - oracle

Table is like:-
ID AMOUNT PAID
1 500 100
1 500 50
1 500 200
2 1000 300
3 800 500
--I want to print ID 1 values should be zeros except first value like:-
ID AMOUNT PAID
1 500 100
1 0 50
1 0 200
2 1000 300
3 800 500
--Instead of repeating values have to be replaced with zeros

This is better done at the application layer. Assuming you have an ordering column, you can do it in SQL:
select id,
(case when row_number() over (partition by id order by ??) = 1
then amount
else 0
end) as amount,
paid
from t
order by id, ??;
The ?? represents the column used for ordering.

Related

Oracle query - How to count two distinct rows as one based on their value

I have table like this:
id
physician
order_id
exam
price
0
Physycian1
111
TSH
1
1
Physycian1
111
FT3
0
2
Physycian1
112
FT4
0
3
Physycian1
112
FT3
0
3
Physycian1
113
FT3
0
3
Physycian1
113
FT4
0
And I have query like this:
SELECT
physician,
COUNT(DISTINCT order_id) sum,
CASE price
WHEN 0 THEN 'Free'
ELSE 'Paid'
END AS is_free
FROM
table
GROUP BY
physician,
CASE price
WHEN 0 THEN 'Free'
ELSE 'Paid'
END
And the result is something like this:
physician
sum
is_free
Physician1
3
Free
Physician1
1
Paid
How can I change this query to count order_id=111(and others that might appear in DB) only as Paid and not as both.
So the end result would be:
physician
sum
is_free
Physician1
2
Free
Physician1
1
Paid
First boil your data down to only one row per physician and order. Then count.
select physician, pay_mode, count(*)
from
(
select
physician,
order_id,
max(case when price = 0 then 'free' else 'paid' end) as pay_mode
from mytable
group by physician, order_id
) orders
group by physician, pay_mode
order by physician, pay_mode;

Retrieving profiles from a table based on specific logic

I have a requirement to extract profiles from a table for the below criteria:
There are 5 IDs in total. 100, 200, 300, 400 and 500
The profiles to have atleast one ID of each value 100, 300 and 400. IDs with value 200 and 500 should not be present even once.
Profile can have multiple number of IDs of value 300 and 400 but will have only one ID of value 100.
The profiles will have IDs 300 and 400 equally. i.e For every ID will the value 300, there will be an ID of value 400.
Eg:
TABLE A:
-------------------------
PROFILE_ID ID
-------------------------
12345 100
12345 300
12345 400
23456 100
23456 300
23456 400
23456 300
23456 400
34567 100
34567 200
-------------------------
The result should fetch PROFILE_IDs 12345 and 23456 and not 34567.
I am pretty stuck and blank in getting a clear idea on how to frame a query for this. Please help.
For sample data you posted:
SQL> select * From test;
PROFILE_ID ID
---------- ----------
12345 100
12345 300
12345 400
23456 100
23456 300
23456 400
23456 300
23456 400
34567 100
34567 200
10 rows selected.
one option is to do it rule-by-rule, each of CTEs retrieving data which satisfy certain rule. The final result is intersection of these PROFILE_IDs.
SQL> with
2 rule2 as -- don't allow IDs 200 nor 500
3 (select profile_id
4 from test
5 where profile_id not in (select profile_id
6 from test
7 where id in (200, 500)
8 )
9 ),
10 rule3 as -- there can be only one ID = 100 for each PROFILE_ID
11 (select profile_id
12 from (select profile_id,
13 sum(case when id = 100 then 1 else 0 end) cnt_100
14 from test
15 group by profile_id
16 )
17 where cnt_100 = 1
18 ),
19 rule4 as -- number of IDs 300 and 400 has to be equal and greater than 0
20 (select profile_id
21 from (select profile_id,
22 sum(case when id = 300 then 1 else 0 end) cnt_300,
23 sum(case when id = 400 then 1 else 0 end) cnt_400
24 from test
25 group by profile_id
26 )
27 where cnt_300 = cnt_400
28 and cnt_300 > 0
29 )
30 select profile_id from rule2
31 intersect
32 select profile_id from rule3
33 intersect
34 select profile_id from rule4;
PROFILE_ID
----------
12345
23456
SQL>
Personally, since all your rules rely on counting the different ids, I'd do it more like this. Some of this is redundant, but it makes explicit which part comes from which of your criteria.
with rules as (
select profile_id,
sum(case when id = 100 then 1 else 0 end) as cnt_100,
sum(case when id = 200 then 1 else 0 end) as cnt_200,
sum(case when id = 300 then 1 else 0 end) as cnt_300,
sum(case when id = 400 then 1 else 0 end) as cnt_400,
sum(case when id = 500 then 1 else 0 end) as cnt_500
from table1
group by profile_id
)
select profile_id
from rules
-- rule 1
where cnt_100 > 0
and cnt_300 > 0
and cnt_400 > 0
-- rule 2
and cnt_200 = 0
and cnt_500 = 0
-- rule 3
and cnt_100 = 1
-- rule 4
and cnt_300 = cnt_400
SQL Fiddle

Want to display Calculated Negative balance on Credit Side Column and Positive on Debit Side

I am creating Trail Balance Report in Oracle Apex.
I have table in the following format:
Account_id Title DC Amount
10 Test 1 3000
10 Test 2 5000
20 XYZ 1 7000
20 XYZ 2 2000
DC Column = 1 Mean Debit and 2 Mean Credit
I want if sum amount is negative then show Net Credit Amount on Credit side column without Negative Sign. If sum amount is positive then show Net Debit Amount on Debit side column. Like
Account_id Title Debit Credit
10 Test 2000
20 XYZ 5000
How to do it with Sql Query?
Thanks
You can also use the conditional aggregation.
select account_id, title,
case when sum(case when DC = 1 then amount else -1*amount end) > 0
then sum(case when DC = 1 then amount else -1*amount end)
end debit,
case when sum(case when DC = 2 then amount else -1*amount end) > 0
then sum(case when DC = 2 then amount else -1*amount end)
end credit
from your_table t
group by account_id, title
First I calculate the balance in the inline view, then I apply group by clause to get the desired output.
select ACCOUNT_ID, TITLE, case when BALANCE < 0 then abs(BALANCE) end Debit, case when BALANCE >= 0 then BALANCE end credit
from (
select ACCOUNT_ID, TITLE, DC, AMOUNT
, sum(case when DC = 1 then -1 * AMOUNT else AMOUNT end)over(partition by ACCOUNT_ID,
TITLE) balance
from your_table
)
group by ACCOUNT_ID, TITLE, BALANCE
;

Oracle: Concatenate sequence number for repeated values

My situation similar to this but I just want to create sequence number for repeated values only.
Table: MyTable
-----------------------
ID CODE
1 100
2 100
3 200
4 200
5 200
6 300
Below is my query:
SELECT ID, CODE, (row_number() over (partition by CODE order by ID)) as SEQ from MyTable
And this is my current result:
ID CODE SEQ
1 100 1
2 100 2
3 200 1
4 200 2
5 200 3
6 300 1
But my expected result is:
ID CODE SEQ
1 100 1
2 100 2
3 200 1
4 200 2
5 200 3
6 300
Eventually, I do some coding to modify my current result. But I want to ask is there any way to generate the expected result via query only?
You can add CASE COUNT(1) over (partition by CODE) in your query, see sample query below.
WITH MyTable
as (
select 1 id, 100 code from dual union all
select 2 id, 100 code from dual union all
select 3 id, 200 code from dual union all
select 4 id, 200 code from dual union all
select 5 id, 200 code from dual union all
select 6 id, 300 code from dual)
SELECT ID, CODE, CASE COUNT(1) over (partition by CODE)
WHEN 1 THEN NULL
ELSE row_number() over (partition by CODE order by ID)
END as SEQ
from MyTable;
ID CODE SEQ
---------- ---------- ----------
1 100 1
2 100 2
3 200 1
4 200 2
5 200 3
6 300
6 rows selected

Oracle Rows into Columns

Can someone help me design the query to get below o/p.
Data I have:
Table X:
Count Name Amount Days
5 ABC 500 Day1
10 ABC 1000 Day2
3 BCD 100 Day1
4 BDC 200 Day2
Result I need:
Name Count AmountDay1 Count AmountDay2
ABC 5 500 10 1000
BCD 3 100 4 200
etc
Is this Possible?
I tried something with the below query, but not getting the desired o/p
select * from X
pivot
(sum(amount) for days in ('Day1', 'Day2'))
Please help
I'm using Oracle 11 G
Since you want to pivot on two columns Count and Amount, in might be easier to use an aggregate function with a CASE expression to get the result:
select name,
sum(case when days = 'Day1' then "count" else 0 end) CountDay1,
sum(case when days = 'Day1' then amount else 0 end) AmountDay1,
sum(case when days = 'Day2' then "count" else 0 end) CountDay2,
sum(case when days = 'Day2' then amount else 0 end) AmountDay2
from tableX
group by name;
See SQL Fiddle with Demo
If you want to use the PIVOT function, then you will want to unpivot the two columns Amount and Count, first:
select name, CountDay1, AmountDay1, CountDay2, AmountDay2
from
(
select name, col||days as col, value
from tableX
unpivot
(
value
for col in ("Count", Amount)
) u
) d
pivot
(
sum(value)
for col in ('CountDay1' as CountDay1, 'AMOUNTDay1' as AmountDay1,
'CountDay2' as CountDay2, 'AMOUNTDay2' as AmountDay2)
) piv;
See SQL Fiddle with Demo
you need to pivot on two columns count and amount:
select * from x
PIVOT
( min(count) as count ,sum(amount)for days in ('Day1', 'Day2'))

Resources