SQL Server : Counts/Group By - sql

I have an orders table that I need to generate a report from that groups by month/year, showing the total orders for the month, and the number actually received.
This gives me total # of orders by month/year:
SELECT YEAR(o.OrderDate) As 'Year'
, MONTH(o.OrderDate) As 'Month'
, COUNT(ID) As 'OrderCount'
FROM Orders o
GROUP BY YEAR(o.OrderDate), MONTH (o.OrderDate)
ORDER BY YEAR(o.OrderDate) DESC, MONTH (o.OrderDate) DESC
This gives me total # of orders RECEIVED by month/year:
SELECT YEAR(o.OrderDate) As 'Year'
, MONTH(o.OrderDate) As 'Month'
, COUNT(o.ID) As 'OrderReceivedCount'
FROM Orders o
INNER JOIN OrderStatus s ON s.ID = o.CurrentStatus
WHERE s.StatusPriority > 4 AND s.IsCancelled = 0
GROUP BY YEAR(o.OrderDate), MONTH (o.OrderDate)
ORDER BY YEAR(o.OrderDate) DESC, MONTH (o.OrderDate) DESC
But how can I combine that into one query? I can't work out how the 'group by' would work?

I think you want something like:
SELECT YEAR(o.OrderDate) As [Year]
, MONTH(o.OrderDate) As [Month]
, COUNT(*) As OrderCount
, SUM(CASE
WHEN s.StatusPriority > 4 AND
s.IsCancelled = 0 THEN 1
ELSE 0 END) as OrderReceivedCount
FROM Orders o
INNER JOIN OrderStatus s ON s.ID = o.CurrentStatus
GROUP BY YEAR(o.OrderDate), MONTH (o.OrderDate)
ORDER BY YEAR(o.OrderDate) DESC, MONTH (o.OrderDate) DESC
That is join all of the orders to their statuses, whatever they may be, and then do a conditional count of those (via SUM/CASE) to work out which ones have been received.

Related

How to get specific SQL counts for each year?

I am trying to get specific counts of bike sales per year, but now I'm only getting a total count of sales over the 5 year period and not broken up by specific years.
--CURRENT CODE:
Select datepart(year, C.TransactionDate) AS SalesYear,
SUM(COUNT(B.SerialNumber)) OVER() AS CountOfSerialNumber
FROM BIKE..Bicycle AS B
INNER JOIN BIKE..CustomerTransaction AS C on B.CustomerID = C.CustomerID
WHERE ModelType = 'Mountain' AND
TransactionDate BETWEEN '20000101' AND '20041231'
GROUP BY datepart(year, C.TransactionDate)
ORDER BY datepart(year, C.TransactionDate) ASC
--And my resulting output reads:
SalesYear | CountOfSerialNumber
2000 9431
2001 9431
2002 9431
2003 9431
2004 9431
You should be able to get the count without using SUM or OVER. OVER can be used to get a percentage of the total, if that's something you wanted to do.
SELECT DATEPART(YEAR, C.TransactionDate) AS SalesYear, COUNT(B.SerialNumber) AS Quantity,
COUNT(B.SerialNumber) * 100.0 / SUM(COUNT(B.SerialNumber)) OVER() AS Percentage
FROM BIKE..Bicycle AS B
INNER JOIN BIKE..CustomerTransaction AS C ON C.CustomerID = B.CustomerID
WHERE B.ModelType = 'Mountain' AND C.TransactionDate BETWEEN '20000101' AND '20041231'
GROUP BY DATEPART(YEAR, C.TransactionDate)
ORDER BY DATEPART(YEAR, C.TransactionDate) ASC

ERROR: missing FROM-clause entry for table "p"

