Selecting Account Numbers with latest date - sql

I have been trying to solve this problem now for days.
I have table called Stat with the following simplified structure and sample data:
Customer BankID AccNumb Type Date Amount AccType
Customer 1 Boa 5 Account Statement 2015-01-01 10000,00 Eur
Customer 1 CS 10 Account Statement 2015-04-04 22000,00 Eur
Customer 2 Sa 15 Account Statement 2015-03-13 3000,00 Eur
Customer 2 Sa 40 Account Statement 2015-04-24 1000,00 Eur
Customer 2 Sa 15 Sale Advice 2015-04-16 400,00 Eur
Customer 2 Sa 15 Account Statement 2015-12-24 50,00 Usd
Customer 2 Boa 20 Sale Advice 2015-05-15 6000,00 Eur
Customer 3 Cu 25 Account Statement 2015-11-27 81000,00 Eur
Customer 3 Cu 30 Sale Advice 2015-11-27 3000,00 Usd
Customer 3 Pop 30 Account Statement 2015-11-27 12000,00 Eur
What I'm trying to do is to Select the AccountNumber with the latest date specified. A Customer can also have different Account Numbers on various Banks, so it should also be grouped by BankID and Customer.
I have come this far:
SELECT AccNumb, Customer, BankID,
(SELECT TOP 1 Amount FROM Stat
WHERE AccNumb = y.AccNumb AND Customer = y.Customer AND
BankID = y.BankID AND Type = 'Account Statement' AND
Date = MAX(y.Date) GROUP BY Amount) Amount
FROM Stat y
GROUP BY AccNumb, Customer, BankID
ORDER BY Customer, AccNumb
And it works fine, the problem is i should also add the column AccType and Date
I managed to do this with 2 more subselects (the query takes long but it works).
But now i have the problem that there are also NULL values in Customer (or Date) Column. Now, the account number of these 'NULL' Customers still should be displayed if it's the latest date. I also tried to do the same by joining the table by itself, and it didnt work out.
SELECT x.AccNumber, x.Customer, x.BankID, x.Date, y.Amount, y.AccType
FROM Stat y RIGHT JOIN
(SELECT AccNumber, Customer, BankID, MAX(Date) Date FROM Stat
GROUP BY AccNumber, Customer, BankID) x
ON x.AccNumber = y.AccNumber AND
x.Customer = y.Customer AND
x.BankID = y.BankID AND
x.Date = y.Date
ORDER BY y.Customer, y.AccNumber
But now the 'NULL' Customers only have NULL values in the Amount, Date and AccType Columns, which is not correct.
The output should be something like this
AccNumb Customer BankID Amount Date AccType
111111111 a Boa 1234.40 31.06.2014 Eur
222222222 NULL Boa 5678.40 31.04.2014 Eur
333333333 b Boa 0.00 25.02.2014 Eur
444444444 NULL Boa 9101.40 23.04.2015 Eur
555555555 NULL Boa 1213.40 31.02.2014 Usd
A66666666 c Sa NULL 31.02.2014 Eur
777777777 c Sa 1415.00 31.12.2014 Eur
888888888 c Boa 1617.40 31.12.2014 Usd
999999999 f Pop 5678.64 31.10.2014 Eur
Thanks in advance.

Just use row_number(), if I understand correctly:
select s.*
from (select s.*,
row_number() over (partition by customer, bankId order by date desc) as seqnum
from stat s
) s
where seqnum = 1;

Find the rows with latest dates, i.e. return a row if no other row with same AccountNumber, BankID and Customer but a later Date exists:
select *
from stat s1
where not exists (select 1 from stat s2
where s1.AccountNumber = s2.AccountNumber
and s1.BankID = s2.BankID
and s1.Customer = s2.Customer
and s1.Date < s2.Date)

You're first query works closely to what, I believe, you are looking for. Using it as a base, we can alter to work for you:
SELECT
AccNumb,
Customer,
BankID,
Amount,
Date,
AccType
FROM Stat y
WHERE Date = (SELECT MAX(z.DATE)
FROM Stat z
WHERE z.AccNumb = y.AccNumb
AND z.Customer = y.Customer
AND z.BankID = y.BankID AND Type = 'Account Statement')
ORDER BY Customer, AccNumb

Related

Subtract Daily amount from Withdraw Amount

