Using an aggregate function in an in line select statement - sql

T-SQL I am joining a services table to a payments table. I am using MAX, AVG for payments_rec'd and allowed_ amounts. I need Balance by last line number per service from the PayApp table. Sometimes payments are listed and then subtracted so the MIN balance doesn't work. I'm thinking of an inline select statement but cannot use an aggregate function inline.
SELECT DISTINCT [CDCLSVC].[RECNUM]
,[CDCLSVC].[ID]
,[CDCLSVC].[CLIENT_ID]
,[CDCLIENT].[CASE_NUM]
,[CDCLSVC].[UNIT_ID]
,[CDCLSVC].[SUB_UNIT_ID]
,[CDCLSVC].[SVC_ID]
,[CDCLSVC].[EMP_ID]
,[CDCLSVC].[BEG_DATE]
,UPPER([CAEMP].[SORT_NAME]) as Employee
,ISNULL([CDCLSVC].[EXTENDED_PRICE],0) As ExtendedPrice
,ISNULL([CDCLSVC].[BALANCE],0) As Balance
,ISNULL(MAX([CDPAYAPP].[ALLOWED_AMT]),0) AS AllowedAmount
,ISNULL(MAX([CDPAYAPP].[ADJ_AMT]),0)As AdjustedAmount
,ISNULL(SUM([CDPAYAPP].[PAY_APPLIED_AMT]),0)As PaymentApplied
,ISNULL(MAX([CDPAYAPP].[WRITE_OFF_AMT]),0)AS WriteOff
,ISNULL(MIN([CDPAYAPP].[SVC_BALANCE]),0) AS SvcBalance
,UPPER([CDPLACE].[DESC]) as Location
FROM [AnaDEV].[dbo].[CDCLSVC]
INNER JOIN [CAEMP] ON [CDCLSVC].[EMP_ID] = [CAEMP].[ID]
INNER JOIN [CDCLIENT] ON [CDCLSVC].[CLIENT_ID] = [CDCLIENT].[ID]
INNER JOIN [CDPLACE] ON [CDCLSVC].[PLACE_ID] = [CDPLACE].[ID]
INNER JOIN [CDPAYAPP] ON [CDPAYAPP].[CLSVC_ID] = [CDCLSVC].[ID]
WHERE [CDCLSVC].[BEG_DATE] between '2015-07-01' and DATEADD(DAY,-
(DATEPART(DAY, getdate())),GETDATE())
GROUP BY CDCLSVC.RECNUM,CDCLSVC.ID,CDCLSVC.CLIENT_ID,
CDCLIENT.CASE_NUM,CDCLSVC.UNIT_ID, CDCLSVC.SUB_UNIT_ID,
CDCLSVC.SVC_ID,CDCLSVC.EMP_ID, CDCLSVC.BEG_DATE, CAEMP.SORT_NAME,
CDCLSVC.EXTENDED_PRICE,CDCLSVC.BALANCE, [CDPLACE].[DESC]
ORDER BY [CDCLIENT].[CASE_NUM], [CDCLSVC].[ID]

