Query calculate Balance in SAP B1 hana database - hana

Here is table i have
SELECT T0."TransId",
T0."Account",
T0."TransType",
SUM(COALESCE(CASE WHEN T0."TransType" = '15' THEN T0."Credit" END,T0."Credit")) As "Credit",
SUM(COALESCE(CASE WHEN T0."TransType" = '13' THEN T0."Debit" END,T0."Debit")) As "Debit",
SUM(COALESCE(CASE WHEN T0."TransType" = '13' THEN T0."Debit" END,T0."Debit")) - SUM(COALESCE(CASE WHEN T0."TransType" = '15' THEN T0."Credit" END,T0."Credit")) "Balance"
FROM JDT1 T0
WHERE T0."RefDate" BETWEEN '2022-09-01' AND '2022-09-30'
AND T0."Account" IN ('4101101','5101101')
GROUP BY T0."TransId", T0."Account",T0."TransType"
and the problem is, how calculate balance from field Debit & Credit

Related

SQL query to use group by to get the sum of two different columns within a date range

I have two tables time track and absence for an employee.
person_number Measure start_Date end_date Time_type
73636 10 01-Jan-2020 02-Jan-2020 Double
73636 24 06-Jan-2020 08-jan-2020 Double
73636 10 15-Jan-2020 25-Jan-2020 Regular Pay
73636 11.9 06-Jan-2020 08-jan-2020 Double
73636 27 10-Jan-2020 15-Jan-2020 Regular Pay
Absence det
person_number start_Date end_date duration Absence_type
73636 05-Jan-2020 10-Jan-2020 10 Vacation
73636 06-Jan-2020 18-jan-2020 9 Paid Leave
73636 20-Jan-2020 21-jan-2020 1 Paid Leave
Now when i pass the from and to date as 01-Jan-2020 and 31-Jan-2020, the output should look like -
Person_Number Double Regular Hour_code hour_amount
73636 31.9 37 Paid Leave 10
The hour_code should have only "Paid Leave" and no other absences
Now I have written the below query for this
SELECT
distinct person_number,
sum(
CASE
WHEN elements = 'Double' THEN measure
END
) AS OT_Hours,
sum(
CASE
WHEN elements LIKE 'Regular Pay%' THEN measure
END
) AS regular_measure_hours,
sum(
CASE
WHEN absence_name IN ('Paid Leave') THEN absence_duration
END
) AS hour3_amount,
max(
CASE
WHEN absence_name IN ('Paid Leave') THEN 'Paid Leave'
END
) AS hour3_code
FROM
(
select
person_number,
Time_type elements,
Absence_type absence_name,
duration,
measure
from
time_track_tab,
abs_tab,
per_all_people_F papf
where
time_track_tab.person_id = abs_tab.person_id
and abs_tab.person_id = papf.person_id
and abs_tab.Absence_type = 'Paid Leave'
)
group by
person_number
This is giving me multiple row output and calculation of sum is not coming correctly as in between the to and from date there are different dates present for both absence and time track.
My requirement is to calculate the sum of ALL the duration and measure column within these parameter dates. How can i tweak my query to get the correct sum between these dates ?
Is there a way to use partition by or group by or anything else to calculation these correctly in the column
You probably need to group both tables first then join them together to avoid the cross join.
select person_number, TimeTrack.DoublePay, TimeTrack.Regular,
Absenses.Hour_code, Absenses.hour_amount from
per_all_people_F papf,
(select
person_id, sum(duration) as hour_amount, Absence_type as Hour_code
from
abs_tab
where
abs_tab.Absence_type = 'Paid Leave'
and
start_Date between '2020-01-01' and '2020-01-31'
group by person_id,Absence_type
) Absenses,
(select
person_id,
sum(case when Time_type = 'Double' then Measure end) as DoublePay,
sum(case when Time_type = 'Regular Pay' then Measure end) as Regular
from time_track_tab
where
start_Date between '2020-01-01' and '2020-01-31'
group by person_id
) TimeTrack
where
papf.person_id = TimeTrack.person_id
and
papf.person_id = Absenses.person_id
and
papf.person_id = 73636
I made a SqlFiddle if you want to play with it
http://sqlfiddle.com/#!9/03e460/36
Also my 2 cents; I'd recommend left outer joining from the per_all_people_F table or else people without absenses will get filtered out.
See if, what you need is something like this:
select * from
(SELECT person_number,
sum(
CASE
WHEN Time_type = 'Double' THEN measure
END
) AS Double,
sum(
CASE
WHEN Time_type = ('Regular Pay') THEN measure
END
) AS regular
from time_track_tab
group by person_number
) A
inner join
(SELECT
person_number,
sum(
CASE
WHEN Absence_type = 'Vacation' THEN duration
END
) AS Vacation,
sum(
CASE
WHEN Absence_type = ('Paid Leave') THEN duration
END
) AS paidLeave
from abs_tab
group by person_number
)B on A.person_number = B.person_number
here the fiddle:
http://sqlfiddle.com/#!4/21253/2

