Grouping or Counting after a Sum - sql

Using Oracle/SQL, I am looking for a way to count the number distinct IDs above or below a specified value following a sum. For example, I need to find how many customers have ordered $1,000 or more in goods last year, regardless of whether it was in one large order or several smaller orders. I don't need to identify each customer specifically, just find the total number of customers over this amount.
So far I am able to find the total of orders with:
select sum (Order_Amount), Customer_ID
from table.orders_placed
where year = 2013
group by Customer_ID
order by Customer_ID
I can also expand it doing this:
select count (dinstinct Customer_ID)
from(
select sum (Order_Amount), Customer_ID
from table.orders_placed
where year = 2013
group by Customer_ID
order by Customer_ID
)
but this just gives me the total number of distinct Customer_ID. Any other argument that I add to try to narrow what the "count" gives me results in an error. How can I specify that I want the total Order_Amount of $1,000 or more?

Try (no need to order by in inner query)
select count (dinstinct Customer_ID)
from(
select sum (Order_Amount) total_order_amount, Customer_ID
from table.orders_placed
where year = 2013
group by Customer_ID
) where total_order_amount > 1000
OR with Having
select sum (Order_Amount) total_order_amount, Customer_ID
from table.orders_placed
where year = 2013
group by Customer_ID
having sum(order_amount) > 1000

Use a HAVING clause to restrict the results of the grouping:
select sum (Order_Amount) as Total, Customer_ID
from table.orders_placed
where year = 2013
group by Customer_ID
having sum (Order_Amount) >= 1000
Then you can use this as a subquery to perform your aggregation and filtering.

Related

How select all data using HAVING clause in WHERE condition?

I have created a table which is keeps records of which product is sold by whom and how much each month;
month
total
product
cons_name
2020-01
10
xyz
123
2020-02
5
abc
456
2020-02
4
def
789
I was creating a query from this table to find out who has sold over 500 products on certain products since the beginning of the year, but I was a bit confused at the time of writing. Because i am not needed to query how much it sells during the year, but how much it sells each month that's i need to find. I can easily find more than 500 sales in total during a year with this query:
SELECT cons_name, product, SUM(total)
FROM TMP_REPORT
WHERE product IN ('abc','xyz')
GROUP BY cons_name, product
HAVING sum(total) > 500
But when it came to querying in detail I got this far:
SELECT
month,
product,
cons_name,
total
FROM TMP_REPORT
WHERE product IN ('abc','xyz')
AND cons_name IN
(SELECT
cons_name
FROM TMP_REPORT
WHERE
product IN ('abc','xyz')
GROUP BY
cons_name
HAVING sum(total) > 500)
The result of this query showed even the totals of sold product are not 500. For example, we would expect the cons_name named '123' to not be in the query result for only 200 sold 'abc' products in a year, but it does exist because of where clause. I knew my mistakes but I don't know that how to fix.
How can i do it?
Thanks for your help.
One approach uses SUM as an analytic function:
WITH cte AS (
SELECT t.*, SUM(total) OVER (PARTITION BY cons_name, product) sum_total
FROM TMP_REPORT t
WHERE product IN ('abc', 'xyz')
)
SELECT *
FROM cte
WHERE sum_total > 500;
I couldn't clearly understand you requirement, I assume you need a query to fetch the product that sold more than 500 unit on monthly basis. I hope below query will fetch the records you need.
-- YearlyReportCTE will Qualify the people who sold more than 500 units in total (i.e. yearly from your statement)
WITH YearlyReportCTE AS (
SELECT
product,
cons_name,
SUM(total) AS Total
FROM #TMP_REPORT
WHERE product in ('abc','xyz')
GROUP BY product,cons_name
HAVING SUM(total) > 500
)--This Query will fetch the month wise report from the qulified records
SELECT month,
TR.product,
TR.cons_name,
SUM(TR.total) AS Total
FROM #TMP_REPORT TR
JOIN YearlyReportCTE YR ON YR.cons_name = TR.cons_name
AND YR.product = TR.product
GROUP BY month,TR.product,TR.cons_name

How to write a SQL query to find out customers who have purchased at least two times every month of the year