I have two tables, tblWithdraw and tblProfit.
tblWithdraw Table
WithdrawId Date User Price
-----------------------------------------------------
1 2021-02-09 SANDANUWAN 2000.00
2 2021-02-09 GAYAN 300.00
3 2021-02-09 KASUN 1500.00
4 2021-02-09 AMAL 4000.00
5 2021-02-09 UDARA 340.00
6 2021-02-09 SULIN 200.00
7 2021-02-09 PERERA 120.00
tblProfit Table
Id Date Inv.No ItemName ItemCode Qty CostPrice DiscountPrice Amount
----------------------------------------------------------------------------------------------
1 2021-02-09 INV0000001 DELL LAP LP001 5 1500.00 1900.00 9500.00
2 2021-02-09 INV0000001 HP MOUSE MO001 7 2500.00 2940.00 20580.00
3 2021-02-09 INV0000001 PACIFIC FAN FAN001 6 2000.00 4000.00 24000.00
4 2021-02-09 INV0000001 SAMSUNG PHONE PH001 8 1000.00 1350.00 10800.00
This is my question. I want to sum all price from tblWithdraw table according to the date. Its mean withdraw date and profit table must be match. New I want to sum all amount from tblProfit table.Then I want to Subtract like this Price - Amount. Finally I want to show total amount of Profit table after subtracted. I used following join but is not working well. please help me to solve this problem. I want to subtract according to the date.
Select MAX(w.Date)Date, SUM(w.Price)Price, SUM(p.Amount)Amount
From tblWithdraw w
Left Join tblProfit p
ON w.Date = p.CurrentDate
Group by w.Date
Out put is like this. its wrong.
Date Price Amount
2021-02-09 33840.00 454160.00
Here's my CTE-based approach based on the comment which I made on the initial question.
WITH WithDraw AS(
SELECT
[date]
,SUM(price) PRICE
FROM tblWithdraw
GROUP BY [date]
),
Profit AS(
SELECT
[date]
,SUM(amount)AMOUNT
FROM tblProfit
GROUP BY [date]
)
SELECT
W.DATE
,W.PRICE
,P.AMOUNT
FROM WithDraw W
INNER JOIN Profit P ON w.date = p.date;
I'm not sure I fully understand the question, this is how I'm reading it:
Select w.Date,
w.agg_price_by_date,
w.agg_price_by_date - COALESCE(p.agg_amount_by_date, 0) AS diff_price_amount
From (SELECT Date,
SUM(Price) AS agg_price_by_date
FROM tblWithdraw w
GROUP
BY Date
) w
Left Join
(SELECT CurrentDate,
SUM(Amount) AS agg_amount_by_date
FROM tblProfit
GROUP
BY CurrentDate
) p
ON w.Date = p.CurrentDate

Calculate payment date for each invoice

Please consider the following table transaction: a company regularly sends invoices to their customers that are part of the same order. The companies' clients will often pay only once per so many weeks.
(trans_date in format yyyy-mm-dd)
id order_id trans_type trans_date trans_amount
----------------------------------------------------------
1 1 invoice 2017-01-10 100
2 1 invoice 2017-05-23 150
3 1 invoice 2017-05-28 200
4 2 invoice 2017-03-01 700
5 2 payment 2017-06-16 700
6 1 payment 2017-10-12 450
7 3 invoice 2017-06-24 199
The company would like to see on what date each invoice was paid for. For example: invoice (id) 1 (part of order_id=1 group) was sent on 2017-01-10 and paid on 2017-10-12 (id=6). Invoice with id=7 has not been paid at all.
The desired output would be the payment date for each invoice (payment_date):
id order_id trans_type trans_date trans_amount payment_date
--------------------------------------------------------------------------
1 1 invoice 2017-01-10 100 2017-10-12
2 1 invoice 2017-05-23 150 2017-10-12
3 1 invoice 2017-05-28 200 2017-10-12
4 2 invoice 2017-03-01 700 2017-06-16
5 2 payment 2017-06-16 700
6 1 payment 2017-10-12 450
7 3 invoice 2017-06-24 199
For transactions 5, 6 and 7, the payment_date is empty because it is either a payment (id=5 and 6) or an unpaid invoice (id=7).
I don't understand how I should solve this issue. In combination with regular scripting, I would get the whole set and loop through it to find each payment. But how can this be solved in SQL only?
Any help would be greatly appreciated!
Did you try a simple left join?
Below code is standard SQL.
Select a.id , a.order_id, a.trans_type, a.trans_date, a.trans_amount, isnull(b.trans_date, '') As payment_date
From transaction a
Left join transaction b
On a.order_id = b.order_id
And a.trans_type = 'invoice'
And b.trans_type = 'payment'
You can do a cumulative sum of payments and invoices and get the first date when the payment total meets or exceeds the invoice total:
with ip as (
select ip.*,
sum(case when ip.trans_type = 'invoice' then ip.trans_amount else 0 end) over (order by ip.trans_date) as running_invoice,
sum(case when ip.trans_type = 'payment' then ip.trans_amount else 0 end) over (order by ip.trans_date) as running_payment,
from invoicepayments i
)
select ip.*,
(select min(ip2.trans_date)
from ip ip2
where ip2.running_payment >= ip.running_invoice and
ip.trans_type = 'invoice'
) as payment_date
from ip;

Accounting Overdue Query