I am trying to use a UNION to append the total column in the first top half to the second half of the query.
SELECT NULL as month, NULL as active, count(cage_player_id) as total
FROM player_signup as p
GROUP BY date_part('month', p.signup_date)
UNION
SELECT date_part('month', signup_date) as month, count(DISTINCT(p.cage_player_id)) as active, NULL as total
FROM player_signup as p
JOIN daily_kpis as d ON p.cage_player_id = d.cage_player_id
WHERE slot_bet_amount > 0
OR ld_bet_amount > 0
OR table_bet_amount > 0
GROUP BY date_part('month', p.signup_date)
ORDER BY date_part('month', p.signup_date) ASC
I keep getting an error that says the FROM clause is missing for table p. Can anyone help? Is there an easier way to combine these two queries?
Here is what each query looks like separately. I just want to add the total column right next to the month and active column. The total ID's would still be broken down by months.
Query 1
Query 2
The problem is the order by clause. It applies to the results of the union query, so it cannot see the identifiers that are defined within the queries. Instead, you should use the names of the column in the resultset.
So you want to change this:
ORDER BY date_part('month', p.signup_date) ASC
To:
ORDER BY month
I believe this is the query you need:
SELECT date_part('month', signup_date) as month
, count(DISTINCT(p.cage_player_id)) as active
, (select count(cage_player_id)
FROM player_signup as p
GROUP BY date_part('month', p.signup_date)) as total
FROM player_signup as p
JOIN daily_kpis as d ON p.cage_player_id = d.cage_player_id
WHERE slot_bet_amount > 0
OR ld_bet_amount > 0
OR table_bet_amount > 0
GROUP BY date_part('month', p.signup_date)
ORDER BY date_part('month', p.signup_date) asc
I am not so good at Postgresql, please try this:
SELECT date_part('month', signup_date) as month
, count(DISTINCT(p.cage_player_id)) as active
, max(t1.total) as total
FROM player_signup as p
left join (select count(pp.cage_player_id) over (partition by date_part('month', pp.signup_date)) as total
, date_part('month', pp.signup_date) date_p
FROM player_signup as pp) t1 on t1.date_p = date_part('month', p.signup_date)
JOIN daily_kpis as d ON p.cage_player_id = d.cage_player_id
WHERE slot_bet_amount > 0
OR ld_bet_amount > 0
OR table_bet_amount > 0
GROUP BY date_part('month', p.signup_date)
ORDER BY date_part('month', p.signup_date)

Postgresql - can you do this without a CTE?

I wanted to get the number of orders and money spent by customers in the first 7 days from their initial order. I managed to do it with a common table expression, but was curious to see if someone could point out to an obvious update to the main query's WHERE or HAVING section, or perhaps a subquery.
--This is a temp table to use in the main query
WITH first_seven AS
(
select min(o.created_at), min(o.created_at) + INTERVAL '7 day' as max_order_date, o.user_id
from orders o
where o.total_price > 0 and o.status = 30
group by o.user_id
having min(o.created_at) > '2015-09-01'
)
--This is the main query, find orders in first 7 days of purchasing
SELECT sum(o.total_price) as sales, count(distinct o.objectid) as orders, o.user_id, min(o.created_at) as first_order
from orders o, first_seven f7
where o.user_id = f7.user_id and o.created_at < f7.max_order_date and o.total_price > 0 and o.status = 30
group by o.user_id
having min(o.created_at) > '2015-09-01'
You can do this without the join by using window functions:
select sum(o.total_price) as sales, count(distinct o.objectid) as orders,
o.user_id, min(o.created_at) as first_order
from (select o.*,
min(o.created_at) over (partition by user_id) as startdate
from orders o
where o.total_price > 0 and o.status = 30
) o
where startdate > '2015-09-01' and
created_at <= startdate + INTERVAL '7 day';
A more complicated query (with the right indexes) is probably more efficient:
select sum(o.total_price) as sales, count(distinct o.objectid) as orders,
o.user_id, min(o.created_at) as first_order
from (select o.*,
min(o.created_at) over (partition by user_id) as startdate
from orders o
where o.total_price > 0 and o.status = 30 and
not exists (select 1 from orders o2 where o2.user_id = o.user_id and created_at <= '2015-09-01')
) o
where startdate > '2015-09-01' and
created_at <= startdate + INTERVAL '7 day';
This filters out older customers before the windows calculation, which should make it more efficient. Indexes that are useful are orders(user_id, created_at) and orders(status, total_price).

Using Subquery And Ordering The Value By Date