Oracle SQL variable/parameter usage in query

I have this query below and I need to change WHERE conditions depending on QUARTER.
Meaning that I have to copy that query and change the DATE conditions to 06.2020 then use UNION.
I have no idea how can I optimize this query only with SQL because I am not able to DEFINE some variables/parameters in SQL (not using PL/SQL).
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
'Q1 2020' as QUARTER
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
AND b.balance_date = last_day(to_date('03.2020', 'MM.YYYY'))
WHERE a.account_close_date > last_day(to_date('03.2020', 'MM.YYYY')) and a.account_open_date <= last_day(to_date('03.2020', 'MM.YYYY'))
Thanks for help
If SQL*Developer, or SQLPlus, you can use the 'substitution variables'.
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
'Q1 2020' as QUARTER -- << probably want TO_CHAR('&&1', 'Q YYYY') or similar here
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
AND b.balance_date = last_day(to_date('&&1', 'MM.YYYY'))
WHERE a.account_close_date > last_day(to_date('&&1', 'MM.YYYY')) and a.account_open_date <= last_day(to_date('&&1', 'MM.YYYY'))
When you run this it will prompt you for the value.
If you used just '&1' it would prompt you for each occurence.
'&&1' reuses the value of the first occurence.
You could specify different variables using &1, &2, ...
Also you may used named variable as follows:
To prompt for the date :
ACCEPT dt CHAR PROMPT 'ENter the date (MM.YYYY): '
Or setting the value at declaration:
DEFINE dt='03.2020'
Then:
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
'Q1 2020' as QUARTER -- << probably want TO_CHAR('&&dt', 'Q YYYY') or similar here
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
AND b.balance_date = last_day(to_date('&&dt', 'MM.YYYY'))
WHERE a.account_close_date > last_day(to_date('&&dt', 'MM.YYYY')) and a.account_open_date <= last_day(to_date('&&dt', 'MM.YYYY'))
Ok first of all a disclaimer:
As I know nothing about your original query's logic - the query I propose only shows the main logic - and probably will not run as is.
Having said that - the approach would be:
Based on parameters :start_y (start year) and :end_y (end year)
generate a list with all the quarters for those years years_and_quarters.
Join the years_and_quarters with the required table based on quarter start and end dates - and then calculate the sums grouping by year and quarter.
with quarters as
(
select 'Q1' quarter, '03' q_end_date, '01' q_start_date from dual
union
select 'Q2' quarter, '06' q_end_date,'04' q_start_date from dual
union
select 'Q3' quarter, '09' q_end_date,'07' q_start_date from dual
union
select 'Q4' quarter, '12' q_end_date,'10' q_start_date from dual
),
years as
(select :start_y + (level-1) as mydate
from dual
connect by level <= (:end_y - :start_y) +1
),
years_and_quarters as (
select * from years,quarters
order by mydate
),
accounts as (
SELECT a.limit_amount ,
b.balance_amount,
'LOAN' as TYPE, b.balance_date,a.account_open_date,a.account_close_date
FROM accounts a
left join account_balances b
ON a.account_key = b.account_key
AND b.balance_type_key = 16
)
select sum(accounts.limit_amount) as LIMIT,
sum(accounts.balance_amount) as OUTSTANDING,
years_and_quarters.quarter,
accounts.TYPE
from
years_and_quarters,accounts
where
trunc(balance_date) = last_day(to_date(q_end_date||'.'||mydate, 'MM.YYYY'))
and trunc(account_close_date) > last_day(to_date(q_end_date||'.'||mydate, 'MM.YYYY')) and trunc(account_open_date) <= last_day(to_date(q_end_date||'.'||mydate, 'MM.YYYY'))
group by years_and_quarters.mydate,years_and_quarters.quarter
How about just aggregate by the quarter?
SELECT sum(a.limit_amount) as LIMIT,
sum(b.balance_amount) as OUTSTANDING,
'LOAN' as TYPE,
to_char(balance_date, '"Q"Q YYYY') as quarter
FROM accounts a LEFT JOIN
account_balances b
ON a.account_key = b.account_key AND
b.balance_type_key = 16 AND
b.balance_date = LAST_DAY(TRUNC(balance_date, 'Q') + INTERVAL '2' MONTH)
WHERE a.account_close_date > LAST_DAY(TRUNC(balance_date, 'Q') + INTERVAL '2' MONTH) AND
a.account_open_date <= LAST_DAY(TRUNC(balance_date, 'Q') + INTERVAL '2' MONTH)
GROUP BY TRUNC(balance_date, 'Q') ;
Your query seems focused on the last day of the quarter. This arrives at that by adding two months to the start and using LAST_DAY().

