running balance debit credit column in oracle query - oracle

Output result
I want the running balance in my query. I had wrote the query.
May be i am mistaking any where please let me know.
SELECT
ACC_VMAST.VM_DATE,
ACC_VDET.CHEQUE,
ACC_VMAST.NARRATION,
ACC_VDET.DEBIT,
ACC_VDET.CREDIT,
sum(nvl(ACC_VDET.DEBIT,0) - nvl(ACC_VDET.CREDIT,0) )
over (order by ACC_VMAST.VM_DATE , ACC_VDET.DEBIT ) running_bal
FROM ACC_VMAST,
ACC_VDET,
ACC_COA
WHERE ACC_VMAST.VM_PK=ACC_VDET.VM_PK
AND ACC_COA.COA_PK=ACC_VDET.COA_PK
AND ACC_VMAST.POST_BY IS NOT NULL
AND ACC_VMAST.CANCEL_STATUS IS NULL
AND ACC_VMAST.VM_DATE BETWEEN '07/06/2021' AND '07/07/2021'
AND ACC_VDET.COA_PK= '303'
ORDER BY ACC_VMAST.VM_DATE , ACC_VDET.DEBIT;

If you have rows that have the same values for the ORDER BY clause then when you SUM the values then all the rows with the same ORDER BY value will be grouped together and totalled.
To prevent that, you can add the ROWNUM pseudo-column to the ORDER BY clause of the analytic function so that there will not be any ties:
SELECT m.VM_DATE,
d.CHEQUE,
m.NARRATION,
d.DEBIT,
d.CREDIT,
SUM( COALESCE(d.DEBIT,0) - COALESCE(d.CREDIT,0) )
OVER ( ORDER BY m.VM_DATE, d.DEBIT, ROWNUM ) AS running_bal
FROM ACC_VMAST m
INNER JOIN ACC_VDET d
ON (m.VM_PK = d.VM_PK)
INNER JOIN ACC_COA c
ON (c.COA_PK = d.COA_PK)
WHERE m.POST_BY IS NOT NULL
AND m.CANCEL_STATUS IS NULL
AND m.VM_DATE BETWEEN DATE '2021-07-06' AND DATE '2021-07-07'
AND d.COA_PK = '303'
ORDER BY
m.VM_DATE,
d.DEBIT;

You need to add a row range with "rows between unbounded preceding and current row".
sum(nvl(ACC_VDET.DEBIT,0) - nvl(ACC_VDET.CREDIT,0) )
over (order by ACC_VMAST.VM_DATE , ACC_VDET.DEBIT rows between unbounded preceding and current row ) running_bal

Related

How to group and uniqe each row with ORACLE?

