Creating an SQL query that eliminates duplicate months/year - sql

Hello Stack Overflow community - hopefully i'm on the right track with this one, but i'm trying to write a query where a report out shows the number of orders placed by month/year. The report currently brings up all the days where i'm trying to join them all by month/year collectively. Hopefully this makes sense, i'm pretty new to this, be gentle please ;)
select distinct month(o.orderdate) 'Month',
year(o.orderdate) 'Year', sum(od.Quantity) as Orders
from OrderDetails od
join Products p
on od.ProductID = p.ProductID
join Orders o
on od.OrderID = o.OrderID
group by o.orderdate
Order by year, month asc;

You need to group by what you want to define each row. In your case, that is the year and month:
select year(orderdate) as yyyy, month(o.orderdate) as mm,
sum(od.Quantity) as Orders
from OrderDetails od join
Products p
on od.ProductID = p.ProductID join
Orders o
on od.OrderID = o.OrderID
group by year(o.orderdate), month(o.orderdate)
Order by yyyy, mm asc;
Notes:
I changed the column names to yyyy and mm so they do not conflict with the reserved words year and month.
Don't use single quotes for column aliases. This is a bad habit that will eventually cause problems in your query.
I always use as for column aliases (to help prevent missing comma mistakes), but never for table aliases.
The product table is not needed for this query.
Edit: If you want a count of orders, which your query suggests, then this might be more appropriate:
select year(o.orderdate) as yyyy, month(o.o.orderdate) as mm,
count(*) as Orders
from orders o
group by year(o.orderdate), month(o.orderdate)
Order by yyyy, mm asc;

You have to group by month and year
select distinct month(o.orderdate) 'Month',
year(o.orderdate) 'Year', sum(od.Quantity) as Orders
from OrderDetails od
join Products p
on od.ProductID = p.ProductID
join Orders o
on od.OrderID = o.OrderID
group by month(o.orderdate), year(o.orderdate)
Order by [Year],[month]

Related

Challenging PostgreSQL SELECT Statement for Northwind Database (NEED HELP - BEGINNER)

I began learning SQL about a month ago and my dad has been giving me practice queries to run
with the Northwind database to help practice my DML. This most recent one he gave me was as follows:
-- Return Month, Product name, SalesForMonth for highest selling product in each
-- month in 1997. (see issues with creating said query below)
(I am currently using PostgreSQL on pgAdmin4)
I was able to come up with the following query that returns the required columns with the correct information for ONLY a single month:
SELECT CAST( EXTRACT( MONTH FROM o.orderdate) AS integer) AS Month, p.productname,
ROUND(CAST(SUM(od.quantity * od.unitprice) AS numeric), 2) SalesForMonth
FROM order_details od
INNER JOIN orders o ON od.orderid = o.orderid
INNER JOIN products p ON od.productid = p.productid
WHERE EXTRACT( YEAR FROM o.orderdate) = 1997 AND EXTRACT( MONTH FROM o.orderdate) = 1
GROUP BY Month, p.productname
ORDER BY salesformonth DESC
LIMIT 1
By making 12 of these queries and changing the extract-month bit in the WHERE statement from 1-12 and UNIONing them all together, I can produce the desired result but I wondered if there was an easier way that I was missing to display the same result but only using 1 query. Interested to see what y'all can come up with.
SIDE NOTE: My initial thought is that it has something to do with subqueries because what you're effectively trying to do is display the MAX(SUM(values)) but can't actually do that since you can't nest aggregate function.
Your query gives you the top selling product for a given month, and you want the same logic for multipe months at once.
Starting from your existing and working query, a simple approach is to use WITH TIES:
SELECT DATE_TRUNC('month', o.orderdate) DateMonth,
p.productname,
ROUND(CAST(SUM(od.quantity * od.unitprice) AS numeric), 2) SalesForMonth
FROM order_details od
INNER JOIN orders o ON od.orderid = o.orderid
INNER JOIN products p ON od.productid = p.productid
WHERE o.orderdate >= DATE '1997-01-01' AND o.orderdate < DATE '1998-01-01'
GROUP BY DateMonth, p.productname
ORDER BY RANK() OVER(
PARTITION BY DATE_TRUNC('month', o.orderdate)
ORDER BY SUM(od.quantity * od.unitprice) DESC
)
FETCH FIRST ROW WITH TIES
We can also use DISTINCT ON:
SELECT DISTINCT ON (DateMonth)
DATE_TRUNC('month', o.orderdate) DateMonth,
p.productname,
ROUND(CAST(SUM(od.quantity * od.unitprice) AS numeric), 2) SalesForMonth
FROM order_details od
INNER JOIN orders o ON od.orderid = o.orderid
INNER JOIN products p ON od.productid = p.productid
WHERE o.orderdate >= DATE '1997-01-01' AND o.orderdate < DATE '1998-01-01'
GROUP BY DateMonth, p.productname
ORDER BY DateMonth, SalesForMonth DESC

