Calculate AVG in SQL Select statement when some values are NULL - sql

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]
;

Related

Why does my record count increase as my denominator for a division performed in a nested query increases?

In my query I'm trying to calculate the total overtime worked by employees by dividing basic salary by 173(hours in a month) which gives me the hourly rate then dividing the total overtime amount of employee by the hourly rate. To my surprise the record count increases as the number of hours in a month increases, should it not decrease?
Here's my script:
select 'Employees that worked more overtime' [CAAT],*
from ( select H.[Month]
,H.[Employee Code]
,H.[Department]
,H.[Job title]
,H.[Surname]
,H.[Full Names]
,H.[Basic Salary]
,R.[Overtime]
,H.[Hourly Rate]
,round(R.[Overtime] / H.[Hourly Rate],2) [Overtime Hours]
from (select [Month]
,[Employee Code]
,Department
,[Job title]
,[Surname]
,[Full Names]
,nullif(convert(money,[Amount]),0.00) [Basic Salary]
,nullif(round(convert(money,[Amount]) / 173,2),0.00) [Hourly Rate]
from [Salary DB]
where [Field Desc] = 'ED01-Basic Salary') H
left join
(select [Month]
,[Employee Code]
,nullif(sum(convert(money,[Amount])),0.00) [Overtime]
from [Salary DB]
where [Field Desc] in ('ED02-O/Time 1.5','ED02-O/Time 2.0','ED42-Sunday Pay')
group by [Month]
,[Employee Code]) R
on H.[Employee Code] = R.[Employee Code]
and H.[Month] = R.[Month]) [Data]
where [Overtime Hours] > '40'
Order by [Employee Code], [Month] Desc

mssql: add column with the same value for all rows to search results

I have my query:
SELECT [Shipment Date], [Amount] as [Running Costs], Sum([Amount]) OVER
(ORDER BY [Shipment Date]) as [Total Running Costs]
FROM...
This gets me 3 columns:
Shipment Date | Running Costs | Total Running Costs
I would like to add a fourth column to this query which has the same value for all rows, and the same number of rows as my original query results.
I know you could add for example '999'as Something to the search results, but how can I do the same for a sum of another column (example: Imagine the total sum of the a column in another table is 1500, and I want to have 1500 for all rows in the fourth column. Something like select sum(column_name)?
The database engine is MSSQL.
You can use a nested query
SELECT [Shipment Date], [Amount] as [Running Costs], [Total Running Costs], SUM([Total Running Costs] OVER ())
FROM
(
SELECT [Shipment Date], [Amount] as [Running Costs], Sum([Amount]) OVER
(ORDER BY [Shipment Date]) as [Total Running Costs]
FROM...
)
Nested window function should also work
SUM(SUM([Running costs]) OVER (ORDER BY [Shipment Date])) OVER ()

Access SQL SUM with subtraction

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]

Query using Group By, Top N and Sum

I have looked through the forum and can find a variety of examples to solve my problem but just cannot put everything together.
My situation is typical that I would like to show the Top 10 customers (Orders.[Customer Name]) by group (Shop_Lookup.ShopGroup]) for their total revenue.
I can get so far in producing the overall Top 10 regardless of ShopGroup but just cannot get my head around getting the Sub Query to work. My current code is -
SELECT TOP 10 Orders.[Customer Name],
Sum(Orders.[Actual Revenue]) AS [SumOfActual Revenue],
Orders.[This Month],
Shop_Lookup.[ShopGroup]
FROM Orders
INNER JOIN Shop_Lookup ON Orders.[ShopID] = ShopLookup.[ShopID]
WHERE ((Orders.[This Month])="current")
GROUP BY Orders.[Customer Name], Orders.[This Month], Shop_Lookup.[ShopGroup]
ORDER BY Sum(Orders.[Actual Revenue]) DESC;
Completely AIR CODED ! Proceed with caution.
You can use Sub Query to get this !
SELECT
Orders.[Customer Name],
Sum(Orders.[Actual Revenue]) AS [SumOfActual Revenue],
Orders.[This Month],
Shop_Lookup.[ShopGroup]
FROM
Orders
INNER JOIN
Shop_Lookup
ON
Orders.[ShopID] = ShopLookup.[ShopID]
WHERE
(
(Orders.[This Month] = 'Current')
AND
(Orders.ShopID IN
(SELECT
TOP 10 ShopID
FROM
Orders AS Dupe
WHERE
Dupe.ShopID = Orders.ShopID
)
)
)
GROUP BY
Orders.[Customer Name],
Orders.[This Month],
Shop_Lookup.[ShopGroup]
ORDER BY
Sum(Orders.[Actual Revenue]) DESC;
More information on Subqueries : http://allenbrowne.com/subquery-01.html

SQL Result Set Merge

I have a limitation where I can only send one result set to a reporting application at any one time, to produce an end report for a customer.
So a query like this
select
[AGENT],
[TRANSDATE],
[RECIPT NO],
[CUSTOMER NAME],
[ORDER NO] ,
[TRANS NO] ,
QUANTITY,
[AMOUNT COST],
From [Customer] C
However I need lots of totals at the bottom such as this query for some of the columns. I cannot make any changes to front end due to it being a legacy reporting application.
select
Sum ( QUANTITY ) as [SUM OF QUANTITY] ,
Sum ( AMOUNT COST ) AS [SUM OF AMOUNT COST]
From [Customer] C
Obviously I simplified the queries I am using. So the question is how to make 2 results sets one result set in SQL?
Union and union all failed due to date columns being defaulted if you use blank for a column in end application.
Rollup or Pivoting or CTE I kinda thought of but cannot see a solution yet.
what about windowed functions?
like...
select
[AGENT],
[TRANSDATE],
[RECIPT NO],
[CUSTOMER NAME],
[ORDER NO] ,
[TRANS NO] ,
QUANTITY,
[AMOUNT COST],
Sum ( QUANTITY ) over () as [SUM OF QUANTITY] ,
Sum ( [AMOUNT COST] ) over () AS [SUM OF AMOUNT COST]
From [Customer] C