Aggregating data in a table up to the date of each row in the table

If I run the following query, it results in 1.6M rows:
SELECT
customer_id,
credit_id,
date
FROM
wallet
WHERE
credit_title = 'Topups'
AND credit_type = 'Credit Card Topups'
AND day >= DATE '2017-11-06'
AND day <= DATE '2020-04-03'
I am now trying to, for each row in the above query, get the count and total amount of all credit transactions for that customer up to the date of the row. I have tried the below query (which is a join with itself), but this results in 1.3M rows. Why are rows being dropped in the join? credit_id is a unique identifier in this table.
SELECT
customer_id,
credit_id,
COALESCE(COUNT(wallet_agg.credit_id), 0) AS topup_count_to_date,
COALESCE(SUM(wallet_agg.credit_amt_usd), 0) AS topup_amount_to_date
FROM
wallet
LEFT JOIN
wallet AS wallet_agg
ON
wallet.customer_id = wallet_agg.customer_id
AND wallet_agg.date < wallet.date
WHERE
wallet.credit_title = 'Topups'
AND wallet.credit_type = 'Credit Card Topups'
AND wallet.day >= DATE '2017-11-06'
AND wallet.day <= DATE '2020-04-03'
AND wallet_agg.credit_title = 'Topups'
AND wallet_agg.credit_type = 'Credit Card Topups'
Here is a simple demo of what I am trying, which gets the result I am expecting. How is the logic of my more complex query above different?
Your JOIN is being turned into an inner join by the WHERE conditions. You need to move the conditions on the second table into the ON clause:
FROM wallet LEFT JOIN
wallet AS wallet_agg
ON wallet.customer_id = wallet_agg.customer_id AND
wallet_agg.date < wallet.date AND
wallet_agg.credit_title = 'Topups'
wallet_agg.credit_type = 'Credit Card Topups'
WHERE wallet.credit_title = 'Topups' AND
wallet.credit_type = 'Credit Card Topups' AND
wallet.day >= DATE '2017-11-06' AND
wallet.day <= DATE '2020-04-03'
Of course, aggregation is way over-kill for this problem. You should just use window functions:
SELECT w.*
FROM (SELECT w.customer_id, w.credit_id, w.date,
COUNT(*) OVER (PARTITION BY customer_id, credit_title, credit_type ORDER BY date) as topup_count_to_date,
SUM(amount) OVER (PARTITION BY customer_id, credit_title, credit_type ORDER BY date) as topup_amount_to_date
FROM wallet w
WHERE w.credit_title = 'Topups' AND
w.credit_type = 'Credit Card Topups'
) w
WHERE w.day >= DATE '2017-11-06' AND
w.day <= DATE '2020-04-03';

Remove rows where one row offsets the next using accounting rules

