Access SQL SUM with subtraction - sql

I am writing what I thought would be a relatively simple Access DB for a doctor client of mine who has a very small practice. I am having trouble with reporting a patient's balance.
I have 3 tables:
Patients (id, [First Name], [Last Name], {other cols})
Treatments (id, Fee, Patient_id, {other cols})
Payments (id, Amount, Patient_id, {other cols})
I want a query to simply show me the total fees for treatments, the total amount paid, and the current balance. I wrote the following...
Select
Patients.[Last Name],
Patients.[First Name],
SUM(select SUM(Treatments.Fee) from Treatments Where Treatments.Patient=#PatientID)
AS CHARGES,
SUM(select SUM(PAYMENTS.AMOUNT) from PAYMENTS Where PAYMENTS.Patient=#PatientID)
AS PAYMENTS,
SUM(
(select SUM(Treatments.Fee) from Treatments Where Treatments.Patient=#PatientID)
- (select SUM(PAYMENTS.AMOUNT) from PAYMENTS Where PAYMENTS.Patient=#PatientID)
)
as Balance
FROM Patients,Treatments,PAYMENTS
WHERE Patients.ID = #PatientID
GROUP BY Patients.[Last Name],Patients.[First Name]
The Charges and Payments columns work fine, but the Balance I'm given is weird. The below is based on:
5 treatment entries for this patient in the amounts, 50,25,35,45,125 (280).
3 payment entries in the amounts, 15,60,25 (100).
I expect:
Charges = $280, Payments = $100, Balance = $180
But I get:
Charges = $280, Payments = $100, Balance = $2,700

By using this FROM clause:
FROM Patients,Treatments,PAYMENTS
you're creating a Cartesian product.
Meaning your sum calculation for Balance will be multiplied by the number of records in each respective table (1 in Patients, 5 in Treatments, 3 in PAYMENTS):
15*total of fees- 15*total of payments = 15*280-15*100 = 4200-1500 =2700
You don't need Treatments and PAYMENTS in your FROM clause as you're doing your calculations in your sub query.
Use this statement instead:
Select
Patients.[Last Name],
Patients.[First Name],
SUM(select SUM(Treatments.Fee) from Treatments Where Treatments.Patient=#PatientID)
AS CHARGES,
SUM(select SUM(PAYMENTS.AMOUNT) from PAYMENTS Where PAYMENTS.Patient=#PatientID)
AS PAYMENTS,
SUM(
(select SUM(Treatments.Fee) from Treatments Where Treatments.Patient=#PatientID)
- (select SUM(PAYMENTS.AMOUNT) from PAYMENTS Where PAYMENTS.Patient=#PatientID)
)
as Balance
FROM Patients
WHERE Patients.ID = #PatientID
GROUP BY Patients.[Last Name],Patients.[First Name]

Select
Patients.[Last Name],
Patients.[First Name],
SUM( treatments.Fee)
AS CHARGES,
SUM(PAYMENTS.AMOUNT)
AS PAYMENTS,
SUM(Treatments.Fee)
- SUM(PAYMENTS.AMOUNT)
as Balance
FROM Patients a join Treatments b on a.id=b.patientid join PAYMENTS c on a.id=c.patientid
WHERE Patients.ID = #PatientID
GROUP BY Patients.[Last Name],Patients.[First Name]

Related

Query to Sum values from one table based on values from another table, considering null values

I have two tables in MS Access. One for contracts, with contract id and total value. Another one for the payments made in each contract.
So for example when a client gets a new contract for a total value of USD 100.00 then he can pay it in any amount he want up to the total value, like two payments of 50.00 or one of 30.00 and another of 70.00 or even one hundred payments of 1.00.
And those payments go to the payments table with the contract id and the payment amount.
I need to make a query to find how much do they still have to pay for each contract. What is the net amount for each contract.
I managed to make a query that gives me the result I need, but if the contract is new and there are no payments on the payments table for this new contract, the net amount shown in my query is zero (blank field) and I needed it to show the total value of the contract instead.
For example
Contract of 100.00 with two payments of 10.00 and 15.00 should show the net value as 75.00
Contract of 100.00 with no payments should show net value as 100.00
Here is what I have so far:
'Query 1
SELECT Contracts.[Contract ID],
Sum(Payments.[Payment Value]
FROM Contracts
LEFT JOIN Payments
ON Contracts.[Contract ID] = Payments.[Contract ID]
GROUP BY Contracts.[Contract ID]
Query 2:
SELECT Contracts.[Contract ID],
Contracts.[Contract Value],
Query1.[SumOfValue Payment Value],
[Contract Value] - [SumOfValue Payment Value]
INTO NetValues
FROM Contracts
INNER JOIN Query1
ON Contracts.[Contract ID] = Query1.[Contract ID]
Basically the Query 1 , sums the payments for each contract and Query 2 calculates the net value (total value - paid amount)
If there is any better way to achieve all of this please let me know.
You can do it as a single query as follows:
SELECT c.[Contract ID],
c.[contract value],
p.Paid,
c.[contract value]-nz(p.paid) AS Remaining
FROM Contracts AS c
LEFT JOIN
(SELECT [Contract ID], sum([Payment Value]) as paid
FROM Payments
group by [Contract ID]) AS p
ON c.[Contract ID] = P.[Contract ID];
Given these two tables:
The query will produce this result:
" ... if the contract is new and there are no payments on the payments table for this new contract, the net amount shown in my query is zero (blank field)"
Actually, when you have no payments recorded for a given contract, Query1 gives you Null, not zero, as the sum of the payments. And any number minus Null gives you Null, not that first number.
If you want the Null treated as zero, use the
Nz Function
Here's an Immediate window example of those issues:
Contract_Value = 100.00
SumOfValue_Payment_Value = Null
? Contract_Value - SumOfValue_Payment_Value
Null
' use Nz() to substitute zero for Null
? Contract_Value - Nz(SumOfValue_Payment_Value, 0)
100
You can do the same in Query2 by including this:
[Contract Value] - Nz([SumOfValue Payment Value], 0)

Calculate AVG in SQL Select statement when some values are NULL

I have struggled to find a solution to this problem, so any help would be hugely appreciated.
I have two tables, lets call one table_a and one table_b, representing two possible sources of customer orders. They both contain a field called "customer_number" which I am using to outer join table_b to table_a. I am then calculating an average customer order size given the two tables.
Where i struggle is where a customer has an entry in either table, but not in both. Then my average calc returns NULL.
Table_a:
Customer Number
Total Online Orders
123456789
1350
987654321
650
Table_b:
Customer Number
Total InStore Orders
123456789
350
So basically the second customer does not have an entry in table_b.
My code as follows:
select distinct
a.[customer number],
a.[Total Online Orders],
b.[Total InStore Orders],
coalesce(a.[Total Online Orders] + b.[Total InStore Orders]) / 2 as [Average Order Size]
from table_a a
full outer join table_b b on a.[customer number]=b.[customer number]
Results table:
Customer Number
Total Online Orders
Total InStore Orders
Average Order Size
123456789
1350
350
850
987654321
650
NULL
NULL
I basically want the results table to show 650 for customer 987654321. Any ideas what I am doing wrong?
thanks!
You can use the brute force method:
( coalesce(a.[Total Online Orders], 0) + coalesce(b.[Total InStore Orders], 0)) /
nullif(case when a.[Total Online Orders] is not null then 1 else 0 end +
case when b.[Total InStore Orders] is not null then 1 else 0 end, 0)
)
) as [Average Order Size]
Use COALESCE() for the values of the 2 columns twice:
(
coalesce(a.[Total Online Orders], b.[Total InStore Orders]) +
coalesce(b.[Total InStore Orders], a.[Total Online Orders])
) / 2 as [Average Order Size]
Consider UNION as well, which handles a few other cases too, especially where customer number isn't necessarily unique in each table:
WITH cte (xtype, [customer number], order_amt) AS (
select 1 AS xtype, [customer number], [Total Online Orders] FROM table_a UNION ALL
select 2 AS xtype, [customer number], [Total InStore Orders] FROM table_b
)
SELECT [customer number]
, SUM(CASE WHEN xtype = 1 THEN order_amt ELSE 0 END) AS [Total Online Orders]
, SUM(CASE WHEN xtype = 2 THEN order_amt ELSE 0 END) AS [Total InStore Orders]
, AVG(order_amt) AS [Average Order Size]
FROM cte
GROUP BY [customer number]
ORDER BY [customer number]
;

How do I join these 3 tables, do a sum calculation and group by multiple values?

So I have these 3 tables below:
User Table:
Deposit Table:
Withdrawal Table:
Question:
How do I join these 3 tables together, having the values grouped by:
Month
Sex
Age
State
Expected Output:
As seen from my expected output above, I want to separate these users into different buckets
i.e.
Month: January, Sex: F, Age: 18, State: Arizona (Bucket 1)
Month: January, Sex: M, Age: 21, State: Texas (Bucket 2)
and find the sum(Deposit.amount) & sum (Withdrawal.amount) group by the fields above ^
as well as how many users are in this bucket.
Clarification:
"Month" column in my Expected Ouput is based on Transaction Month for both Deposit & Withdrawal table combined, I just added the "Account Created Month" column in User table just in case its possible to do a 3-table join through Month values.
This should work:
SELECT tr.Month, u.Sex, u.Age, u.Stage
, Sum(CASE WHEN [Type] = 'D' THEN tr.Amount ELSE 0 END) As [Total Deposit Amount]
, Sum(CASE WHEN [Type] = 'W' THEN tr.Amount ELSE 0 END) As [Total Withdrawal Amount]
, COUNT(distint u.UserID) As [Number of Users in this Category]
FROM (
SELECT userid, [Deposit Amount] As Amount, [Transaction Month] As Month, 'D' As [Type] FROM [Deposit]
UNION ALL
SELECT userid, [Withdrawal Amount], [Transaction Month], 'W' FROM [Withdrawal]
) tr
INNER JOIN User u ON u.UserID = tr.UserId
GROUP BY tr.Month, u.Sex, u.Age, u.State
Really, deposits and withdrawals should be the same table (Transactions) to begin with, where the only difference is withdrawals are negative.
Also... please stop using images to represent your sample data and results. It makes things so much harder on us to help you, which makes it less likely you'll get a good or prompt answer.

Subtract column data from two unrelated tables

This is the database diagram I want to determine the blood that's remaining in each bank this achieved by subtracting [Amount Accepted] from [Amount Donated]
I have tried to make selections of blood drawn into banks and blood withdrawn from banks but my problem is I can't subtract the two columns [blood drawn] and [blood withdrawn] because they are in tables which are not related
You can try like following.
SELECT *, [blood drawn(ml)]-[blood withdrawn(ml)]
FROM
(
<YOUR QUERY1>
)A
INNER JOIN
(
<YOUR QUERY2>
)B
ON A.[Blood Bank Name]= B.[Blood Bank Name]
Wrap both Selects in Derived Tables or Common Table Expressions and join them, e.g.
with donated as
( select ...
)
, accepted as
( select ...
)
select ...
from donated as a
join accepted as a
on a.[blood bank name] = d..[blood bank name]
Note the '-' before [Amount Accepted] is the key.
SELECT x.[Blood Bank Name], SUM(x.[Net Amount])
FROM
(
SELECT [Blood Bank Name], [Amount Donated] AS [Net Amount]
FROM [Donation Record]
UNION ALL
SELECT [Blood Bank Name], -[Amount Accepted] AS [Net Amount]
FROM [Transfusion Record]
) x
GROUP BY x.[Blood Bank Name]

How get previous values form last transaction using PK and FK

I have this SQL Queries,
SELECT PaymentID, CustomerID, PaymentDate, Amount, Balance, Credit
FROM Payment
WHERE (PaymentDate = '2012-11-03')
I also like to print previous Balance and Credit form last transaction of the customer.
This is my try.
SELECT DISTINCT TOP 1 Balance, Credit, PaymentID
FROM Payment
WHERE CustomerID = '??' AND PaymentID < '??'
ORDER BY PaymentID DESC
As you can see this does nothing as Queries are not liked.
I think I have I have to use T-SQL or UNION but have no idea How to implement it.
This kind of Output I am trying to achieve.
Payment Table
PaymentID int
CustomerID varchar(50)
PaymentDate date
Amount decimal(18, 2)
Balance decimal(18, 2)
Credit decimal(18, 2)
Note:
This is for all the customers not only for single customer, it's like a sale Report.
There are can be more than one payment per customer in a day.
This might get you started
SELECT P1.paymentID, P1.CustomerID, P1.PaymentDate, max(PP.paymentID)
FROM Payment P1
Join Payment PP
on PP.paymentID < P1.paymentID
and PP.CustomerID = P1.CustomerID
and P1.PaymentDate = '2012-11-03'
GroupBy P1.paymentID, P1.CustomerID, P1.PaymentDate
not test but I think this will pull in the other data
select PC.*, PL.PaymentDate, PL.Amount, PL.Balance, PL.Credit
from
(SELECT P1.paymentID, P1.CustomerID, P1.PaymentDate, max(PP.paymentID) as PriorPID
FROM Payment P1
Join Payment PP
on PP.paymentID < P1.paymentID
and PP.CustomerID = P1.CustomerID
and P1.PaymentDate = '2012-11-03'
GroupBy P1.paymentID, P1.CustomerID, P1.PaymentDate) as PC
join Payment PL
on PL.paymentID = PC.PriorPID
and PL.CustomerID = PC.CustomerID