DB2 for IBM i (AS400) - SQL - sql

Two tables A (daily Loan Transaction table) and B (daily eod Loan Balance table).
Trying to write a select statement in DB2 AS400 to display daily loan transactions, eod Loan Balance and beginning loan balance (prior day) for selected date range.
Below query would give me daily loan transactions and eod loan balance. But need some help on modifying the query below to calculate beginning loan balance as well (balance as of 02/28/2015) for date range 03/01/2015 to 03/31/2015 selected.
select A.*, -- daily loan transactions
B.EOD_Loan_Balance
from A
inner join B
on A.date_id = B.date_id
and A.Loan_num = B.Loan_num
where a.date_id between 03/01/2015 to 03/31/2015
Note that previous day should be business day so if 02/28/2015 is not a business day then we need to pull eod balance of day before that and which should be a business day.
Any idea would be appreciated.
Thank you!

This is one of the many tasks that are easy to do with a "calender" or "dates" table...
select D.*,
B.EOD_Loan_Balance,
E.EOD_Loan_Balance,
from MY_CALENDAR_TABLE C
join daily_trans D on c.date = d.date
join eod_bal B on c.date - 1 day= b.date and d.loan_num = b.loan_num
join eod_bal E on c.date = e.date and d.loan_num = e.loan_num
where c.date between 2015-03-01 and 2015-03-31
and c.is_business_day = 1;
--Edit--
Looking back at this, I realized that it doesn't quite work; the join from beginning balance won't give you the prior businsess day.
Luckily, the flexibility of a calendar table comes to the rescue. Just include a PRIOR_BUSINESS_DAY column in your calendar table.
Then the SQL becomes:
select D.*,
B.EOD_Loan_Balance,
E.EOD_Loan_Balance,
from MY_CALENDAR_TABLE C
join daily_trans D on c.date = d.date
join eod_bal B on c.prior_buisiness_day = b.date
and d.loan_num = b.loan_num
join eod_bal E on c.date = e.date
and d.loan_num = e.loan_num
where c.date between 2015-03-01 and 2015-03-31
and c.is_business_day = 1;

Would this work for you?
select A.*, -- daily loan transactions
B.EOD_Loan_Balance,
C.EOD_Loan_Balance,
from A
inner join B
on A.date_id = B.date_id
and A.Loan_num = B.Loan_num
LEFT OUTER join C
on A.Loan_num = C.Loan_num
where a.date_id between '2015-03-01' to '2015-03-31'
and c.date_id = CASE
WHEN DAYOFWEEK('2015-03-01') = 1 THEN '2015-03-01' - 2 days 'Sunday --> Friday
WHEN DAYOFWEEK('2015-03-01') = 2 THEN '2015-03-01' - 3 days 'Monday --> Friday
ELSE '2015-03-01' - 1 day ' Previous day
END
Untested as I don't have a DB2 instance handy.
EDIT Revised to allow for weekends as mentioned in comments.

Related

SQL subquery to pull gallons for last 6 months from the respective date