I have a view of materials data which contains what was purchased and reversals of some of the purchases. I need a query that removes records that have reversals of purchase transactions. NOTE: The view does not have a primary key.
In the example I need to remove the first two rows as the second row offsets the first row because it reverses the purchase, but I need to keep the third row. Any ideas?
Here is the SQL for the view:
SELECT LEFT(mi.Plnt, 3) AS SBUID ,
oth.EQUIP AS PROJECTID ,
ms.Req_No AS GI ,
ms.Req_Item AS GI_LINE ,
CONVERT(VARCHAR(11), [Doc_Date], 100) + ' 12:00 AM' AS DOC_DATE ,
mi.[SLoc] AS SLOC ,
[Material] AS MATERIAL ,
mi.[Description] AS MATERIAL_DESCRIPTION ,
[Qty] AS QUANTITY ,
mi.[UoM] AS UOM ,
CASE WHEN mi.Mvt IN ( '101', '103', '105', '123', '261' ) THEN
mi.Amount
ELSE mi.Amount * -1
END AS Cost ,
mi.Amount AS EXT_ORG_COST ,
mi.PO AS [PO] ,
mi.Batch ,
mi.Vendor AS VENDOR ,
mi.VendorName AS VENDOR_NAME ,
at.AC_Group AS AC_TYPE ,
[Mvt] AS MVT
FROM [dbo].[MatIssued] mi
INNER JOIN dbo.OrderTableHistory oth ON oth.SUB_ORDER = mi.SubOrder
INNER JOIN dbo.Aircraft_Information2 ai ON ai.Equip = oth.EQUIP
INNER JOIN dbo.RFC_AcftTypeList at ON at.ID = ai.AC_TypeID
LEFT OUTER JOIN dbo.MatStatus ms ON ms.MPN = mi.Material
AND ms.SubOrder = mi.SubOrder
WHERE mi.Plnt IN ( '9131', '9132' )
AND mi.Mvt IN ( '101', '102', '103', '104', '105', '106', '122', '123' ,
'261' ,'262' )
AND mi.Doc_Date >= DATEADD(YEAR, -1, GETDATE())
ORDER BY mi.PO ,
mi.Batch ,
PROJECTID ,
mi.Mvt;
Some assumptions, based on your screenshot:
Reversals have same DOC_DATE as purchases
Reversals have same Batch as purchases
If the above assumptions are correct, try something like this:
DELETE FROM t
FROM MyTable t
WHERE EXISTS (
SELECT 1
FROM MyTable t2
WHERE
-- Join to outer table
t2.SLOC = t.SLOC
AND t2.MATERIAL = t.MATERIAL
AND t2.QUANTITY = t.QUANTITY
AND t2.PO = t.PO
AND t2.Batch = t.Batch
AND t2.VENDOR = t.VENDOR
GROUP BY SLOC, MATERIAL, QUANTITY, PO, Batch, VENDOR
HAVING COUNT(*) = 2 -- There are 2 matching rows
AND -MIN(QUANTITY) = MAX(QUANTITY) -- Minimum quantity negates Maximum quantity
AND MIN(COST) + MAX(COST) = 0 -- Costs cancel each other out
AND MIN(CASE WHEN Cost > 0 THEN DOC_DATE END) <= MIN(CASE WHEN Cost < 0 THEN DOC_DATE END) -- Purchase DOC_DATE less than or equal to reversal DOC_DATE
AND MIN(MVT) = MAX(MVT) + 1 -- Correlate purchase and reversal movement
AND (t.DOC_DATE = MIN(DOC_DATE) OR t.DOC_DATE = MAX(DOC_DATE)) -- Join to outer table
)

Iteration in sql query