Find the most popular month for customers to order a certain product

I am trying to find out which month has the most orders for a certain product (Product HHYDP). This is my code so far, but each time I try to use GROUP BY and SORT BY functions related to my problem, I get an error. There are three years in the database (2006,2007,2008) and they are formatted as (YYYY-MM-DD). I am trying to find which month has the highest total order volume across the three years, quantity is irrelevant.
SELECT p.productname, o.orderdate
FROM [Sales].[Orders] as o
JOIN [Sales].[OrderDetails] as od
ON o.orderid = od.orderid
JOIN [Production].[Products] as p
ON od.productid = p.productid
WHERE p.productname like '%hhydp%'
I am using microsoft SQL management server.
You can try to use year and month with COUNT aggregate function function and add them in group by
SELECT p.productname,
year(o.orderdate) yr,
month(o.orderdate) mn,
COUNT(*) cnt
FROM [Sales].[Orders] as o
JOIN [Sales].[OrderDetails] as od
ON o.orderid = od.orderid
JOIN [Production].[Products] as p
ON od.productid = p.productid
WHERE p.productname like '%hhydp%'
GROUP BY p.productname, year(o.orderdate) ,month(o.orderdate)

Trying to sum part of the rows

So I have three tables
Orders:
CustomerID,OrderID
Order Details;
OrderID,ProductId,UnitPrice,Quantity,Discount
And Products:
ProductID,ProductName
And I need to combine these 2 tables and create this one:
[Orderd Details].CustomersID,Products.ProductName,FORMULA
Formula is how much money people spent money on this product. So I think I have to sum UnitPrice* Quantity*(1-Discount) from every order for this product.
Sadly I have no idea how should I do it. The best I did is:
SELECT o.CustomerID,p.ProductName,SUM(od.Quantity*od.UnitPrice*(1-od.Discount)) as 'SKZ'
FROM Customers as c, Orders as o,[Order Details] as od,Products as p
WHERE (o.OrderID=od.OrderID AND p.ProductID=od.ProductID)
GROUP BY od.ProductID ORDER BY o.CustomerID;
But it doesn't work.
First, learn explicit JOIN syntax. Simple rule: Never use commas in the FROM clause.
Second, you should include all non-aggregated columns in the GROUP BY:
SELECT o.CustomerID, p.ProductName,
SUM(od.Quantity * od.UnitPrice * (1 - od.Discount)) as SKZ
FROM Orders as o JOIN
[Order Details] od
ON o.OrderID = od.OrderID JOIN
Products p
ON p.ProductID = od.ProductID
GROUP BY o.CustomerID, p.ProductName
ORDER BY o.CustomerID;

Left join returning bad values

