Summing a total from multiple sales into a single column - sql-server

I'm having issues with what I believe should be a simple problem in SQL Server 2017. I need to show the total sales price for a given sale. This is what I have written (note I deleted the totalprice sum formulae because it returned an error) :
USE [Antiques]
GO
DECLARE #TotalPrice INT
SELECT
Sales.SaleNo,
Sales.SalesDate,
Customers.FirstName,
Customers.LastName,
Products.Price,
#TotalPrice AS TotalPrice
FROM
Customers JOIN Sales ON Customers.CustomerID = Sales.CustomerID
JOIN SalesProducts ON Sales.SaleNo = SalesProducts.SaleNo
JOIN Products ON SalesProducts.ProductID = Products.ProductID
WHERE (Products.ProductID = SalesProducts.ProductID)
GO
This is the result:
Even if I remove the item price (which I have just put in temporarily and won't be in final code) there are still multiple lines per sale (1 for each item in the sale). Have I used the wrong joins? How can I get it looking like this:
Can anyone please tell me what I'm doing wrong? I've tried so many ways of using the Sum function and Group By and failing. (Note: this has to be a basic query and not a stored procedure unfortunately)

Actually you're not too far away. I'd move the grouping in a derived table and join that, to get the totalprice.
SELECT sales.saleno,
sales.salesdate,
customers.firstname,
customers.lastname,
x.totalprice
FROM sales
INNER JOIN customers
ON customers.customerid = sales.customerid
INNER JOIN (SELECT salesproducts.saleno,
sum(products.price) totalprice
FROM salesproducts
INNER JOIN products
ON products.productid = salesproducts.productid
GROUP BY salesproducts.saleno) x
ON x.saleno = sales.salesno;

Related

SQLite - Return 0 if null

I have an assignment in Database Management Systems in which I have to write queries for given problems.
I have 4 problems, of which I solved 3 and stuck with the last one.
Details:
Using version 1.4 of the Chinook Database
(https://chinookdatabase.codeplex.com/).
SQLite DB Browser
Chinook Sqlite AutoIncrementPKs.sqlite file​ in the directory with Chinook files is the database I am working on
Problem Statement:
Write a query to generate a ranked list of employees based upon the amount of money brought in via customer invoices for which they were the support representative. The result set (see figure below) should have the following fields (in order) for all employees (even those that did not support any customers): ID (e_id), first name (e_first name), last name (e_last_name), title (e_title), and invoice total (total_invoices). The rows should be sorted by the invoice total (greatest first), then by last name (alphabetically), then first name (alphabetically). The invoice total should be preceded by a dollar sign ($) and have two digits after the decimal point (rounded, as appropriate); in the case of employees without any invoices, you should output a $0.00, not NULL. You may find it useful to look at the IFNULL, ROUND, and PRINTF functions of SQLite.
Desired Output:
My Query:
Select Employee.EmployeeId as e_id,
Employee.FirstName as e_first_name,
Employee.LastName as e_last_name,
Employee.Title as e_title,
'$' || printf("%.2f", Sum(Invoice.Total)) as total_invoices
From Invoice Inner Join Customer On Customer.CustomerId = Invoice.CustomerId
Inner Join Employee On Employee.EmployeeId = Customer.SupportRepId
Group by Employee.EmployeeId
Having Invoice.CustomerId in
(Select Customer.CustomerId From Customer
Where Customer.SupportRepId in
(Select Employee.EmployeeId From Employee Inner Join Customer On Employee.EmployeeId = Customer.SupportRepId)
)
order by sum(Invoice.Total) desc
My Output:
As you can see, the first three rows are correct but the later rows are not printed because employees don't have any invoices and hence EmployeeID is null.
How do I print the rows in this condition?
I tried with Coalesce and ifnull functions but I can't get them to work.
I'd really appreciate if someone can modify my query to get matching solutions.
Thanks!
P.S: This is the schema of Chinook Database
It often happens that it is simpler to use subqueries:
SELECT EmployeeId,
FirstMame,
LastName,
Title,
(SELECT printf("...", ifnull(sum(Total), 0))
FROM Invoice
JOIN Customer USING (CustomerId)
WHERE Customer.SupportRepId = Employee.EmployeeId
) AS total_invoices
FROM Employee
ORDER BY total_invoices DESC;
(The inner join could be replaced with a subquery, too.)
But it's possible that you are supposed to show that you have learned about outer joins, which generate a fake row containing NULL values if a matching row is not found:
...
FROM Employee
LEFT JOIN Customer ON Employee.EmployeeId = Customer.SupportRepId
LEFT JOIN Invoice USING (CustomerID)
...
And if you want to be a smartass, replace ifnull(sum(...), 0) with total(...).

my join query returns inncorrect values

i am having problem when I query the sql server to retrieve records from different three tables its performing totally incorrect values for SUM([Bills.Extrabill_dollar]) and SUM([Receipts.Amount]), bellow is my query please help me.
select Customers.SNO, SUM(Bills.Extrabill_dollar) as [ExtraAmount],SUM(Receipts.Amount) as [Received]
from
Customers left join Bills on Customers.SNO=Bills.CustomerSNO
left join Receipts on Customers.SNO=Receipts.CSNO
group by Customers.SNO
but if i use bellow two separate queries to return SUM(Extra_dollar) and SUM(Extra_dollar)
SELECT Bills.CustomerSNO as [CustomerSNO],
SUM(Bills.Extrabill_dollar) AS [TotalExtraAmount] from Bills
group by Bills.CustomerSNO
SELECT Receipts.CSNO as [CustomerSNO],
SUM(Receipts.Amount) AS [TotalReveied] from Receipts
group by Receipts.CSNO the correct results will be returned which won't be returned if combine these tables by using JOIN, please help me Dears.
By saying that you got incorrect answers, I wouldn't know.
I think since you're using LEFT JOIN, it might be causing the problem.
Left join takes all the values from Customers, even the unmatched rows will be present, which will be mapped to NULL values under Bills and Receipts.
You can use this code and try it out.
SELECT Customers.SNO
, SUM(Bills.Extrabill_dollar) AS [ExtraAmount]
, SUM(Receipts.Amount) AS [Received]
FROM
Customers INNER JOIN Bills
ON Customers.SNO=Bills.CustomerSNO
INNER JOIN Receipts
ON Customers.SNO=Receipts.CSNO
GROUP BY Customers.SNO
select Customers.SNO, SUM(Bills.Extrabill_dollar) as [ExtraAmount],SUM(Receipts.Amount) as [Received]
from
Customers right join Bills on Customers.SNO=Bills.CustomerSNO
right join Receipts on Customers.SNO=Receipts.CSNO
group by Customers.SNO
Dear all thanks for your help, after long journey i found the answer:
select c.SNO, c.CName,
isnull((select SUM(PricePerKwt_dollar) from Bills
where Bills.CustomerSNO =c.SNO),0.00) as [TotalBills],
isnull((select SUM(Receipts.Amount) from Receipts
where Receipts.CSNO = c.SNO),0.00) as [TotalReceipts]
from Customers c

Using Max with Date Values with or without the Nulls

Now, after so many hours trying to figure this out, i really now need help.
I have a table which contains millions of records of about 50,000 customers. Each customer has at least 2000 transactions.
I want to have a table that holds the latest date each customer had either bought or sold something on the website.
I have tried
'FOR PURCHASES
Update tblmycustomers
set LastBoughtdate = (Select ISNULL(Max(trndate), '01-Jan-1900') from tbltransactions where Type = 'PURCHASES')
from tbltransactions.AccountRef = tblmycustomers.AccountNo
It works fine for records that have "PURCHASES" but for those who dont have PURCHASES, it saves another date which is wrong
I want it to return the default date ("01-Jan-1900") for every customer who doesn't have purchases and return the Maximum transaction date for those who has.
Please help me.
Try this:
UPDATE c
SET LastBoughtdate = ISNULL(Q.MaxBoughtDate, '1900-01-01')
FROM tblmycustomers c
LEFT JOIN
(SELECT AccountRef, MAX(trndate) AS MaxBoughtDate
FROM tblTransactions
WHERE Type = 'PURCHASES'
GROUP BY AccountRef) Q ON Q.AccountRef = AccountNo
I believe this would work. The driver table being "customers", you want to left join to the transactions...so you get all customer records even if there is no transactions. So...do something like this :)
Select cust.AccountNo
, isnull(max(trans.trndate), convert(date,'01/01/1900')) as LastBoughtDate
from tblmycustomers as cust
left join tbltransactions as trans on cust.AccountNo = trans.AccountRef
where type = 'PURCHASES'
group by cust.AccountNo
This updates based on a max transdate of an left outer joined subquery of the account number.
UPDATE
T
SET
T.LastBoughtdate=COALESCE(A.LastPurchaseDate,'01/01/1900')
FROM
tblmycustomers T
LEFT OUTER JOIN
(
SELECT
T2.AccountRef
LastPurchaseDate=MAX(trndate)
FROM
tbltransactions T2
WHERE
T2.Type='PURCHASES'
GROUP BY
T2.AccountRef
)AS A ON A.AccountRef=T.AccountNo
If your "LastBoughtdate" is DateTime. I think you need to convert the "01-01-1900'. Convert(Date, '01-01-1900', 101).

Filling up a table in sql with data from another table only if it does not already exist

I am working on a problem in SQL Server that is mind boggling. What I am trying to accomplish is, I have a table temp2 (picture below) that houses data from a lot of inner joins which is then used for a SSRS report.
The problem I am trying to solve is, how can I fill in the missing titles for each employee even if they have not put any values in it for the dates provided?
Question is, is it possible to fill in the missing titles from ProjectName for each Employee? As seen in the SSRS report, each employee should have all of the ProjectName being returned from the data set which is reading the table temp2...
EDIT
So This is what I tried and even Though I have gotten all the projectnames into my temp2, this is ugly and inefficient. The ssrs will take too long to run because of unwanted data.
Select distinct Employee = Coalesce(a.Employee, #SelectEmployee), EmpId = Coalesce(a.EmpId, (Select PkId from AllRef Where Ness='All')), c.Day, Title=Coalesce((case when a.Title like '%-%'
then left(a.Title, charindex('-', a.Title))
else a.Title
end),''), p.ProjectName, Description =coalesce(a.Description,''), Val = Coalesce(a.Val,''), AbbrevJob = COALESCE(a.abbrevjob, ''),
week1Total=(select sum(val) as week1 from temp1 WHERE day >= Dateadd("d", -14, #WeekEnding) AND day <= Dateadd("d", -7, #WeekEnding)),
week2Total=(select sum(val) as week2 from temp1 WHERE day >= Dateadd("d", -7, #WeekEnding) AND day <= #WeekEnding )
from dbo.Calender as c
left outer join temp2 as a
on c.Day = a.Day
cross join ProjectName p
--on p.PkId = a.Abbrevjob-2
Where c.Day >= Dateadd("d",-13,#WeekEnding) and c.Day <= #WeekEnding
order by EmpId asc
The Cross Join did accomplish the task but the repetition is killing performance. Anyone knows how to deal with that?
The usual way to do that is to build a matrix of all employees and all projects, and then optionally query the hours. For example:
; with Employees as
(
select distinct EmployeeName
from TableWithEmployees
)
, Projects as
(
select distinct ProjectName
from TableWithProjects
)
select *
from Employees e
cross join
Projects p
left join
TableWithDetails d
on d.EmployeeName = e.EmployeeName
and d.ProjectName = p.ProjectName
The left join means that rows without details will not be filtered out.
Though I never done it before, solution that comes upon my mind is pretty simple...
The problem with that report is that you try to inner join data with the project name table. It's not actually the problem, it's the right approach that everyone uses when they need such type of report.
But when it comes to the problem you stated, then it is the problem...
The point of getting the whole list of project names is outer joining project name table. But in that case you still get only projects per employee and if there is any project name not filled by employee record, it will still appear on the list, but only once and not assigned to any employee. And that's not what you need.
So solution is to outer join employees and project names first, and then inner join your data table.
You can just pick DISTINCT empId values from your data table and FULL OUTER JOIN it on ProjectNames. Then LEFT OUTER JOIN results to data table, this time by empId = empId and PkId = AbbrevJob (if I got right the columns which hold project name id-s).
It should work, let me know please whether it does or not, good luck with that!

SQL: Summing columns with a similar column in common

I'm extremely new to SQL Sever and so I apologize if the question is worded strange. I am doing a homework assignment, and this is the question:
"A manager wants to know the email address, number or orders, and the total amount of purchases made by each customer. Create a summary query that returns these three items for each customer that has orders."
I have all of the data queried, the problem is when I pull data from each customer, it will show the quantity of items per order, and I need the items to be pooled together into one column. This is my query thus far (again, total noob, please excuse any poor syntax, etc.)
SELECT EmailAddress,
ItemPrice - DiscountAmount * Quantity AS TotalPurchaseAmount,
COUNT(*) AS OrderQty
FROM Customers
JOIN Orders ON Customers.CustomerID = Orders.CustomerID
JOIN OrderItems ON Orders.OrderID = OrderItems.OrderID
GROUP BY Orders.CustomerID,
OrderItems.ItemPrice, OrderItems.DiscountAmount,
OrderItems.Quantity,
Customers.EmailAddress;
The following is a small bit of the result set that I get:
Email Address OrderTotal OrderQty
allan.sherwood#yahoo.com 253.15 2
allan.sherwood#yahoo.com 839.30 2
allan.sherwood#yahoo.com 1208.16 2
barryz#gmail.com 303.79 4
christineb#solarone.com 479.60 2
david.goldstein#hotmail.com 299.00 2
david.goldstein#hotmail.com 489.30 1
david.goldstein#hotmail.com 479.60 1
So as you can see, I have several orders I need to smoosh together into one single row per e-mail, I have looked and looked for an answer but the only thing I can find is how to find duplicates and ignore them, not combine their data. Any help is extremely appreciate, thanks so much for taking the time to read this :) If my question doesn't make sense please let me know so I can clear up any bad wording I may have used!
Just do GROUP BY CustomerID, EmailAddress:
SELECT
c.EmailAddress,
SUM((i.ItemPrice - i.DiscountAmount) * Quantity) AS TotalPurchaseAmount,
COUNT(*) AS OrderQty
FROM Customers c
INNER JOIN Orders o
ON c.CustomerID = o.CustomerID
INNER JOIN OrderItems i
ON o.OrderID = i.OrderID
GROUP BY
c.CustomerID, c.EmailAddress
Additional note: Use aliases for your tables
You need to change your formula and remove columns that you dont want to group by from select query..
for example your query should be something like this
SELECT EmailAddress,
--do your aggregation here
blah AS TotalPurchaseAmount,
COUNT(*) AS OrderQty
FROM Customers
JOIN Orders ON Customers.CustomerID = Orders.CustomerID
JOIN OrderItems ON Orders.OrderID = OrderItems.OrderID
GROUP BY Orders.CustomerID,
Customers.EmailAddress;

Resources