Anyone please help me, how to uniqe each row
SELECT T3.ID_JS,T5.DIVISI_AREA,T4.NAME_METHODE_REPAIR, T1.URUTAN AS SORT, TO_DATE(T1.TIME_FINISHED_WORK,'DD/MM/YYYY') AS DATE_FINISHED
FROM TB_WORK T1
JOIN TB_INSPECTION T2 ON T1.ID_INSPECTION=T2.ID_INSPECTION
JOIN TB_JOBSHEET T3 ON T2.ID_JS = T3.ID_JS
JOIN TB_METHODE_REPAIR T4 ON T1.ID_METHODE_REPAIR=T4.ID_METHODE_REPAIR
JOIN TB_DIVISI T5 ON T1.ID_DIVISI=T5.ID_DIVISI
where t3.id_js=142414
GROUP BY T3.ID_JS,T5.DIVISI_AREA,T4.NAME_METHODE_REPAIR, T1.URUTAN,TO_DATE(T1.TIME_FINISHED_WORK,'DD/MM/YYYY')
ORDER BY T3.ID_JS, TO_DATE(T1.TIME_FINISHED_WORK,'DD/MM/YYYY') desc
[Result] (http://prntscr.com/sfpuuq)
Those rows are unique, so I'll guess: there are two rows with sort = 3 and you'd want to have only one. If that's so, regarding the fact that date value is the only thing that makes difference, a simple way is to use aggregate function (such as min or max). I used max, but you can change it if you want:
SELECT t3.id_js,
t5.divisi_area,
t4.name_methode_repair,
t1.urutan AS sort,
MAX (TO_DATE (t1.time_finished_work, 'DD/MM/YYYY')) AS date_finished
FROM tb_work t1
JOIN tb_inspection t2 ON t1.id_inspection = t2.id_inspection
JOIN tb_jobsheet t3 ON t2.id_js = t3.id_js
JOIN tb_methode_repair t4
ON t1.id_methode_repair = t4.id_methode_repair
JOIN tb_divisi t5 ON t1.id_divisi = t5.id_divisi
WHERE t3.id_js = 142414
GROUP BY t3.id_js,
t5.divisi_area,
t4.name_methode_repair,
t1.urutan
ORDER BY t3.id_js, date_finished DESC
You'd remove date column from GROUP BY; besides, if you wanted to select distinct rows, why didn't you apply select distinct instead of using group by? Result is the same, but - group by is generally used with aggregates, not to return distinct result.

Left Join with Multiple Conditions and MAX Value

I'm trying to execute a left join where multiple conditions must be met with the inclusion of pulling in the MAX sequence number that meets those conditions.
The left join is on the unique identifier in both tables. Table acaps_history has several rows for each app_id. I need to pull in only one row with the highest seq_number and activity_code of 'XU'. If the code 'XU' doesn't exist for the given app_id, then the case statement above should return 'N' for that row. The code I have currently just isn't working - returning the error "a column may not be outer-joined to a subquery":
create table orig_play3 as
(select
x.*,
case when xa.activity_code in 'XU' then 'Y' else 'N' end as cpo_flag
from
dfs_tab_orig_play_x x
left join cf.acaps_history xa on
x.APP_ID = xa.FOC_APPL_ID
and xa.activity_code in 'XU'
and xa.seq_number = (select max(seq_number) from cf.acaps_history where FOC_APPL_ID=x.app_id)
)
Given your error, it seems that the issue is the last part of your query:
and xa.seq_number = (select max(seq_number) from cf.acaps_history where FOC_APPL_ID=x.app_id)
This is still operating in the context of the ON clause, so the sub-query to find the max sequence number is the issue.
You should be able to avoid this by moving that sub-query out of the ON clause:
LEFT JOIN (
SELECT FOC_APPL_ID, activity_code, seq_number
FROM cf.acaps_history
WHERE activity_code in 'XU'
) xa
ON x.APP_ID = xa.FOC_APPL_ID
WHERE xa.seq_number = (select max(ah.seq_number) from cf.acaps_history ah where ah.FOC_APPL_ID=x.app_id and ah.activity_code in 'XU')
This may be the most inefficient way to execute this query, but it worked... It took like 3 minutes to run (table size is over 600K rows), but again, it returned the results I needed:
create table test as (
select x.*,
case when xb.activity_code in 'XU' then 'Y' else 'N' end as cpo_flag
from dfs_tab_orig_play_x x
left join
(select
xa.FOC_APPL_ID, xa.activity_code, xa.seq_number
from dfs_tab_orig_play_x x, cf.acaps_history xa
where x.app_id = xa.FOC_APPL_ID (+)
and xa.seq_number = (select max(seq_number) from cf.acaps_history where
x.app_id=FOC_APPL_ID(+) and activity_code in 'XU')) xb
on x.app_id = xb.FOC_APPL_ID (+)
)
If you are on 12c, I like OUTER APPLY for this sort of thing, because it lets you sort the rows for each app_id descending by seq_number and then just pick the highest one.
SELECT
x.*,
CASE
WHEN xa.activity_code IN 'XU' THEN 'Y'
ELSE 'N'
END
AS cpo_flag
FROM
dfs_tab_orig_play_x x
OUTER APPLY ( SELECT *
FROM cf.acaps_history xa
WHERE xa.foc_appl_id = x.app_id
AND xa.activity_code = 'XU'
ORDER BY xa.seq_number DESC
FETCH FIRST 1 ROW ONLY ) xa
Note: this logic is a little different from what you posted. In this version, it will join to the acaps_history row having the highest seq_number from among 'XU' records for the given app_id. Your version was joining to the row having the highest seq_number for the given app_id, whether that row was an 'XU' row or not. I am assuming (with little reason) that that was a bug on your part. But, if it wasn't, my version won't work as given.

Get default value (and context column) when group by returns no records in Oracle

I have a query that I need for it to return a record even when there are no records. In the case where there are records, I simply want those records returned. On the other hand, when there are no records, I need it to still return a record but with the value for the "context" column (the GROUP BY column) equal to the value of the GROUP BY column that did not meet the criteria and a default value for aggregate function/column (e.g., 0). I tried a subquery:
SELECT
(
SELECT
CONTEXT,
SUM(VAL)
FROM
A_TABLE
WHERE
COL = 'absent'
GROUP BY
CONTEXT
)
FROM
DUAL;
but anything greater than one column in the subquery SELECT clause fails w/ a "too many values" message.
I also tried a UNION (with a little more context to more faithfully represent my situation):
SELECT
*
FROM
(
SELECT
CONTEXT,
SUM(VAL)
FROM
A_TABLE
WHERE
COL = 'absent'
GROUP BY
CONTEXT
UNION
SELECT
CONTEXT,
0
FROM
B_TABLE
)
AB_TABLE
INNER JOIN C_TABLE C -- just a table that I need to join to
ON
C.ID = AB_TABLE.C_ID
WHERE
C.ID = 10
AND ROWNUM = 1 -- excludes 2nd UNION subquery result when 1st returns record;
This one does work but I don't know why since the 2nd UNION subquery does not seem to be expressly connected w/ the first (I need the 2nd CONTEXT value to be the same as the 1st for the case where the 1st returns no records). The problem is that the real query does not return any records when I try to implement a similar strategy. I would like to see if there's a better way to approach this problem and perhaps get it to work for the real query (not included as it is too large and somewhat sensitive).
I am not sure I understand the question, but let's try.
I believe what you are saying is this. You have a table called A_TABLE, with columns CONTEXT, VAL, COL (and perhaps others as well).
You want to group by CONTEXT, and get the sum of VAL but only for those rows where COL = 'absent'. Otherwise you want to return a default value (let's say 0).
This can be done with conditional aggregation. The condition is in a CASE expression within the SUM, not in a WHERE clause (as you saw already, if you filter by COL='absent', in a WHERE clause, the query - past the WHERE clause - has no knowledge of the CONTEXT values that don't appear in any rows with COL = 'absent').
If the "default value" was NULL, you could do it like this:
select context, sum(case when col = 'absent' then value end) as val
from a_table
group by context
;
If the default value is anything other than NULL, the temptation may be to use NVL() around the sum. However, if VAL may be NULL, then it is possible that SUM(VAL) is NULL even when there are rows with COL = 'absent'. To address that possibility, you must leave the sum as NULL in those cases, and instead set the value to 0 (or whatever other "default value") only when there are NO rows with COL = 'absent'. Here is one way to do that. Still a standard "conditional" aggregate query:
select context,
case when count(case when col = 'absent' then 1 end) > 0
then sum(case when col = 'absent' then value end)
else 0 -- or whatever "default value" you must assign here
end as val
from a_table
group by context
;
Here's another way you could handle it that avoids the two additional tables (B_TABLE and C_TABLE).
SELECT context
, MAX(val)
FROM (
SELECT context
, SUM(val) as val
FROM a_table
WHERE col = 'absent'
GROUP BY context
UNION
SELECT context
, 0 as val
FROM a_table
) t
GROUP BY context
This assumes the default value you want to return is 0 and that any value in A_TABLE.VAL will be a positive integer.
http://sqlfiddle.com/#!4/c6ca0/20
SELECT b.context
, sum(a.val)
FROM b_table b
LEFT OUTER JOIN a_table a
ON a.context = b.context
AND a.col = 'absent'
GROUP BY b.context

Oracle 9 PL/SQL - How to get first row using order by without sub query

I know the "how to limit" or "how to get 1st row" has been posted many times but I can't find a solution to my specific issue.
I have a inventory balance table that contains bin # with quantities
I want on my row the bin # that contains the highest quantity
The real queries are much bigger and complex than this but this example shows the issue I am facing
I first I did
select itemnumber,
(select binnumber from inventory_balance where current_balance = (select max(current_balance) from inventory_balance where inventory_balance.itemnumber = item_table.itemnumber)) as binnumber
from item_table;
This will work when there is only one "bin" with the highest quantity.
If there are 2 bins for the same item with a quantity of 10 (which is the highest quantity), the sub query will return 2 rows, triggering a oracle error
Then I tried this :
select
itemnumber,
(select binnumber from (select binnumber from inventory_balance where current_balance = (select max(current_balance) from inventory_balance where inventory_balance.itemnumber = item_table.itemnumber)) where rownum =1) as binnumber
from item_table;
Now this will not work because it seems that the references to item_table.itemnumber is invalid when inside the from (...). I get "invalid column name" error when trying to do so.
I can't use ROW_NUMBER() because the "OLAP Window functions" do not seem to be activated on the database.
Something like this:
SELECT t.itemnumber,
MIN( b.binnumber ) KEEP ( DENSE_RANK LAST ORDER BY b.current_balance ASC ) AS binnumber
FROM item_table t
LEFT OUTER JOIN inventory_balance b
ON ( t.itemnumber = b.itemnumber )
GROUP BY t.itemnumber;
Looking at the explain plan then this will only scan inventory_balance once whereas doing nested selects to get the MAX balance and then filter an outer query based on that requires two scans of inventory_balance.
Although all the required output for you minimal working example seems to be contained in the inventory_balance table so you can do (if you are not interested in the itemnumbers where there are no entries in the inventory_balance table):
SELECT itemnumber,
MIN( binnumber ) KEEP ( DENSE_RANK LAST ORDER BY current_balance ASC ) AS binnumber
FROM inventory_balance
GROUP BY itemnumber;
If you want the highest binnumber (instead of the lowest) then you can just change it to:
MAX( binnumber ) KEEP ...

The first row when single-row subquery returns more than one row

My subquery returns more than one row and I need the first row and keep my max function
Select ...
Where GHTY_FIRME.FIRME_ID= (Select FIRME_ID
FROM imag_verification_tube
WHERE (numero_dossier = '12004' OR NUMERO_TIRE= '12004')
AND CREE_LE = (select max (CREE_LE)
from tableX where(numero_dossier ='12004' OR
NUMERO_PQDCS= '12004 ')));
Using the rownum pseudocolumn:
Select ...
Where GHTY_FIRME.FIRME_ID= (Select FIRME_ID
FROM imag_verification_tube
WHERE (numero_dossier = '12004' OR NUMERO_TIRE= '12004')
AND CREE_LE = (select max (CREE_LE)
from tableX
where(numero_dossier ='12004'
OR NUMERO_PQDCS= '12004 ')
and rownum < 2));
See also On ROWNUM and Limiting Results.
That said, you can try to optimize the sql. One of the first things I would do is to convert the inner SQLs to joins. Also, depending on your business logic, the or condition could also be elminated.

Resources