I'm getting an unexpected result for my query in group function. I'm using the following 3 tables:
sale with columns AccountId, NetAmount, quantity, date
Purchase columns AccountId, NetAmount, quantity, date
Account columns AccountId, AccountName
I made a stored procedure that takes two inputs: Date1 and Date2.
I need to calculate the following:
Account.AccountName
sum of NetAmount of Purchase
sum of NetAmount of Sale with Date before Date
sum of NetAmount of Purchase - sum of NetAmount of Sale for Date between Date1 and Date2
sum of NetAmount of Sale and Purchase for Date between Date1 and Date2
I'm currently doing this:
SELECT a.SecurityName,
Sum( d.NetAmount) - Sum(e.NetAmount)As 'Opening Amount',
Sum( d.Quantity) - Sum(e.Quantity) As 'Opening Number',
Sum( d.NetAmount) / Sum( d.Quantity)As 'Opening Rate',
Sum( s.Quantity) As 'Number',
Sum( s.NetAmount) / Sum( s.Quantity) As 'Rate',
Sum( s.NetAmount) As 'Amount',
Sum( p.Quantity) As 'Number',
Sum( p.NetAmount) / Sum( p.Quantity) As 'Rate',
Sum( p.NetAmount) AS 'Amount',
IsNull(Sum( d.Quantity), 0) + (Sum( p.Quantity)) - IsNull((Sum( s.Quantity)), 0) As 'Closing Number',
IsNull(Sum( d.NetAmount),0)+(Sum( p.NetAmount)) -IsNull((Sum( s.NetAmount)),0) As 'Closing Amount',
IsNull(Sum( d.Rate),0)+(Sum( p.Rate))-IsNull((Sum( s.Rate)),0) As 'Closing Rate'
FROM Sale s
left Join SecurityAccount a ON s.SecurityAccountId = a.SecurityAccountId
Right JOIN Purchase p ON a.SecurityAccountId = p.SecurityAccountId
Left JOin Purchase d On a.SecurityAccountId=d.SecurityAccountId
And d.Date < #PeriodStart
Left Join Sale e On a.SecurityAccountId=e.SecurityAccountId
And e.Date < #PeriodStart
Group by a.SecurityName
End
But I'm getting values 3 times greater than expected.
Can anyone tell me what should i do?
You are joining tables 4 times by the same field SecurityAccountId. Each join will result multiplying of result rows. The only way I see is to create 4 subqueries with grouping and then using those results in main query. This should work, if I have no mistakes :)
SELECT a.SecurityName,
(d.SumNetAmount - e.SumNetAmount) AS 'Opening Amount',
(d.SumQuantity - e.SumQuantity) AS 'Opening Number',
(d.SumNetAmount) / d.SumQuantity) AS 'Opening Rate',
s.SumQuantity AS 'Number',
(s.SumNetAmount / s.SumQuantity) AS 'Rate',
s.SumNetAmount AS 'Amount',
p.SumQuantity AS 'Number',
(p.SumNetAmount / p.SumQuantity) AS 'Rate',
p.SumNetAmount AS 'Amount',
(ISNULL(d.SumQuantity, 0) + p.SumQuantity - ISNULL(s.SumQuantity, 0)) AS 'Closing Number',
(ISNULL(d.SumNetAmount,0) + p.SumNetAmount - ISNULL(s.SumNetAmount,0)) AS 'Closing Amount',
(ISNULL(d.SumRate,0) + p.SumRate - ISNULL(s.SumRate,0)) As 'Closing Rate'
FROM SecurityAccount a
LEFT JOIN (SELECT SecurityAccountId,
SUM(Quantity) AS 'SumQuantity',
SUM(NetAmount) AS 'SumNetAmount',
SUM(Rate) AS 'SumRate'
FROM Sale) AS s ON a.SecurityAccountId = s.SecurityAccountId
LEFT JOIN (SELECT SecurityAccountId,
SUM(Quantity) AS 'SumQuantity',
SUM(NetAmount) AS 'SumNetAmount',
SUM(Rate) AS 'SumRate'
FROM Sale WHERE Date < #PeriodStart) AS e ON a.SecurityAccountId = e.SecurityAccountId
LEFT JOIN (SELECT SecurityAccountId,
SUM(Quantity) AS 'SumQuantity',
SUM(NetAmount) AS 'SumNetAmount',
SUM(Rate) AS 'SumRate'
FROM Purchase) AS p ON a.SecurityAccountId = p.SecurityAccountId
LEFT JOIN (SELECT SecurityAccountId,
SUM(Quantity) AS 'SumQuantity',
SUM(NetAmount) AS 'SumNetAmount',
SUM(Rate) AS 'SumRate'
FROM Purchase WHERE Date < #PeriodStart) AS d ON a.SecurityAccountId = d.SecurityAccountId