How to retrieve unpaid amount of each invoice for finance charges? - sql

I am working on implementing finance charges, but don't know how to retrieve the unpaid amount of each invoice. I am not new to SQL, however this problem has me stumped. Forgive me if I just didn't search properly.
So, let us say I have an invoice table:
id | amount
----+--------------
1 | 50.00
2 | 50.00
3 | 50.00
4 | 50.00
5 | 50.00
And a payments table:
amount
--------------
50.00
25.00
The result set should be this:
invoice_id | unpaid_amount
------------+--------------
2 | 25.00
3 | 50.00
4 | 50.00
5 | 50.00
Of course, there is quite a bit more to add to implement finance charges, but I think I can get the rest.
Edit: Sorry, an oversight of mine. The id's are not related. Removed payment id column.
Edit 2: These are fictual numbers, the real life numbers will be anything, so no matches can be made on the amounts.
Edit 3: And here I have created a SQL Fiddle to show what I have so far, based on #GordonLinoff second answer. I would appreciate a cleaner approach than the kludgy SQL I concocted.

I think you want a join:
select i.id as invoice_id,
(i.amount - coalesce(p.amount, 0)) as net_amount
from invoice i left join
payment p
on i.id = p.id;
EDIT:
Or, you may want:
select i.*,
(case when sum(i.amount) over (order by i.id) < p.amount
then i.amount
else greatest(p.amount - sum(i.amount) over (order by i.id) + i.amount, 0)
end) as amount_paid
from invoice i cross join
(select sum(amount) as amount
from payment
) p;
Here is a db<>fiddle.

You can do by using LEFT JOIN and subtract amounts from both table. COALSECE is here to prevent you from getting NULL when there is no payment.
select
i.id as invoice_id
,i.amount - coalesce(p.amount,0) as amount
from invoice i
left join payments p
on p.id = i.id
If in your payments talbe can be more than one payment for an invoice you should group payments before joining it to invoice.
select
i.id as invoice_id
,i.amount - coalesce(p.amount,0) as amount
from invoice i
left join (select id, sum(amount) as amount from payments group by id) p
on p.id = i.id

Related

SQL: How to return revenue for specific year

I would like to show the revenue for a specific year for all customers regardless of whether or not they have revenue data for the specific year. (in cases they dont have data for the specific year, a filler like 'no data' would work)
Sample Data looks like:
Table 1
Customer
Price
Quantity
Order Date
xxx
12
5
1990/03/25
yyy
15
7
1991/05/35
xxx
34
2
1990/08/21
Desired Output would look a little something like this:
Customer
Revenue (for 1990)
xxx
128
yyy
no data
Getting the total revenue for each would be:
SELECT Customer,
SUM(quantity*price) AS Revenue
but how would i go about listing it out for a specific year for all customers? (incl. customers that dont have data for that specific year)
We can use a CTE or a sub-query to create a list of all customers and another to get all years and the cross join them and left join onto revenue.
This gives an row for each customer for each year. If you add where y= you will only get the year requested.
CREATE TABLE revenue(
Customer varchar(10),
Price int,
Quantity int,
OrderDate date);
insert into revenue values
('xxx', 12,5,'2021-03-25'),
('yyy', 15,7,'2021-05-15'),
('xxx', 34,2,'2022-08-21');
with cust as
(select distinct customer c from revenue),
years as
(select distinct year(OrderDate) y from revenue)
select
y "year",
c customer ,
sum(price*quantity) revenue
from years
cross join cust
left join revenue r
on cust.c = r.customer and years.y = year(OrderDate)
group by
c,y,
year(OrderDate)
order by y,c
year | customer | revenue
---: | :------- | ------:
2021 | xxx | 60
2021 | yyy | 105
2022 | xxx | 68
2022 | yyy | null
db<>fiddle here
You would simply use group by and do the sum in a subquery and left join it to your customers table. ie:
select customers.Name, totals.Revenue
from Customers
Left join
( select customerId, sum(quantity*price) as revenue
from myTable
where year(orderDate) = 1990
group by customer) totals on customers.CustomerId = myTable.customerId;

SQL Query to get value of recent order alongwith data from other tables

