I'm trying to create a table that I can use for Accounts Receivable. It's a very simple table, needing only the total cost of an order, how much has been paid into the order, and the order number itself. I'm trying to adapt an existing query used for report generation to do so.
SELECT DISTINCTROW
Round(Sum(nz([Quantity]*[UnitPrice]*(1-[Discount])*100)/100)+
[RushCharge],2) AS TotalCost,
[Sum Of Payments Query].[Total Payments],
[Order Details].RushCharge AS RushCharge,
Orders.OrderID,
Orders.Cancel,
Orders.PriceQuote
INTO test2
FROM Orders
LEFT JOIN [Sum Of Payments Query]
ON Orders.OrderID = [Sum Of Payments Query].OrderID
GROUP BY Orders.OrderID,
Orders.Cancel,
[Sum Of Payments Query].[Total Payments],
Orders.PriceQuote
The issue is the age of the DB, where the total cost of an order is always dynamically generated instead of being stored somewhere (Even though there is only one form that alters it, but still), meaning I have to resort to the same. The Round function calculates the total cost of the order, and it works elsewhere, but here, it just prompts me for values instead of pulling them from Orders.
What am I doing wrong? I know it has to be something simple.
I am guessing that [Quantity], [unit price] and [Discount] and RushCharge are fields in [order details] table, which is not included in the query.
you could create a query that returns the totalcost per order
select orderid,
Round(Sum(nz([Quantity]*[UnitPrice]*(1-[Discount])*100)/100)+
[RushCharge],2) AS TotalCost from
from [order details]
group by orderid
and include this in your query in the same way you have [sum of Payments Query]
Related
I am writing a SQL query that needs to show the total number of orders from each store. The issue I am running into, is that while I can figure out how to sum the orders by product and each product is only sold by one store, I can't figure out how to total the orders by store alone
This is the code I currently have
SELECT storeID AS [STORE], Product_ID
, SUM(quantity) AS [ORDERS BY STORE]
FROM Fulfillment, Store
GROUP BY storeID, Product_ID;
This line of code leads to a repeat of storeID in the results, where ideally, I would only want storeID to be included in the results once with the total quantity of all of Product_ID being included. I tried to remove Product_ID from the GROUP BY statement, but this resulted in the following error
Column 'Fulfillment.Product_ID' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I'm new to SQL and am trying to learn, so any help and advice is greatly appreciated
#ZLK is correct that if your goal is a total number of units ordered ("quantity") of any product, simply remove the [product_id] column from the SELECT and GROUP BY.
However, it appears that you're referencing two tables ("FROM Fulfillment, Store") and not specifying how those tables are joined, creating a cartesian join - all rows in one table will be joined to all rows in the other table. If the [storeID] and [quantity] fields are available in the Fulfillment table, I recommend removing the Store table reference from the FROM clause (so "FROM Fulfillment" alone).
One last note: You mention that you want to count "orders". In some circumstances, an order may have multiple products and a quantity > 1. If your goal is the total number of "orders" regardless of the number of products or quantity of products on an order, you'll want to use "COUNT(DISTINCT orderID) as [Orders]" (where "orderID" is the reference to the unique order number).
I'm using an SQL Server database which has Order table contains a foreign key called invoice_id, this attribute belongs to Invoice table. Also, I have another table called "Receiptwhich also includesinvoice_idandamount_paid` attributes.
Many items can be assigned to the same invoice (customer may make many orders at once), also many receipts can be assigned to the same invoice (customer may make different payments to the same invoice as for example they can pay 50% and then pay the rest later).
So the problem I'm facing is when I try to select the total paid amounts from the Receipt table taking the order_id as a condition, the result will be multiplied according to the number of orders that have the same invoice_id
For example, customer A placed three orders at once, each order cost is 100 USD, which should be 300 USD in the invoice and he already paid that invoice. Now if I query the Receipt table for the paid amounts, the result will be 900 USD (300 USD * 3 orders), which is obviously incorrect.
I'm stuck at this issue, I believe there are some mistakes in my database logic, so please provide me your suggestions to solve this problem and also what should do with the database if the logic is incorrect.
Below is the query i'm using to get the result:
select sum(r.amount_paid), o.invoice_id from RECEIPT r, INVOICE i, ORDER o
where r.invoice_id = i.invoice_id
and o.invoice_id = i.invoice_id
group by o.invoice_id;
Here's three answers to three slightly different questions you might ask of your database.
Amount paid per invoice
If you are trying to get the total amount paid per invoice, all you need is
SELECT SUM(Amount_Paid) Total_Paid,
Invoice_ID
FROM Receipt
GROUP BY Invoice_ID
Amount paid per order
If you want to know the total paid per order, this is not quite possible in your data model as you have described it. If a invoice has three orders on it, and the invoice is only partly paid, there is no way to tell which of the the orders is paid and which is not.
You need some additional data structure that indicates how payments are applied to orders within an invoice, e.g. an Allocation or Split table.
Amount paid on invoices that pertain to one or more orders
On the other hand, if you want to know how much payment has been received on invoices that contain one or more order IDs, you could write this:
SELECT SUM(Amount_Paid) Total_Paid,
Invoice_ID
FROM Receipt
WHERE Invoice_ID IN (SELECT Invoice_ID
FROM Order
WHERE Order_ID IN (1,2,3,4)) --Edit these IDs for your specific case
GROUP BY Invoice_ID
Notice none of the queries above required any joins, so no multiplying :)
Please, try this:
SELECT SUM(r.amount_paid), r.invoice_id FROM RECEIPT r
JOIN INVOICE i ON r.invoice_id = i.invoice_id
JOIN ORDER o ON r.invoice_id = o.invoice_id AND r.order_id = o.order_Id
GROUP BY r.invoice_id;
From Northwind database I want to get total revenue generated by emplyee sales
Employee -> Orders -> "Order Details"
I am not sure if my solution gives the right data (it was partly guessing)
SELECT
Employees.FirstName, Employees.LastName,
SUM(CONVERT(MONEY, ("Order Details".UnitPrice * Quantity * (1 - Discount) / 100)) * 100) AS ExtendedPrice
FROM
((Orders
INNER JOIN
"Order Details" ON Orders.OrderID = "Order Details".OrderID)
INNER JOIN
Employees ON Orders.EmployeeID = Employees.EmployeeID)
GROUP BY
LastName, FirstName;
Northwind database structure can be found here
Thank you in advance. It would be great to have a nice explanation as well
Chris, your effort is pretty good first effort, so there are a few things to change on this.
You don't need to divide by 100 and then multiply by 100. The discount is already a %. Your operation just truncates the numbers. I would avoid to this too early in a process as it introduces rounding errors. It is better to keep numbers raw and keep their precision as best you can for as long as you can. It is OK to display numbers as money in the GUI though i.e. to 2 decimals but not in intermediate calculations due to error introduced by truncating.
Table names and field names with spaces should be handled using [] rather than quotes. That makes it easier to find misspelling so use [Order Details]
When grouping and summing, make sure you use the keys. So name is not a key, so use EmployeeID if you are trying to group individual employees, this is because in real datasets you may have 2 employees with the same name and their sales will be grouped together incorrectly using your code.
Try this course/book, it is a good intro to querying databases. https://www.microsoft.com/en-au/learning/exam-70-461.aspx
The reason how this works? Select syntax has Select [fieldlist] from [table] inner join [jointable] on [join fields] group by [grouping fields]. fieldlist can be a calculation as well as actual field names to display. "inner join" means you want only those orders, order details, employees where there is actual matching data - Correct in your scenario. [table] and [jointable] is the actual tables that contain your data in a relational sense.
There is obvisouly a lot here to learn in one go. I would work through some of the different SQL Server querying courses that you can google.
Here's a revised version of the code:
SELECT Employees.EmployeeID, Employees.FirstName, Employees.LastName, Sum([Order Details].UnitPrice * Quantity * (1 - Discount)) AS ExtendedPrice
FROM Orders
INNER JOIN [Order Details] ON Orders.OrderID = [Order Details].OrderID
INNER JOIN Employees ON Orders.EmployeeID = Employees.EmployeeID
group by Employees.EmployeeID, Employees.FirstName, Employees.LastName
order by Employees.FirstName, Employees.LastName;
I'm using Oracle 11g Application Express, and executing these commands within the SQL Plus CLI. This is for a class, and I cannot get past this problem. I don't know how to add the total quantity of the items on the orders - I get confused as I don't know how to take the SUM of the QUANTITY per ORDER (customers have multiple orders).
For each customer having an order, list the customer number, the number of orders
that customer has, the total quantity of items on those orders, and the total price for
the items. Order the output by customer number. (Hint: You must use a GROUP BY
clause in this query).
Tables (we will use):
CUSTOMER: contains customer_num
ORDERS: contains order_num, customer_num
ITEMS: contains order_num, quantity, total_price
My logic: I need to be able to calculate the sum of the quantity per order number per customer. I have sat here for over an hour and cannot figure it out.
So far this is what I can formulate..
SELECT customer_num, count(customer_num)
FROM orders
GROUP BY customer_num;
I don't understand how to GROUP BY very well (yes, I have googled and researched it for a bit, and it just isn't clicking), and I have no clue how to take the SUM of the QUANTITY per ORDER per CUSTOMER.
Not looking for someone to do my work for me, just some guidance - thanks!
select o.customer_num,
count(distinct o.order_num) as num_orders,
sum(i.quantity) as total_qty,
sum(i.total_price) as total_price
from orders o
join items i
on o.order_num = i.order_num
group by o.customer_num
order by o.customer_num
First thing:
you have to join the two tables necessary to solve the problem (orders and items). The related field appears to be order_num
Second thing:
Your group by clause is fine, you want one row per customer. But because of the join to the items table, you will have to count DISTINCT orders (because there may be a one to many relationship between orders and items). Otherwise an order with 2 different associated items would be counted twice.
Next, sum the quantity and total price, you can do this now because you've joined to the needed table.
This is also solved by using WHERE:
SELECT orders.customer_num, /*customer number*/
COUNT(DISTINCT orders.order_num) AS "num_orders", /*number of orders/c*/
SUM(items.quantity) as "total_qty", /*total quantity/c*/
SUM(items.total_price) as "total_price" /*total price/items*/
/* For each customer having an order, list the customer number,
the number of orders that customer has, the total quantity of items
on those orders, and the total price for the items.
Order the output by customer number.
(Hint: You must use a GROUP BY clause in this query). */
FROM orders, items
WHERE orders.order_num = items.order_num
GROUP BY orders.customer_num
ORDER BY orders.customer_num
;
I was once given this task to do in an RDBMS:
Given tables customer, order, orderlines and product. Everything done with the usual fields and relationships, with a comment memo field on the orderline table.
For one customer retrieve a list of all products that customer has ever ordered with product name, year of first purchase, dates of three last purchases, comment of the latest order, sum of total income for that product-customer combination last 12 months.
After a couple of days I gave up doing it as a Query and opted to just fetch every orderline for a customer, and every product and run through the data procedurally to build the required table clientside.
I regard this a symptom of one or more of the following:
I'm a lazy idiot and should have seen how to do it in SQL
Set operations are not as expressive as procedural operations
SQL is not as expressive as it should be
Did I do the right thing? Did I have other options?
You definitely should be able to do this exercise without doing the work equivalent to a JOIN in application code, i.e. by fetching all rows from both orderlines and products and iterating through them. You don't have to be an SQL wizard to do that one. JOIN is to SQL what a loop is to a procedural language -- in that both are fundamental language features that you should know how to use.
One trap people fall into is thinking that the whole report has to be produced in a single SQL query. Not true! Most reports don't fit into a rectangle, as Tony Andrews points out. There are lots of rollups, summaries, special cases, etc. so it's both simpler and more efficient to fetch parts of the report in separate queries. Likewise, in a procedural language you wouldn't try do all your computation in a single line of code, or even in a single function (hopefully).
Some reporting tools insist that a report is generated from a single query, and you have no opportunity to merge in multiple queries. If so, then you need to produce multiple reports (and if the boss wants it on one page, then you need to do some paste-up manually).
To get a list of all products ordered (with product name), dates of last three purchases, and comment on latest order is straightforward:
SELECT o.*, l.*, p.*
FROM Orders o
JOIN OrderLines l USING (order_id)
JOIN Products p USING (product_id)
WHERE o.customer_id = ?
ORDER BY o.order_date;
It's fine to iterate over the result row-by-row to extract the dates and comments on the latest orders, since you're fetching those rows anyway. But make it easy on yourself by asking the database to return the results sorted by date.
Year of first purchase is available from the previous query, if you sort by the order_date and fetch the result row-by-row, you'll have access to the first order. Otherwise, you can do it this way:
SELECT YEAR(MIN(o.order_date)) FROM Orders o WHERE o.customer_id = ?;
Sum of product purchases for the last 12 months is best calculated by a separate query:
SELECT SUM(l.quantity * p.price)
FROM Orders o
JOIN OrderLines l USING (order_id)
JOIN Products p USING (product_id)
WHERE o.customer_id = ?
AND o.order_date > CURDATE() - INTERVAL 1 YEAR;
edit: You said in another comment that you'd like to see how to get the dates of the last three purchases in standard SQL:
SELECT o1.order_date
FROM Orders o1
LEFT OUTER JOIN Orders o2
ON (o1.customer_id = o2.customer_id AND (o1.order_date < o2.order_date
OR (o1.order_date = o2.order_date AND o1.order_id < o2.order_id)))
WHERE o1.customer_id = ?
GROUP BY o1.order_id
HAVING COUNT(*) <= 3;
If you can use a wee bit of vendor-specific SQL features, you can use Microsoft/Sybase TOP n, or MySQL/PostgreSQL LIMIT:
SELECT TOP 3 order_date
FROM Orders
WHERE customer_id = ?
ORDER BY order_date DESC;
SELECT order_date
FROM Orders
WHERE customer_id = ?
ORDER BY order_date DESC
LIMIT 3;
Set operations are not as expressive as procedural operations
Perhaps more like: "Set operations are not as familiar as procedural operations to a developer used to procedural languages" ;-)
Doing it iteratively as you have done now is fine for small sets of data, but simply doesn't scale the same way. The answer to whether you did the right thing depends on whether you are satisfied with the performance right now and/or don't expect the amount of data to increase much.
If you could provide some sample code, we might be able to help you find a set-based solution, which will be faster to begin with and scale far, far better. As GalacticCowboy mentioned, techniques such as temporary tables can help make the statements far more readable while largely retaining the performance benefits.
In most RDBMS you have the option of temporary tables or local table variables that you can use to break up a task like this into manageable chunks.
I don't see any way to easily do this as a single query (without some nasty subqueries), but it still should be doable without dropping out to procedural code, if you use temp tables.
This problem may not have been solvable by one query. I see several distinct parts...
For one customer
Get a list of all products ordered (with product name)
Get year of first purchase
Get dates of last three purchases
Get comment on latest order
Get sum of product purchases for the last 12 months
Your procedure is steps 1 - 5 and SQL gets you the data.
Sounds like a data warehouse project to me. If you need things like "three most recent things" and "sum of something over the last 12 months" then store them i.e. denormalize.
EDIT: This is a completely new take on the solution, using no temp tables or strange sub-sub-sub queries. However, it will ONLY work on SQL 2005 or newer, as it uses the "pivot" command that is new in that version.
The fundamental problem is the desired pivot from a set of rows (in the data) into columns in the output. While noodling on the issue, I recalled that SQL Server now has a "pivot" operator to deal with this.
This works on SQL 2005 only, using the Northwind sample data.
-- This could be a parameter to a stored procedure
-- I picked this one because he has products that he ordered 4 or more times
declare #customerId nchar(5)
set #customerId = 'ERNSH'
select c.CustomerID, p.ProductName, products_ordered_by_cust.FirstOrderYear,
latest_order_dates_pivot.LatestOrder1 as LatestOrderDate,
latest_order_dates_pivot.LatestOrder2 as SecondLatestOrderDate,
latest_order_dates_pivot.LatestOrder3 as ThirdLatestOrderDate,
'If I had a comment field it would go here' as LatestOrderComment,
isnull(last_year_revenue_sum.ItemGrandTotal, 0) as LastYearIncome
from
-- Find all products ordered by customer, along with first year product was ordered
(
select c.CustomerID, od.ProductID,
datepart(year, min(o.OrderDate)) as FirstOrderYear
from Customers c
join Orders o on o.CustomerID = c.CustomerID
join [Order Details] od on od.OrderID = o.OrderID
group by c.CustomerID, od.ProductID
) products_ordered_by_cust
-- Find the grand total for product purchased within last year - note fudged date below (Northwind)
join (
select o.CustomerID, od.ProductID,
sum(cast(round((od.UnitPrice * od.Quantity) - ((od.UnitPrice * od.Quantity) * od.Discount), 2) as money)) as ItemGrandTotal
from
Orders o
join [Order Details] od on od.OrderID = o.OrderID
-- The Northwind database only contains orders from 1998 and earlier, otherwise I would just use getdate()
where datediff(yy, o.OrderDate, dateadd(year, -10, getdate())) = 0
group by o.CustomerID, od.ProductID
) last_year_revenue_sum on last_year_revenue_sum.CustomerID = products_ordered_by_cust.CustomerID
and last_year_revenue_sum.ProductID = products_ordered_by_cust.ProductID
-- THIS is where the magic happens. I will walk through the individual pieces for you
join (
select CustomerID, ProductID,
max([1]) as LatestOrder1,
max([2]) as LatestOrder2,
max([3]) as LatestOrder3
from
(
-- For all orders matching the customer and product, assign them a row number based on the order date, descending
-- So, the most recent is row # 1, next is row # 2, etc.
select o.CustomerID, od.ProductID, o.OrderID, o.OrderDate,
row_number() over (partition by o.CustomerID, od.ProductID order by o.OrderDate desc) as RowNumber
from Orders o join [Order Details] od on o.OrderID = od.OrderID
) src
-- Now, produce a pivot table that contains the first three row #s from our result table,
-- pivoted into columns by customer and product
pivot
(
max(OrderDate)
for RowNumber in ([1], [2], [3])
) as pvt
group by CustomerID, ProductID
) latest_order_dates_pivot on products_ordered_by_cust.CustomerID = latest_order_dates_pivot.CustomerID
and products_ordered_by_cust.ProductID = latest_order_dates_pivot.ProductID
-- Finally, join back to our other tables to get more details
join Customers c on c.CustomerID = products_ordered_by_cust.CustomerID
join Orders o on o.CustomerID = products_ordered_by_cust.CustomerID and o.OrderDate = latest_order_dates_pivot.LatestOrder1
join [Order Details] od on od.OrderID = o.OrderID and od.ProductID = products_ordered_by_cust.ProductID
join Products p on p.ProductID = products_ordered_by_cust.ProductID
where c.CustomerID = #customerId
order by CustomerID, p.ProductID
SQL queries return results in the form of a single "flat" table of rows and columns. Reporting requirements are often more complex than this, demanding a "jagged" set of results like your example. There is nothing wrong with "going procedural" to solve such requirements, or using a reporting tool that sits on top of the database. However, you should use SQL as far as possible to get the best performance from the database.