I have a table of services(CDCLSVC) for a small medical clinic. Each service has many payments/adjustments(PAYAPP). So I have a one to many relationship. I also have a look-up table of Clinicians and another for Clients. The goal is to group the services by Clinician, then Client and have one row per service. This was easy aggregating the PAYAPP table using SUM, MIN, MAX. The problem was the Balance in the PAYAPP table was like a checkbook and I just needed the last entry.
PAYAPP
ROWNUM SVC PRICE ADJUSTMENT WRITE-OFF PAYMENTS BALANCE
1 001 $100 $25 $75
2 001 $100 $10 $65
3 001 $100 $50 $15
4 001 $100 $15 $0
EACH SERVICE MAX(PRICE) SUM(ADJUSTMENT) MAX(WRITE-OFF) SUM(PAYMENT)
The problem was getting the final BALANCE which I did by a sub query after the left outer join.
SELECT CDCLSVC.ID AS SERVICE_ID
--,CDPAYAPP.[ID] AS CDPAYAPP_ID
,CDCLSVC.[CLIENT_ID]
,CDPAYAPP.[CLSVC_ID]
,ISNULL(CDCLSVC.[EXTENDED_PRICE],0) AS EXTENTED_PRICE
,ISNULL(CDCLSVC.[BALANCE],0) AS BALANCE
,ISNULL(MAX(CDPAYAPP.[ALLOWED_AMT]),0) AS AllowedAmt
,ISNULL(MAX(CDPAYAPP.[ADJ_AMT]),0) AS AdjAmt
,ISNULL(MAX(CDPAYAPP.[WRITE_OFF_AMT]),0)AS WriteOff
,ISNULL(SUM(CDPAYAPP.[PAY_APPLIED_AMT]),0)AS AppliedAmt
,ISNULL(TBL1.SVC_BALANCE,0) AS SVC_BALANCE
,CDCLSVC.[EMP_ID]
,CDCLSVC.[PLACE_ID]
,CDCLSVC.[BEG_DATE]
,CDCLSVC.[UNIT_ID]
,CDCLSVC.[SUB_UNIT_ID]
FROM [AnaDEV].[dbo].[CDCLSVC]
LEFT OUTER JOIN CDPAYAPP
ON CDCLSVC.ID = CDPAYAPP.CLSVC_ID
LEFT OUTER JOIN
(SELECT SVC_BALANCE
,CLSVC_ID
FROM CDPAYAPP
WHERE CDPAYAPP.RECNUM IN
(SELECT MAX([RECNUM])
FROM [AnaDEV].[dbo].[CDPAYAPP]
GROUP BY CLSVC_ID)) AS TBL1
ON TBL1.CLSVC_ID = CDPAYAPP.CLSVC_ID
WHERE [CDCLSVC].[BEG_DATE] between '2015-07-01' and DATEADD(DAY,-(DATEPART(DAY, getdate())),GETDATE())
GROUP BY CDCLSVC.ID,CDCLSVC.[CLIENT_ID],CDPAYAPP.CLSVC_ID,CDCLSVC.
[EXTENDED_PRICE],CDCLSVC.[BALANCE],CDCLSVC.[EMP_ID]
,CDCLSVC.[BEG_DATE],TBL1.SVC_BALANCE, CDCLSVC.PLACE_ID
,CDCLSVC.[UNIT_ID] ,CDCLSVC.[SUB_UNIT_ID]
ORDER BY CDCLSVC.BEG_DATE DESC -- CHECKING THE WHERE CLAUSE

Related

SQL Server Left Join producing duplicates

I have 3 tables, Budgets, Income, and Expenses.
Budget table:
Income table:
Expenses table:
This is my SQL statement:
SELECT
Budgets.BudgetID, Budgets.BudgetName, Budgets.Username_FK,
Budgets.BudgetAmount, Budgets.SavePercentage,
Expenses.ExpensesID, Expenses.ExpensesAmount, Expenses.ExpensesCategory,
Income.IncomeID, Income.IncomeAmount, Income.IncomeCategory
FROM
Budgets
LEFT JOIN
Income ON Budgets.BudgetID = Income.BudgetID_FK
LEFT JOIN
Expenses ON Budgets.BudgetID = Expenses.BudgetID_FK
WHERE
BudgetName = '2019
And the results are as follows:
Based on my Income table, there is only 1 record tied to BudgetID = 3, but in the left join, it duplicates.
Ideally, I would want it to return "null" on the duplicates. How do I do this?
You have several rows in expenses per budgetID, so your join produces that many rows. I tend to suspect that the same situation could happen with income too.
If you want one row per budgetID, then one option is pre-aggregation and a left join (or outer apply). Say you want the total expense and income per budget, you would do:
select b.*, e.expenseAmount, i.amountAmount
from budgets b
left join (
select budgetID_FK, sum(expenseAmount) expenseAmount
from expenses
group by budgetID_FK
) e on e.budgetID_FK = b.budgetID
left join (
select budgetID_FK, sum(incomeAmount) incomeAmount
from income
group by budgetID_FK
) i on i.budgetID_FK = b.budgetID
Now you are grouping rows in the dependent tables by budgetID, so you can't see the other columns of these tables, such as incomeCategory or expenseCategory (which have multiple values per budgetID).
For budgetID 3 , you have 4 expenses( distinct expense IDs) of 50.00 in your expense table for the same user. I think what you would want is the total expenses of same category and budget ID i.e budgetID 3 in this case
SELECT
Budgets.BudgetID, Budgets.BudgetName, Budgets.Username_FK,
Budgets.BudgetAmount, Budgets.SavePercentage,
Income.IncomeID, Income.IncomeAmount, Income.IncomeCategory,
ex.ExpensesCategory,
ex.Total_ExpensesAmount,
FROM Budgets
LEFT JOIN Income ON Budgets.BudgetID = Income.BudgetID_FK
LEFT JOIN (Select BudgetID_FK, ExpensesCategory, SUM(ExpensesAmount) as Total_ExpensesAmount
FROM Expenses
GROUP BY BudgetID_FK, ExpensesCategory) ex ON Budgets.BudgetID = ex.BudgetID_FK
WHERE BudgetName = '2019'

Select top balance per branch, only ONE per branch