I have a table with columns order_id, customer_id, order_date and Amount.
How to find the customers who have ordered at least 2 times each month of the year in SQL (from Jan to Dec)?
I think you are looking for something like this
select
order_date_year,
customer_id
from
(
select
year(order_date) as order_date_year,
month(order_date) as order_date_month,
customer_id,
count(*) as number_of_orders
from
tableName
group by
year(order_date),
month(order_date),
customer_id
having
count(*) >= 2
) as t
group by
order_date_year,
customer_id
having
count(*) = 12

Can a daily customer frequency filter be reduced to less than 2 subqueries when multiple timestamps exist for a given day and customer?

I am trying to figure out if there is a more efficient way to get a count of frequent customers. The tricky part is I want to filter the customers based on payments per day while removing secondary records that occur for a given customer on more than one day. The dataset includes records for customers on the same day but at different times. I only want to count 1 and only 1 payment per day.
For example, given the following values for (payment_id, customer_id, payment_date), I want a count 2
(17504, 341, '2007-02-16 17:23:14'),
(17505, 341, '2007-02-16 22:41:45'),
(17506, 341, '2007-02-19 19:39:56')
Once the records are grouped by customer and day, I want to filter on customers having more than 3 records and I want to return the count.
My current query is below. Is there another way to do this without so many nested subqueries?
SELECT (COUNT(*)) AS count_for_customers_with_more_than_3_visits
FROM (
SELECT customer_id
FROM (
SELECT customer_id, date_trunc('day', payment_date) AS day
FROM payments
GROUP BY customer_id, day
) visits_by_day
GROUP BY customer_id
HAVING COUNT(day) > 3
) sub;
I'm using Postgres v9.6
Data and query on SQL fiddle
This may not be more efficient, but it is shorter:
SELECT COUNT(*)) AS count_for_customers_with_more_than_3_visits
FROM (SELECT customer_id
FROM payments
GROUP BY customer_id
HAVING COUNT(DISTINCT date_trunc('day', payment_date)) > 3
) sub;

Sql Subquery Syntax

I want to execute a subquery using the current customer ID as I try to describe below
SELECT DISTINCT Customer_Id,
(SELECT SUM (total) FROM Orders where Customer_Id = Customer_Id AND CAST(Date) > DayIspecify )
FROM Orders where shop_id= '1-9THT'
What I want is to calculate the SUM each customer spent over a specified time period on the specific shop.
SELECT Customer_Id, SUM(total) SumTotal
FROM Orders
where shop_id= '1-9THT'
group by Customer_id
Not require subquery
Try this:
SELECT Customer_Id,SUM(total)FROM Orders WHERE shop_id='1-9THT' GROUP BY Customer_Id
(Updated) Try:
select Customer_Id,
sum(case when o.shop_id = '1-9THT' and Date > DayIspecify
then total else 0 end) total
from Orders
group by Customer_Id
- to return all customers recorded on the Orders table, together with the values of any of their orders placed through shop 1-9THT after the date specified. (Change > to >= to make it on or after the date specified.)
Use SQL GroupBy
SELECT DISTINCT Customer_Id, SUM (total) FROM Orders where shop_id= '1-9THT' group by customer_Id

Can I limit the amount of rows to be used for a group in a GROUP BY statement