I have a need to pull deals by date, location and customer and gallons for last 6 months from the respective date.
I am using IBM Netezza DB.
My code:
SELECT
DT.DIM_DATE_ID, DT.LOCATION_ID, DT.CUSTOMER, DT.DEAL, GL.GALLONS
FROM
DEALS_TBL DT
JOIN
DIM_DATE D ON D.DIM_DATE_ID = DT.DIM_DATE_ID
LEFT JOIN
(SELECT
D.MONTH, D.DIM_DATE_ID, D.DATE_ID,
G.LOCATION_ID, G.CUSTOMER, G.QUANTITY,
SUM(G.QUANTITY) OVER (ORDER BY D.DATE_ID, G.LOCATION_ID, G.CUSTOMER) GALLONS
FROM
FCT_GALLON G
JOIN
DIM_DATE D ON D.DIM_DATE_ID = G.DIM_DATE_ID
WHERE
D.DATE_ID BETWEEN ADD_MONTHS(D.DATE_ID, -6) AND D.DATE_ID) GL ON GL.DIM_DATE_ID = DT.DIM_DATE_ID
AND GL.LOCATION_ID = DT.LOCATION_ID
AND GL.CUSTOMER = DT.CUSTOMER
WHERE
D.DATE_ID = '2020-11-08'
For example if I am pulling the deal data for '2020-11-08' date I would wish to pull the sum of gallons from '2020-05-08' to '2020-11-08'
I am failing to do that with above left joined sub query.
Please refer to the sample data below and the expected output where the gallon is sum of QUANTITY from FCT_GALLON table from 20200508 to 20201108.
can you try this?
select
de.DIM_DATE_ID,
de.LOCATION_ID, de.CUSTOMER,
sum(fc.QUANTITY) from DEALS_TBL de
left join DIM_DATE di
on di.DIM_DATE_ID=de.DIM_DATE_ID
left join FCT_GALLON fc
on fc.CUSTOMER = de.CUSTOMER and fc.LOCATION_ID = de.LOCATION_ID
where
di.DATE_ID= '2021-03-01' and fc.DIM_DATE_ID between add_months(de.DIM_DATE_ID, -6) and de.DIM_DATE_ID
group by DE.DIM_DATE_ID,DE.LOCATION_ID, DE.CUSTOMER;

daily incremental results on table where transaction date <= #Date parameter

specifically looking for General Leger results. This means that I can't sum up transactions for specfic dates, or cant run Between date.
to get the results for say, today I would need to query the table for all transactions <= #Today.
That said, i am tasked with running this for every single day in 2020 thus far. is there a method to do this where i dont have to manually run for each day myself?
Query example:
glo.GLValue
, Sum(UnitCR) AS 'Credit'
, Sum(UnitDR) AS 'Debit'
, sum(FirmCR) AS 'FirmCredit'
, sum(FirmDR) AS 'FirmDebit'
FROM glacct ga
inner join gldetail gd on gd.glacct = ga.AcctIndex
inner join glnatural gn on ga.glnatural = gn.GLNaturalID
inner join glunit glu on ga.glunit = glu.GLUnitID
inner join gloffice glo on ga.GLOffice = glo.GLOfficeID
WHERE gn.GLNat IN ('11001','11002','11003','11005','11007','11011','11016','11019','11020','11021','11022','11024','11025','11026','11027','11032','11033',
'11034','11035','11036','11037','11040','11041','11042','11043','11044','11050','11051','11052','11053','11190','11199','11201','11202','11203','11204',
'11205','11206','11207','11301','11603','11700','11705','11801','11802','11803','11804','11806','11807','11808','11809','11901')--,'22001')
AND gd.PostDate <= #Yesterday
GROUP BY
glo.GLValue
Create a sub-table that give the sums for each PostDate and GLValue similar to above but also grouped on PostDate, then join that to your select above, e.g.
inner join gloffice glo on ga.GLOffice = glo.GLOfficeID
inner join ( ... new grouped select here ...) gs on gs.GLValue = glo.GlValue and gs.PostDate < gd.PostDate
Now you should be able to sum the gs values:
, Sum(gs.Credit) as Credit
, Sum(gs.Debit) as Debit
etc.

Advanced SQL: Sum of sales between a date range