I have a database based on a bank, each branch having a Unique branch ID.
I am trying to create a query showing the top balance from the savings accounts at each branch. Only showing ONE row per branch ID.
I have this query which shows the top balances of savings accounts but I cannot figure out how to have this unique per branch ID.
select max(a.balance) as balance, b.bid, c.pname.firstname, c.pname.middleinitial, c.pname.lastname
from brancht b
join accountt a on b.bid=a.bid
join customeraccountt ca on ca.accnum = a.accnum
join customert c on c.custid = ca.custid
where acctype='Savings'
group by b.bid, c.pname.firstname, c.pname.middleinitial, c.pname.lastname
order by balance desc, b.bid
/
The results for this query returns this:
BALANCE BID PNAME.FIRSTNAME PNAME.MIDDLEINITIAL PNAME.LASTNAME
14050 101 Kornelia J Oconnor
13000 101 Myra D Johnson
9850 105 Lucie M Crosby
9050 105 Simon R Patrick
Another part of the query would be to also show their limit of overdraft on their current accounts (if they have one) if it's possible to have that in the same query?
Hopefully this is enough information.
Could you please try using ROW_NUMBER function as below. In the sub-query a ROW_NUMBER is assigned to each row based on balance partitioned by Branch. Row with maximum balance for a particular branch will be assigned 1 and so on ; Then you filter only those records having row_number -1 ; Each branch will have only one record (maximum balance) with ROW_NUMBER=1
select * from
(select balance, b.bid, c.pname.firstname, c.pname.middleinitial, c.pname.lastname,
row_number() over (partion by b.bid order by balance desc) as rn
from brancht b
join accountt a on b.bid=a.bid
join customeraccountt ca on ca.accnum = a.accnum
join customert c on c.custid = ca.custid
where acctype='Savings'
group by b.bid, c.pname.firstname, c.pname.middleinitial, c.pname.lastname
) where rn=1

Subquery amount not coming in full

I have this query:
Select I.Invoice_Number, PA.Invoice_Number, I.Line_Amount, PA.Invoiced_Amount
from XXX as PA
Left join (select Invoice_Number, Line_Amount from Invoices) as I
on PA.Invoice_Number = I.Invoice_Number
Group by PA.Invoice_Number;
Both should give me the same amount of cost (I.Line_Amount = PA.Invoice_Amount) per Invoice_Number, yet I.Line_Amount is only bringing the first row on the list, while PA.Invoiced.Number brings the sum of the cost on that Invoice.
I tried using sum(Line_Amount) within the subquery but all records come out as Null.
Is there a way for me to join both tables and make sure that the amounts per invoice match to the total amount of that invoice?
Thanks!!
If I understand you correctly (and you want to make sure that sum of Line_Amount in Invoices table is the same as Invoiced_Amount in XXX table) the second table should have invoice number and sum of amounts:
select I.Invoice_Number, PA.Invoice_Number, I.total, PA.Invoiced_Amount
from XXX as PA
left join (
select Invoice_Number, sum(Line_Amount) as total
from Invoices
group by Invoice_Number
) as I
on PA.Invoice_Number = I.Invoice_Number
You can try it here: http://sqlfiddle.com/#!9/d1d010/1/0

Access 2002 SQL for joining three tables

I have been trying to get this to work for a while now. I have 3 tables. First table has the Sales for customers which include the CustomerID, DateOfSales (Which always has the first of the month). The second table has the CustomerName, CustomerID. The third table has which customers buy what product lines. They are stored by CustomerID, ProductID.
I want to get a list (from one SQL hopefully) that has ALL the customers that are listed as buying a certain ProductID AND the maxDate from the Sales. I can get all of them IF there are sales for that customer. How the heck do I get ALL customers that buy the certain ProductID AND the maxDate from Sales or NULL if there is no sales found?
SalesList |CustomerList|WhoBuysWhat
----------|------------|-----------
maxDate |CustomerID |CustomerID
CustomerID| |ProductID=17
This is as close as I got. It gets all max dates but only if there have been sales. I want the CustomerID and a NULL for the maxDate if there were no sales recorded yet.
SELECT WhoBuysWhat.CustomerID, CustomerList.CustomerName,
Max(SalesList.MonthYear) AS MaxOfMonthYear FROM (CustomerList INNER
JOIN SalesList ON CustomerList.CustomerID = SalesList.CustomerID) INNER
JOIN WhoBuysWhat ON CustomerList.CustomerID = WhoBuysWhat.CustomerID
WHERE (((SalesList.ProductID)=17)) GROUP BY WhoBuysWhat.CustomerID,
CustomerList.CustomerName;
Is it possible or do I need to use multiple SQL statements? I know we should get something newer than Access 2002 but that is what they have.
You want LEFT JOINs:
SELECT cl.CustomerID, cl.CustomerName,
Max(sl.MonthYear) AS MaxOfMonthYear
FROM (CustomerList as cl LEFT JOIN
(SELECT sl.*
FROM SalesList sl
WHERE sl.ProductID = 17
) as sl
ON cl.CustomerID = sl.CustomerID
) LEFT JOIN
WhoBuysWhat wbw
ON cl.CustomerID = wbw.CustomerID
GROUP BY cl.CustomerID, cl.CustomerName;