I am writing an SQL query to get data from more than 3 tables, but for simplifying the question here I am using a similar scenario with 3 tables.
Table1 Customer (PK-CustomerID, Name)
CustomerID
Name
1
John
2
Tina
3
Sam
Table2 Sales (FK-Id, SalePrice)
ID
SalePrice
1
200.00
2
300.00
3
400.00
Table3 Order (PK-Id, FK-CustomerID, Date, Amount)
Id
CustomerID
Date
Amount
101
1
25-09-2021
30.0
102
1
27-09-2021
40.0
103
2
19-09-2021
60.0
In the output, Date and Amount should be the from most recent Order (latest Date), for a customer
My approach was
Select c.CustomerID, c.Name, s.SalePrice, RecentOrder.Date, RecentOrder.Amount from
Customer as c
LEFT JOIN Sales s ON c.CustomerID = s.ID
LEFT JOIN (SELECT top 1 o.Date, o.Amount, o.CustomerID
FROM Order o, Customer c1 WHERE c1.CustomerID = o.CustomerID ORDER BY o.Date DESC)
RecentOrder ON c.CustomerID = RecentOrder.CustomerID
Output I get
CustomerID, Name, SalePrice, Date, Amount
CustomerID
Name
SalePrice
Date
Amount
1
John
200.00
27-09-2021
40.0
2
Tina
300.00
null
null
3
Sam
400.00
null
null
The output I get includes the most recent order out of all the orders. But I want to get the recent order out of the orders made by that customer
Output Required
CustomerID, Name, SalePrice, Date, Amount
CustomerID
Name
SalePrice
Date
Amount
1
John
200.00
27-09-2021
40.0
2
Tina
300.00
19-09-2021
60.0
3
Sam
400.00
null
null
Instead of subquery in left join, you can check with outer apply.
Check following way
Select c.CustomerID, c.Name, s.SalePrice, RecentOrder.Date, RecentOrder.Amount
from Customer c
LEFT JOIN Sales s ON c.CustomerID = s.ID
OUTER APPLY (
SELECT top 1 o.Date, o.Amount, o.CustomerID
FROM [Order] o
WHERE o.CustomerID = c.CustomerID ORDER BY o.Date DESC) RecentOrder`
You need to pre-aggregate or identify the most recent order for each customer order, your query is selecting 1 row for all orders.
Try the following (untested!)
select c.CustomerID, c.Name, s.SalePrice, o.Date, o.Amount
from Customer c
left join Sales s on c.CustomerID = s.ID
outer apply (
select top (1) date, amount
from [order] o
where o.CustomerId=c.CustomerId
order by Id desc
)o

Postgresql Joining tables without losing records

Let's say I have the following tables:
1 - StartingStock:
vendor | starting_stock
------------------------
adidas | 13
Reebok | 5
2 - Restock:
vendor | restocks
-----------------
adidas | 2
nike | 3
3 - Sales:
vendor | quantity_sold
----------------------
adidas | 10
nike | 1
I want my resulting table to be the sell through grouped by vendor. In this scenario, sell through is calculated like this: quantity_sold/(starting_stock + restocks). My only problem is that starting stock and restock tables may not have the some vendors that are present in the sales table. So in the scenario above, StartingStock does not have nike as a record. So if that's the case the sell though for nike would be just 1/3 or 1/(3+0). Therefore, my resulting table would be:
vendor | sell_through
---------------------
adidas | 1.5
nike | 0.33
Reebok | 0
So I'd want all of the vendors present in the result table (if it has no sales, value is 0 like Reebok shown above).
I tried working with the different types of joins but I couldn't get it. Any help would be great. Thanks.
We can try a full outer join approach here:
SELECT
COALESCE(ss.vendor, r.vendor, s.vendor) AS vendor,
COALESCE(s.quantity_sold, 0) /
(COALESCE(ss.starting_stock, 0) + COALESCE(r.restocks, 0)) AS sell_through
FROM StartingStock ss
FULL OUTER JOIN Restock r ON ss.vendor = r.vendor
FULL OUTER JOIN Sales s ON s.vendor = COALESCE(ss.vendor, r.vendor)
Demo
Note that I am coming up with 2/3 for the sell through for Adidas, since the quantity sold is 10, and the sum of stocks is 15.
I would use union all and aggregation:
select vendor,
sum(starting_stock), sum(restock), sum(quantity_sold),
(sum(quantity_sold) * 1.0 / sum(starting_stock) + sum(restock)) as sell_through
from ((select vendor, starting_stock, 0 as restock, 0 as quantity_sold
from startingstock
) union all
(select vendor, 0 as starting_stock, restock, 0 as quantity_sold
from restock
) union all
(select vendor, 0 as starting_stock, 0 as restock, quantity_sold
from sales
)
) v
group by vendor;
In particular, this version includes each number in the calculation only once. A JOIN approach will produce inaccurate results if a vendor has multiple rows in any of the tables.

Eliminate database rows based on mod dates

I have an issue grabbing data from two tables base on a single field in one of the tables The field does not exist in both tables.
Example:
Table 1 (call it invoices):
invoice email customer_name original_bill invoice_status
1 a#g.com bob 5.00 P
2 a#g.com harry 23.00 P
3 a#g.com sally 4.00 P
4 b#g.com loretta 14.00 P
5 b#g.com hamish 74.00 P
Table 2 (customer invoice edits):
invoice email date_last_modified_timestamp mod_status mod_amount
1 a#g.com 2019-05-01 A 3.00
1 a#g.com 2019-04-01 D
3 b#g.com 2019-10-25 A
What I want
a list of all invioces and their mods but I only wish each invoice to appear once in the list. based on the latest date in Table two.
example:
invoice email customer_name original_bill invoice_status date_last_modified_timestamp mod_status mod_amount
1 a#g.com bob 5.00 P 2019-05-01 A 3.00
2 a#g.com harry 23.00 P
3 a#g.com sally 4.00 P 2019-10-25 A
4 b#g.com loretta 14.00 P
5 b#g.com hamish 74.00 P
How im pulling it now:
select invoice, email, customer_name, original bill, invoice status, max(date_last_modified_timestamp), mod status, mod_amount
from table one
left join on table1.invoice = table 2.invoice
group by
invoice, email, customer_name, original bill, invoice status, max(date_last_modified_timestamp), mod status, mod_amount
I've tried a gazzillion variation to no avail.
What i get
i do get accurate results, but those results inclues a duplicate row containing each invoice that has been modified. I only want one invoice per row. I want the one that's been modified last. Is this even possible? What am I doing wrong?
You can join and use a correlated subquery for filtering:
select i.*, e.*
from invoices i
inner join customer_invoice_edits e
on e.invoice = i.invoice
and e.date_last_modified_timestamp = (
select max(date_last_modified_timestamp)
from customer_invoice_edits e1
where e1.invoice = e.invoice
)
This will give you one record per row in the invoices table that has a match in customer_invoice_edits, along with the latest corresponding record in customer_invoice_edits.
If some invoices have no corresponding record in the dependant table, they will not appear in the resultset. If you do want to see them, then you can use a left join instead.
Is this what you want?
select
invoice,
email
from (
select
invoice,
email,
mod_status,
mod_amount,
date_last_modified_timestamp,
row_number() over (
partition by invoice, email
order by date_last_modified_timestamp desc
) rn
from table2
)
where rn=1
I'm pretty sure you want this field-by-field. Here is an example:
select i.invoice,
nz( (select top (1)
from invoice_edits as ie
where ie.invoice = i.invoice and ie.email is not null
), i.email
) as email,
nz( (select top (1)
from invoice_edits as ie
where ie.invoice = i.invoice and ie.mod_amount is not null
), i.original_bill
) as original_bill,
. . .
from invoices as i;

Select query where column does not equal column in different table in SQL Server

I have two different tables that contain order/pricing data. The first table, Orders, has columns order, item and price. The second table, Pricing has columns item, price, and effective date. I want to find all orders where orders.price <> pricing.price where effective date = 7/27/2017.
Orders:
Order Item Price
---------------------
1 ABC 12.50
2 ABC 12.00
3 ABC 11.50
4 XYZ 20.00
Pricing:
Item Price Effective Date
-------------------------------
ABC 12.50 7/27/2017
ABC 12.00 12/1/2016
ABC 11.50 12/1/2015
XYZ 25.00 7/27/2017
XYZ 20.00 12/1/2016
I want the query to tell me that order 2,3,4 do not have the most up to date pricing.
Please advise.
SELECT O.ORDER, (P.PRICE - O.PRICE) AS 'Variance'
FROM ORDERS O, PRICING P
WHERE O.ITEM = P.ITEM
AND P.[EFFECTIVE DATE] = '7/27/2017'
AND P.PRICE <> O.PRICE
ORDER BY O.ORDER ASC
It should be like this:-
SELECT * FROM Orders O
INNER JOIN Pricing P ON O.Item = P.Item
Where O.Price != P.Price AND [Effective Date] ='07/27/2017'
Are you looking for something like this?
SELECT o.[Order]
FROM Orders o JOIN Pricing p
ON o.Item = p.Item
AND p.Effective_Date = '2017-07-27'
AND o.price <> p.price
SQLFiddle demo
Output:
| Order |
|-------|
| 2 |
| 3 |
| 4 |
select o.order,o.Item,o.price from orders o inner join
pricing on o.item=pricing.item
where o.price <>(select p.price from pricing p where p.effectivedate = '2017-07-27' and p.item=o.item)