I'm having an odd problem
I have a table with the columns product_id, sales and day
Not all products have sales every day. I'd like to get the average number of sales that each product had in the last 10 days where it had sales
Usually I'd get the average like this
SELECT product_id, AVG(sales)
FROM table
GROUP BY product_id
Is there a way to limit the amount of rows to be taken into consideration for each product?
I'm afraid it's not possible but I wanted to check if someone has an idea
Update to clarify:
Product may be sold on days 1,3,5,10,15,17,20.
Since I don't want to get an the average of all days but only the average of the days where the product did actually get sold doing something like
SELECT product_id, AVG(sales)
FROM table
WHERE day > '01/01/2009'
GROUP BY product_id
won't work
If you want the last 10 calendar day since products had a sale:
SELECT product_id, AVG(sales)
FROM table t
JOIN (
SELECT product_id, MAX(sales_date) as max_sales_date
FROM table
GROUP BY product_id
) t_max ON t.product_id = t_max.product_id
AND DATEDIFF(day, t.sales_date, t_max.max_sales_date) < 10
GROUP BY product_id;
The date difference is SQL server specific, you'd have to replace it with your server syntax for date difference functions.
To get the last 10 days when the product had any sale:
SELECT product_id, AVG(sales)
FROM (
SELECT product_id, sales, DENSE_RANK() OVER
(PARTITION BY product_id ORDER BY sales_date DESC) AS rn
FROM Table
) As t_rn
WHERE rn <= 10
GROUP BY product_id;
This asumes sales_date is a date, not a datetime. You'd have to extract the date part if the field is datetime.
And finaly a windowing function free version:
SELECT product_id, AVG(sales)
FROM Table t
WHERE sales_date IN (
SELECT TOP(10) sales_date
FROM Table s
WHERE t.product_id = s.product_id
ORDER BY sales_date DESC)
GROUP BY product_id;
Again, sales_date is asumed to be date, not datetime. Use other limiting syntax if TOP is not suported by your server.
Give this a whirl. The sub-query selects the last ten days of a product where there was a sale, the outer query does the aggregation.
SELECT t1.product_id, SUM(t1.sales) / COUNT(t1.*)
FROM table t1
INNER JOIN (
SELECT TOP 10 day, Product_ID
FROM table t2
WHERE (t2.product_ID=t1.Product_ID)
ORDER BY DAY DESC
)
ON (t2.day=t1.day)
GROUP BY t1.product_id
BTW: This approach uses a correlated subquery, which may not be very performant, but it should work in theory.
I'm not sure if I get it right but If you'd like to get the average of sales for last 10 days for you products you can do as follows :
SELECT Product_Id,Sum(Sales)/Count(*) FROM (SELECT ProductId,Sales FROM Table WHERE SaleDAte>=#Date) table GROUP BY Product_id HAVING Count(*)>0
OR You can use AVG Aggregate function which is easier :
SELECT Product_Id,AVG(Sales) FROM (SELECT ProductId,Sales FROM Table WHERE SaleDAte>=#Date) table GROUP BY Product_id
Updated
Now I got what you meant ,As far as I know it is not possible to do this in one query.It could be possible if we could do something like this(Northwind database):
select a.CustomerId,count(a.OrderId)
from Orders a INNER JOIN(SELECT CustomerId,OrderDate FROM Orders Order By OrderDate) AS b ON a.CustomerId=b.CustomerId GROUP BY a.CustomerId Having count(a.OrderId)<10
but you can't use order by in subqueries unless you use TOP which is not suitable for this case.But maybe you can do it as follows:
SELECT PorductId,Sales INTO #temp FROM table Order By Day
select a.ProductId,Sum(a.Sales) /Count(a.Sales)
from table a INNER JOIN #temp AS b ON a.ProductId=b.ProductId GROUP BY a.ProductId Having count(a.Sales)<=10
If this is a table of sales transactions, then there should not be any rows in there for days on which there were no Sales. I.e., If ProductId 21 had no sales on 1 June, then this table should not have any rows with productId = 21 and day = '1 June'... Therefore you should not have to filter anything out - there should not be anything to filter out
Select ProductId, Avg(Sales) AvgSales
From Table
Group By ProductId
should work fine. So if it's not, then you have not explained the problem completely or accurately.
Also, in yr question, you show Avg(Sales) in the example SQL query but then in the text you mention "average number of sales that each product ... " Do you want the average sales amount, or the average count of sales transactions? And do you want this average by Product alone (i.e., one output value reported for each product) or do you want the average per product per day ?
If you want the average per product alone, for just thpse sales in the ten days prior to now? or the ten days prior to the date of the last sale for each product?
If the latter then
Select ProductId, Avg(Sales) AvgSales
From Table T
Where day > (Select Max(Day) - 10
From Table
Where ProductId = T.ProductID)
Group By ProductId
If you want the average per product alone, for just those sales in the ten days with sales prior to the date of the last sale for each product, then
Select ProductId, Avg(Sales) AvgSales
From Table T
Where (Select Count(Distinct day) From Table
Where ProductId = T.ProductID
And Day > T.Day) <= 10
Group By ProductId