I'm trying to get the sum of sales for a specific product between a date range. Unfortunately, the sum of sales from the results for both dates are the same. By right the total sales on 2019/01/01 is 5000 and the total sales on following day 2019/01/02 is 3000. The results showed total sales which is 8000 for both days. Which is wrong. Any expert can help to improve is this query?
Declare #BusinessDate datetime ='2019-01-01'
Declare #end datetime ='2019-01-02'
DECLARE #StoreId int = 100
SELECT [Terminals].[Id] AS [TerminalId],
[Terminals].[StoreId],
[EOD].[Id] AS [EODId],
SUM([Sales].[SalesAmount]) AS [SalesAmount],
[EOD].BusinessDate
FROM [CEPP]..[Stores] WITH (NOLOCK)
INNER JOIN [CEPP]..[Terminals] WITH (NOLOCK)
ON [Stores].[Id] = [Terminals].[StoreId]
AND [Terminals].[MWorkFlowStatusId] = 2
AND ([Terminals].[MStatusId] = 1
OR ([Terminals].[MStatusId] = 0
AND [Terminals].[SuspendedDate] > #BusinessDate ))
LEFT JOIN [EndOfDays] AS [EOD] WITH (NOLOCK)
ON [Terminals].[Id] = [EOD].[TerminalId]
AND [EOD].[BusinessDate] >= #BusinessDate and [EOD].[BusinessDate]<=#end
CROSS APPLY (
SELECT SUM([Products].[Deno]) AS [SalesAmount]
FROM [SalesOrders] AS [SO] WITH (NOLOCK)
INNER JOIN [SalesTransactions] AS [ST] WITH (NOLOCK)
ON [SO].[Id] = [ST].[SalesOrderId]
LEFT JOIN [VoidOrders] AS [VO] WITH (NOLOCK)
INNER JOIN [VoidTransactions] AS [VT] WITH (NOLOCK)
ON [VO].[Id] = [VT].[VoidOrderId]
ON [SO].[DealerId] = [VO].[DealerId]
AND [SO].[StoreId] = [VO].[StoreId]
AND [SO].[TerminalId] = [VO].[TerminalId]
AND [ST].[ProductId] = [VT].[ProductId]
AND [ST].[SerialNo] = [VT].[SerialNo]
AND [ST].[BusinessDate] = [VT].[BusinessDate]
AND [VT].[MVoidTypeId] = 1
INNER JOIN [CEPP].[dbo].[Products] WITH (NOLOCK)
ON [ST].[ProductId] = [Products].[Id]
WHERE [EOD].[Id] IS NOT NULL
AND [VT].[SerialNo] IS NULL
AND [SO].[TerminalId] = [Terminals].[Id]
AND [ST].[BusinessDate] >= #BusinessDate and [ST].[BusinessDate] <= #end
) AS [Sales]
WHERE [Stores].[DealerId] = 1 AND (#StoreId IS NULL OR [Terminals].[StoreId] = #StoreId)
GROUP BY [Terminals].[Id], [Terminals].[StoreId], [EOD].[Id], [Stores].[Code], [Terminals].[Code],[EOD].BusinessDate
ORDER BY ISNULL([EOD].[Id], 0), [Stores].[Code], [Terminals].[Code]
The unexpected results I got is :
TerminalId StoreId EODId SalesAmount BusinessDate
21598 100 5427531 8000.00 2019-01-01 00:00:00.000
21598 100 5427532 8000.00 2019-01-02 00:00:00.000
The results should be like this:
TerminalId StoreId EODId SalesAmount BusinessDate
21598 100 5427531 5000.00 2019-01-01 00:00:00.000
21598 100 5427532 3000.00 2019-01-02 00:00:00.000
From what I can see at a glance and without test data, is that the SUM([Products].[Deno]) is being performed in the CROSS APPLY irrespective of any GROUP BY you have in the outer query. Hence why you're getting SUM([Sales].[SalesAmount]) to equal 8000 for each output row.
Refactor the CROSS APPLY subquery to aggregate SUM([Products].[Deno]) with respect to a GROUP BY and join back to the main table by the GROUP BY predicates to your outer query.
AND [EOD].[BusinessDate] >= #BusinessDate and [EOD].[BusinessDate]<=#end
This part looks very suspicious to me. I think it should be something like
[EOD].[BusinessDate] = [Sales].[Date]
If that does not resolve your problem, please provide us with scripts for creation of tables and test data. That way it's much easier to investigate query.

SQL Select Where Between statement missing results that should be there?

I apologize if this question has already been asked, but I couldn't find anything on it.
Basically, my accounting department runs a report every month to pull up all orders from a specific client for the previous month. My problem is that I have orders that are not showing up in these reports. The only similarities I can find in the orders that are missing is that if there are 2 or more sequential order numbers, one or more of them will not be picked up by the report if most other fields are the same.
When I ran the report over a single 24 hour period, any orders that were missing from the month-long report would show up for that day.
This is the code I'm using for the query:
SELECT paydetail.ord_hdrnumber as ord_hdrnumber,
NULL As TotalRevenue,
paydetail.pyt_itemcode As Item,
NULL As ItemRevenue,
pyd_amount As ItemPay,
paydetail.asgn_type,
paydetail.asgn_id, ord_driver1,
ord_revtype1, pyt_description As ItemDesc,
pyt_basis As ItemBasis
FROM paydetail
LEFT OUTER JOIN payheader ON pyh_pyhnumber = pyh_number
JOIN orderheader on orderheader.ord_hdrnumber = paydetail.ord_hdrnumber
JOIN paytype on paytype.pyt_itemcode = paydetail.pyt_itemcode
WHERE ord_billto = #billto
AND ord_startdate BETWEEN #startdate + ' 00:00:00' AND #enddate + ' 23:59:59'
AND paydetail.pyt_itemcode NOT IN ('TCKEVE','TCKEXP','TCKDEB')
UNION ALL
SELECT invoicedetail.ord_hdrnumber as ord_hdrnumber,
ivh_totalcharge As TotalRevenue,
invoicedetail.cht_itemcode As Item,
ivd_charge As ItemRevenue,
NULL As ItemPay,
NULL As asgn_type,
NULL as asgn_id,
NULL as ord_driver1,
ord_revtype1,
[cht_description] As ItemDesc,
[cht_basis] As ItemBasis
FROM invoicedetail
LEFT OUTER JOIN invoiceheader ON invoiceheader.ivh_hdrnumber = invoicedetail.ivh_hdrnumber
JOIN orderheader on orderheader.ord_hdrnumber = invoicedetail.ord_hdrnumber
JOIN chargetype ON chargetype.cht_itemcode = invoicedetail.cht_itemcode
WHERE ivh_billto = #billto
AND ord_startdate BETWEEN #startdate + ' 00:00:00' AND #enddate + ' 23:59:59'
ORDER by ord_hdrnumber

Use last available currency rate by date available to calculate totals

I am trying to convert currency rates on orders.
The currency rates table is updated daily End of day for that particular day, however an order can be created earlier in the day, and as a result the rate will not be reflected with the current query. How would I add case statements and modify the query where by if the currency rate for the day does not exist use the last currency date available (within the table).
#Curr is the desired currency code. Could be 'USD', 'GBP' etc.
SELECT OrderNumber
,(CASE WHEN #Curr<>cur.code THEN
o.price * (SELECT Rate FROM xchangeRates xr
WHERE xr.FromCurrCode = c.Code
AND xr.ToCurrCode = #Curr
AND xr.Date= ISNULL((SELECT TOP 1 CAST(Crtd_DateTime AS Date) FROM ApDoc apdoc
WHERE apdoc.PONbr = o.OrderNumber AND apdoc.PONbr>''
ORDER BY apdoc.Crtd_DateTime DESC),o.orderdate)
)
ELSE o.price
END ) as o.price
from orders o
join currency c on c.curcode = o.curcode
Why not this:
...
o.price * (SELECT TOP 1 Rate FROM xchangeRates xr
WHERE xr.FromCurrCode = c.Code
AND xr.ToCurrCode = #Curr
ORDER BY xr.Date DESC)
...
If there are future dates in the xchangeRates table, all you have to do is add an additional filter to the WHERE clause to limit xr.Date to <= today.
EDIT: to handle this requirement:
if the invoice was created in the apdoc the it uses that Date for the
exchange rate, but if not then it uses the date the order was created.
forget your subselect to apDoc and JOIN it to orders in the outer query instead:
LEFT OUTER JOIN apDoc
ON apdoc.PONbr = o.OrderNumber
And then do this for your subquery instead of what I have above:
o.price * (SELECT TOP 1 Rate FROM xchangeRates xr
WHERE xr.FromCurrCode = c.Code
AND xr.ToCurrCode = #Curr
AND xr.Date =< COALESCE(CAST(apdoc.Crtd_DateTime AS Date),o.OrderDate)
ORDER BY xr.Date DESC)
nb: I CAST Crtd_DateTime AS Date because you did it in your code, and for all I know it's a varchar, but if it's a Datetime datatype, then the cast isn't necessary in my solution.