I'm not very good with SQL queries but I attempted to write this one:
SELECT DATEPART(YY,Orders.OrderDate) as Year,
DATEPART(MM,Orders.OrderDate) as Month,
(SUM(case when OrderDetails.ProductCode = 'XXX' then
OrderDetails.ProductPrice else 0 end) + SUM(Orders.Total))
AS XXX
FROM Orders
LEFT JOIN OrderDetails ON Orders.OrderID = OrderDetails.OrderID
WHERE Orders.OrderStatus = 'Shipped'
GROUP BY DATEPART(MM,Orders.OrderDate), DATEPART(YY,Orders.OrderDate)
ORDER BY DATEPART(YY,Orders.OrderDate),DATEPART(MM,Orders.OrderDate)
The OrderDetails is linked to the Orders table by the field OrderID. In this SELECT query I'm trying to get the SUM of OrderDetails.ProductPrice when the OrderDetails.ProductCode is XXX and add it to the Orders.Total to get total amounts for each month/year.
The query is working except for one problem (that's probably either a amateur mistake or has been worked around several times). When performing the LEFT JOIN, the OrderDetails table can have multiple records linked to the Orders table which is throwing bad results in the SUM(Orders.Total). I've isolated that issue I just can't seem to fix it.
Can anybody point me in the right direction?
If we assume that the XXX product only appears at most once for each order, then this should work:
SELECT year(o.OrderDate) as Year, month(o.OrderDate) as Month,
(COALESCE(SUM(od.ProductPrice), 0) + SUM(o.Total)) AS XXX
FROM Orders o LEFT JOIN
OrderDetails od
ON o.OrderID = od.OrderID AND od.ProductCode = 'XXX'
WHERE o.OrderStatus = 'Shipped'
GROUP BY year(o.OrderDate), month(o.OrderDate)
ORDER BY year(o.OrderDate), month(o.OrderDate);
If it can appear multiple times, then move that part of the aggregation to a subquery:
SELECT year(o.OrderDate) as Year, month(o.OrderDate) as Month,
(COALESCE(XXX, 0) + SUM(o.Total)) AS XXX
FROM Orders o LEFT JOIN
(SELECT od.OrderId, SUM(od.ProductPrice) as XXX
FROM OrderDetails od
WHERE od.ProductCode = 'XXX'
GROUP BY od.OrderId
) od
ON o.OrderID = od.OrderID
WHERE o.OrderStatus = 'Shipped'
GROUP BY year(o.OrderDate), month(o.OrderDate)
ORDER BY year(o.OrderDate), month(o.OrderDate);

How to group by within a table that doesn't have attributes that can be grouped on SQL

I have the following tables:
Orders
Products
OrderDetails as a Joint table.
I also have a Categories table that is connected to Products; CategoryID is a foreign table on Products.
I'm trying to get a table that contains Year of order between 1997, 1998, Quarter, Category Name starting with C letter and Sale(UDF). Here's what I tried:
Select YEAR(o.OrderDate) AS "Year", DATENAME(Quarter, o.OrderDate) AS "Qtr",
c.CategoryName,
dbo.SaleAfterDiscount(od.UnitPrice, od.Quantity, od.Discount) AS "Sale"
From Orders o, [Order Details] od, Categories c, Products p
WHERE (YEAR(o.OrderDate)='1997'OR YEAR(o.OrderDate)='1998')
AND c.CategoryName LIKE 'c%'
AND od.OrderID = o.OrderID
AND od.ProductID = p.ProductID
AND c.CategoryID = p.CategoryID
But I get a lot of results. How can I group them or fix the query to get the right answer?
Use explicit JOINs
Don't use a function on a column
GROUP BY all non-aggregated columns
Like this:
Select
YEAR(o.OrderDate) AS "Year",
DATENAME(Quarter, o.OrderDate) AS "Qtr",
c.CategoryName,
SUM(dbo.SaleAfterDiscount(od.UnitPrice, od.Quantity, od.Discount)) AS "SaleSum"
From
Orders o
JOIN
[Order Details] od ON od.OrderID=o.OrderID
JOIN
Products p ON od.ProductID=p.ProductID
JOIN
Categories c ON c.CategoryID=p.CategoryID
WHERE
o.OrderDate >= '19970101' AND o.OrderDate < '19990101'
AND
c.CategoryName LIKE 'c%'
GROUP BY
YEAR(o.OrderDate),
DATENAME(Quarter, o.OrderDate),
c.CategoryName;