How can I join these 3 tables

I have 3 tables:
Trip Promotion Promotion Cost.
1 ---- M 1 --------- M
Sample data include:
TripID TripName Date
XYZ123 Hawaii 09/06/09
YTU574 Japan 09/09/09
GHR752 US 11/07/09
PromotionID TripID Name
1 XYZ123 Poster
2 XYZ123 Brochure
3 GHR752 TV ad
CostID PromotionID Cost
1 1 $50
2 1 $100
3 1 $120
4 3 $2000
5 2 $500
I'm trying to build a query like this:
TripID Number of Promotions Total Cost
XYZ123 2 $770
GHR752 1 $2000
What I have is this:
SELECT
Trip.TripID, Count(Trip.TripID) AS [Number Of Promotions], Sum(PromotionCost.Cost) AS SumOfCost
FROM
Trip
INNER JOIN
(Promotion
INNER JOIN
PromotionCost ON Promotion.PromotionID = PromotionCost.PromotionID
) ON Trip.TripID = Promotion.TripID
GROUP BY
Trip.TripID;
And it gives me something like this:
TripID Number of Promotions Total Cost
XYZ123 4 $770
GHR752 1 $2000
I'm not sure why the Number of Promotions is messed up like that for the first one (XYZ123). It seems that somehow the JOIN is affecting it because if I use this:
SELECT
Trip.TripID, Count(Trip.TripID) AS [Number Of Promotions],
FROM
Trip
INNER JOIN
Promotion ON Trip.TripID = Promotion.TripID
GROUP BY
Trip.TripID;
It gives me the right number of promotions which is just 2.
You can add up the cost for each promotion in a subquery. That way, you only get one row for each promotion, and COUNT works to calculate the number of promotions per trip. For example:
select
t.TripId
, count(p.PromotionId) as [Number of Promotions]
, sum(pc.PromotionCost) as [Total Cost]
from trip t
left join promotions p on p.TripId = t.TripId
left join (
select
PromotionId
, PromotionCost = sum(cost)
from Promotions
group by PromotionId
) pc on pc.PromotionId = p.PromotionId
group by t.TripId
In case MS Access does not allow subqueries, you can store the subquery in a view, and join on that.
You can try to compensate for the duplicate Promotion rows by using COUNT(DISTINCT):
SELECT Trip.TripID, Count(DISTINCT Promotion.PromotionID) AS [Number Of Promotions],
Sum(PromotionCost.Cost) AS SumOfCost
FROM Trip INNER JOIN Promotion ON Trip.TripID = Promotion.TripID
INNER JOIN PromotionCost ON Promotion.PromotionID = PromotionCost.PromotionID
GROUP BY Trip.TripID;
What's going on is that by default, COUNT() counts the rows produced after all joins have been done. There are four promotion costs for TripID XYZ123, so four rows, even though the TripId occurs multiple times among those four rows.
It's easier to visualize if you try a similar query without the GROUP BY:
SELECT Trip.TripID, Promotion.PromotionID, PromotionCost.Cost
FROM Trip INNER JOIN Promotion ON Trip.TripID = Promotion.TripID
INNER JOIN PromotionCost ON Promotion.PromotionID = PromotionCost.PromotionID;
You'll see the four rows for XYZ123 (with duplicate PromotionID values), and one row for GHR752.
Re comments that MS Access doesn't support COUNT(DISTINCT): if that's the case, then you shouldn't do this in a single query. Do it in two queries:
SELECT Trip.TripID, SUM(PromotionCost.Cost) AS SumOfCost
FROM Trip INNER JOIN Promotion ON Trip.TripID = Promotion.TripID
INNER JOIN PromotionCost ON Promotion.PromotionID = PromotionCost.PromotionID
GROUP BY Trip.TripID;
SELECT Trip.TripID, Count(Promotion.PromotionID) AS [Number Of Promotions]
FROM Trip INNER JOIN Promotion ON Trip.TripID = Promotion.TripID
GROUP BY Trip.TripID;
The alternative is a very convoluted solution using subqueries, described in this article at Microsoft:
http://blogs.msdn.com/access/archive/2007/09/19/writing-a-count-distinct-query-in-access.aspx
Not the answer to your question but a useful recommendation (I hope): convert your query into a view by using the visual designer of SQL Server Management Studio, and examine the generated SQL code. You don't have to actually keep and use the generated view, but it is a good way of learning by example. I do that whenever I'm struggled with a complex query.
EDIT. Shame on me, I hand't read the tags: the question is MS-Access related, not SQL Server related. Anyway I think that my advice is still valid as far as concept-learning is the concern, since the SQL syntax is similar.