I need a query to get open invoices and overdue (if any).
I have a table named "Invoices" with this basic structure:
InvoiceNo CustID InvoiceDate Amount DueDate Credit
1 1 01/04/2015 120.00 01/31/2015
2 2 01/13/2015 72.00 01/20/2015
3 1 01/12/2015 63.00 TRUE
4 2 02/18/2015 43.00 02/10/2015
5 2 02/24/2015 16.00 TRUE
A table named "Payments" reflecting the customers payments:
PaymentNo CustID PaymentDate Amount Refund
I have also an "Opening Balance" value for each customer
I need to be able to see on the query results of customers having a balance <> 0. Something like that:
CustID Balance Amount Overdue Days Overdue
I use this query to show open invoices, it works fine but I'm stuck with the overdue deal.
Select Customers.CustID
, Customers.DisplayName As 'Name'
, COALESCE(Customers.OpeningBalance,0)
+(( Select COALESCE(Sum(Invoices.Amount),0) From Invoices Where Credit = 0 And Invoices.CustID = Customers.CustID )
+( Select COALESCE(Sum(Payments.Amount),0) From Payments Where Refund = 1 And Payments.CustID = Customers.CustID ) )
-(( Select COALESCE(Sum(Invoices.Amount),0) From Invoices Where Credit = 1 And Invoices.CustID = Customers.CustID )
+( Select COALESCE(Sum(Payments.Amount),0) From Payments Where Refund = 0 And Payments.CustID = Customers.CustID )) as Balance
From Customers
Where Exists ( Select * From Invoices
Where Invoices.CustID = Customers.CustID )
#Zajonc
The overdue balance is calculated according to the invoice amount and the payment amount relatively to their dates.
Scenario:
assuming a customer has an invoice #1 with a due date dated 01/15/2015 and with the amount of 140.00
assuming that that customer made a payment of 40.00 on the 01/20/2015.
The days overdue would be the days between today and the due date.
The due date balance would be 140.00 - 40.00 = 100.00.
That's for only one invoice and one payment transaction but since the reality is a bit more complicated the calculation should reflects the accumulation of both of the days and balance overdue.ie:
Assuming we execute the query on 02/15/2015 with the following data on our customer:
Invoice # Amount Due Date
1 140.00 01/15/2015
2 360.00 02/08/2015
Payment # Amount Payment Date
1 40.00 01/20/2015
2 100.00 02/18/2015
The days overdue would be:
( days elapsed between 02/15/2015 and 01/15/2015 ) +
( days elapsed between 02/15/2015 and 02/08/2015 ) = 31 + 7 = 38 days.
The balance overdue would be 100.00 (the customer made a payment 3 days after the query's date ) + 360.00 = 460.00
I hope it clarifies.
Thanks.

Counting presence of discount ranges in SQL

I have a simple orders table for a product being sold that has a column with the discount per order. For example:
Order Number DiscountPrcnt
1234 0
1235 10
1236 41
What I would like to do is create an output where I can join the order table to a customer table and group the discounts into ranges by email, as follows:
Email_Address 0-20 20-50 50-100
joe#abc.com Yes Yes
tom#abc.com Yes Yes
So the idea is to determine if each customer (here designated by email) has ever received a discount in the specified range, and if not, it should return NULL for the range.
A simplified version of the table structure is:
Customer Table:
CustID Email
123 joe#abc.com
234 tom#abc.com
456 joe#abc.com
So emails can repeat across customers.
Orders Table:
CustID OrderID Amount DiscPrcnt
123 1234 50.00 0
234 1235 75.00 10
456 1236 20.00 41
select c.email, COUNT(o1.order_number), COUNT(o2.order_number), COUNT(o3.order_number)
from customer c, order o1, order o2, order o3
where c.CustID = o1.CustId
and c.CustID = o2.CustID
and c.CustID = o3.CustID
and o1.DiscountPrcnt > 0
and o1.DiscountPrcnt <= 20
and o2.DiscountPrcnt > 2
and o2.DiscountPrcnt <= 50
and o3.DiscountPrcnt > 50
and o3.DiscountPrcnt <= 100
group by c.email
You'll not get yes as expected but the number of time the user benefits the discount. 0 if none.

How to join tables based on the dates

COMMISSION table
PRODUCT_ID DATE COMMISSION
1 20110101 27.00
1 20120101 28.00
1 20130705 30.00
2 20110101 17.00
2 20120501 16.00
2 20130101 18.00
...
ORDER table
PRODUCT_ID DATE PRICE
1 20110405 2500
2 20130402 3000
2 20130101 1900
Desired output
PRODUCT_ID DATE PRICE COMMISSION
1 20110405 2500 27.00
2 20130402 3000 16.00
2 20130101 1900 18.00
Commission table records commission % based on the product id and date.
Order table is basically a record of orders placed on a particular date,
I'd like to join two tables and bring the appropriate commission based on the date of the order. For example, you can see that the first order's commission is 27.00 as the date for the product_id 1 falls between 20110101 and 20120101.
How do I do this? Seems like a simple 1 to n relationship but I can't figure it out.
Try
SELECT o.*,
(
SELECT TOP 1 commission
FROM commission
WHERE product_id = o.product_id
AND date <= o.date
ORDER BY date DESC
) commission
FROM [order] o
Here is SQLFiddle demo