I'm Trying to Get the date , Discount , Total , Net Total ... ordered by date , the discount is showing the real amount but when I select multiple dates the total will be summed in those dates I've selected and it will be ordered by date
DECLARE #pR FLOAT = (SELECT SUM(CAST(Price AS FLOAT)) AS Price
FROM Orders WHERE isPaid = 1
AND PaidDate BETWEEN '8/17/2015' AND '8/18/2015' ) ;
SELECT Orders.PaidDate
, #pR AS Total
,sum(theorderids.Discount) As Discount
,(#pR - sum(theorderids.Discount)) AS [Net Total]
From
(SELECT OrderId, PaidDate
FROM Orders
WHERE Orders.PaidDate BETWEEN '8/17/2015' AND'8/18/2015'
GROUP BY Orders.OrderId, Orders.PaidDate) AS Orders
INNER JOIN theorderids ON Orders.OrderId = theorderids.ID
GROUP BY Orders.PaidDate ;
Example Data :
Row 1
"PaidDate": "17-08-2015",
"Total": 7388.0,
"Discount": 38.0,
"NetTotal": 7363.0
Row 2
"PaidDate": "18-08-2015",
"Total": 7388.0,
"Discount": 2.0,
"NetTotal": 7363.0
This will work.
SELECT TheOrderids.PaidDate, MAX(Price) AS Total
,sum(theorderids.Discount) As Discount
,(MAX(Price) - sum(theorderids.Discount)) AS [Net Total]
From
(SELECT PaidDate ,SUM(Price ) AS Price
FROM Orders
WHERE Orders.PaidDate BETWEEN '8/17/2015' AND'8/19/2015'
GROUP BY Orders.PaidDate
) AS Orders
INNER JOIN theorderids ON Orders.PaidDate = theorderids.PaidDate
GROUP BY theorderids.PaidDate
ORDER BY theorderids.PaidDate ;
Try this way
SELECT Orders.PaidDate
, #pR AS Total
,sum(theorderids.Discount) As Discount
,(#pR - sum(theorderids.Discount)) AS [Net Total]
From
(SELECT ROW_NUMBER() OVER(ORDER BY Orders.PaidDate ) AS Row, OrderId, PaidDate
FROM Orders
WHERE Orders.PaidDate BETWEEN '8/17/2015' AND'8/18/2015'
GROUP BY Orders.OrderId, Orders.PaidDate) AS Orders
INNER JOIN theorderids ON Orders.OrderId = theorderids.ID
GROUP BY Orders.PaidDate ;

MySQL: Returning multiple columns from an in-line subquery

I'm creating an SQL statement that will return a month by month summary on sales.
The summary will list some simple columns for the date, total number of sales and the total value of sales.
However, in addition to these columns, i'd like to include 3 more that will list the months best customer by amount spent. For these columns, I need some kind of inline subquery that can return their ID, Name and the Amount they spent.
My current effort uses an inline SELECT statement, however, from my knowledge on how to implement these, you can only return one column and row per in-line statement.
To get around this with my scenario, I can of course create 3 separate in-line statements, however, besides this seeming impractical, it increases the query time more that necessary.
SELECT
DATE_FORMAT(OrderDate,'%M %Y') AS OrderMonth,
COUNT(OrderID) AS TotalOrders,
SUM(OrderTotal) AS TotalAmount,
(SELECT SUM(OrderTotal) FROM Orders WHERE DATE_FORMAT(OrderDate,'%M %Y') = OrderMonth GROUP BY OrderCustomerFK ORDER BY SUM(OrderTotal) DESC LIMIT 1) AS TotalCustomerAmount,
(SELECT OrderCustomerFK FROM Orders WHERE DATE_FORMAT(OrderDate,'%M %Y') = OrderMonth GROUP BY OrderCustomerFK ORDER BY SUM(OrderTotal) DESC LIMIT 1) AS CustomerID,
(SELECT CustomerName FROM Orders INNER JOIN Customers ON OrderCustomerFK = CustomerID WHERE DATE_FORMAT(OrderDate,'%M %Y') = OrderMonth GROUP BY OrderCustomerFK ORDER BY SUM(OrderTotal) DESC LIMIT 1) AS CustomerName
FROM Orders
GROUP BY DATE_FORMAT(OrderDate,'%m%y')
ORDER BY DATE_FORMAT(OrderDate,'%y%m') DESC
How can i better structure this query?
FULL ANSWER
After some tweaking of Dave Barkers solution, I have a final version for anyone in the future looking for help.
The solution by Dave Barker worked perfectly with the customer details, however, it made the simpler Total Sales and Total Sale Amount columns get some crazy figures.
SELECT
Y.OrderMonth, Y.TotalOrders, Y.TotalAmount,
Z.OrdCustFK, Z.CustCompany, Z.CustOrdTotal, Z.CustSalesTotal
FROM
(SELECT
OrdDate,
DATE_FORMAT(OrdDate,'%M %Y') AS OrderMonth,
COUNT(OrderID) AS TotalOrders,
SUM(OrdGrandTotal) AS TotalAmount
FROM Orders
WHERE OrdConfirmed = 1
GROUP BY DATE_FORMAT(OrdDate,'%m%y')
ORDER BY DATE_FORMAT(OrdDate,'%Y%m') DESC)
Y INNER JOIN
(SELECT
DATE_FORMAT(OrdDate,'%M %Y') AS CustMonth,
OrdCustFK,
CustCompany,
COUNT(OrderID) AS CustOrdTotal,
SUM(OrdGrandTotal) AS CustSalesTotal
FROM Orders INNER JOIN CustomerDetails ON OrdCustFK = CustomerID
WHERE OrdConfirmed = 1
GROUP BY DATE_FORMAT(OrdDate,'%m%y'), OrdCustFK
ORDER BY SUM(OrdGrandTotal) DESC)
Z ON Z.CustMonth = Y.OrderMonth
GROUP BY DATE_FORMAT(OrdDate,'%Y%m')
ORDER BY DATE_FORMAT(OrdDate,'%Y%m') DESC
Move the inline SQL to be a inner join query. So you'd have something like...
SELECT DATE_FORMAT(OrderDate,'%M %Y') AS OrderMonth, COUNT(OrderID) AS TotalOrders, SUM(OrderTotal) AS TotalAmount, Z.OrderCustomerFK, Z.CustomerName, z.OrderTotal as CustomerTotal
FROM Orders
INNER JOIN (SELECT DATE_FORMAT(OrderDate,'%M %Y') as Mon, OrderCustomerFK, CustomerName, SUM(OrderTotal) as OrderTotal
FROM Orders
GROUP BY DATE_FORMAT(OrderDate,'%M %Y'), OrderCustomerFK, CustomerName ORDER BY SUM(OrderTotal) DESC LIMIT 1) Z
ON Z.Mon = DATE_FORMAT(OrderDate,'%M %Y')
GROUP BY DATE_FORMAT(OrderDate,'%m%y'), Z.OrderCustomerFK, Z.CustomerName
ORDER BY DATE_FORMAT(OrderDate,'%y%m') DESC
You can also do something like:
SELECT
a.`y`,
( SELECT #c:=NULL ) AS `temp`,
( SELECT #d:=NULL ) AS `temp`,
( SELECT
CONCAT(#c:=b.`c`, #d:=b.`d`)
FROM `b`
ORDER BY b.`uid`
LIMIT 1 ) AS `temp`,
#c as c,
#d as d
FROM `a`
Give this a shot:
SELECT CONCAT(o.order_month, ' ', o.order_year),
o.total_orders,
o.total_amount,
x.sum_order_total,
x.ordercustomerfk,
x.customername
FROM (SELECT MONTH(t.orderdate) AS order_month,
YEAR(t.orderdate) AS order_year
COUNT(t.orderid) AS total_orders,
SUM(t.ordertotal) AS total_amount
FROM ORDERS t
GROUP BY MONTH(t.orderdate), YEAR(t.orderdate)) o
JOIN (SELECT MONTH(t.orderdate) AS ordermonth,
YEAR(t.orderdate) AS orderyear
SUM(t.ordertotal) 'sum_order_total',
t.ordercustomerfk,
c.customername
FROM ORDERS t
JOIN CUSTOMERS c ON c.customerid = o.ordercustomerfk
GROUP BY t.ordercustomerfk, MONTH(t.orderdate), YEAR(t.orderdate)) x ON x.order_month = o.order_month
AND x.order_year = o.order_year
ORDER BY o.order_year DESC, o.order_month DESC