Displaying results for fixed values in SQL - sql

I am having difficulty in solving the below problem:
I have a table which contains the shopid, date, hour, category and sales amount.
shopid date hour category amount
------------------------------------
1 date1 7 food 10
1 date1 8 food 15
1 date1 10 misc. 5
2 date1 7 food 6
...................................
I am trying to calculate the total sales amount in each hour by food category and display like the following:
shopid category hour amount
------------------------------------
1 food 6 0
1 food 7 5
1 food 8 20
2 food 9 40
...................................
The shops' opening hours are 6 am -10 pm. So for each hour, there might be any sales or not. I was able to perform the hourly summation. But I am unable to display zero and the time when there are no sales at a particular time (e.g. 6 am or any other time between the opening hours) for each sale category.

Use a left join against a list of hours:
select t.shopid, t.category. g.hour, sum(t.amount)
from generate_series(6,22) as g(hour)
left join the_table t on t.hour = g.hour
group by t.shopid, t.category, g.hour
order by t.shopid, t.category, g.hour;

I am trying to calculate the total sales amount in each hour by food category.
This makes sense, but it doesn't make sense to include the shopid in the results.
To do this, you need to generate the rows -- which are all hours and food categories. Then bring in the actual results using left join:
select c.category. g.hour, coalesce(sum(s.amount), 0)
from generate_series(6, 22) g(hour) cross join
(select distinct category from sales) c left join
sales s
on s.hour = g.hour and s.category = c.category
group by c.category, g.hour
order by c.category, g.hour;
If you want results by shop/category/hour, then you can use the same idea:
select sh.shopid, c.category. g.hour,
coalesce(sum(s.amount), 0)
from generate_series(6, 22) g(hour) cross join
(select distinct category from sales) c cross join
(select distinct shopid from sales) sh left join
sales s
on s.shopid = sh.shopid and
s.hour = g.hour and
s.category = c.category
group by sh.shopid, c.category, g.hour
order by sh.shopid, c.category, g.hour;

Related

SQL: How to return revenue for specific year

I would like to show the revenue for a specific year for all customers regardless of whether or not they have revenue data for the specific year. (in cases they dont have data for the specific year, a filler like 'no data' would work)
Sample Data looks like:
Table 1
Customer
Price
Quantity
Order Date
xxx
12
5
1990/03/25
yyy
15
7
1991/05/35
xxx
34
2
1990/08/21
Desired Output would look a little something like this:
Customer
Revenue (for 1990)
xxx
128
yyy
no data
Getting the total revenue for each would be:
SELECT Customer,
SUM(quantity*price) AS Revenue
but how would i go about listing it out for a specific year for all customers? (incl. customers that dont have data for that specific year)
We can use a CTE or a sub-query to create a list of all customers and another to get all years and the cross join them and left join onto revenue.
This gives an row for each customer for each year. If you add where y= you will only get the year requested.
CREATE TABLE revenue(
Customer varchar(10),
Price int,
Quantity int,
OrderDate date);
insert into revenue values
('xxx', 12,5,'2021-03-25'),
('yyy', 15,7,'2021-05-15'),
('xxx', 34,2,'2022-08-21');
with cust as
(select distinct customer c from revenue),
years as
(select distinct year(OrderDate) y from revenue)
select
y "year",
c customer ,
sum(price*quantity) revenue
from years
cross join cust
left join revenue r
on cust.c = r.customer and years.y = year(OrderDate)
group by
c,y,
year(OrderDate)
order by y,c
year | customer | revenue
---: | :------- | ------:
2021 | xxx | 60
2021 | yyy | 105
2022 | xxx | 68
2022 | yyy | null
db<>fiddle here
You would simply use group by and do the sum in a subquery and left join it to your customers table. ie:
select customers.Name, totals.Revenue
from Customers
Left join
( select customerId, sum(quantity*price) as revenue
from myTable
where year(orderDate) = 1990
group by customer) totals on customers.CustomerId = myTable.customerId;

Select count & sum from order table, as well as count & sum from event table where order_id matches & event = expiry

I have 2 tables, one containing Order information and one containing Order Event information, example structure below:
Orders Table:
merchant_id
order_id
amount
order_date
111111
123456
100
2021-07-01
111111
789012
50
2021-07-20
111111
642443
75
2021-08-12
Events Table:
merchant_id
order_id
event
amount
date
111111
789012
EXPIRY
50
2021-08-03
111111
642443
EXPIRY
75
2021-08-28
Desired Output:
I am trying to get a breakdown by Merchant Id and month of:
Order Count
Order Sum
Expiry Count (how many of the orders placed in that month have expired regardless of date expired)
Expiry Sum (value of the expiry count above)
Example Output:
merchant_id
order_month
order_count
order_sum
expiry_count
expiry_sum
111111
7
3
150
1
50
111111
8
1
75
1
50
I have tried a few queries with no luck, the furthest I've gotten is:
select o.merchant_id, extract(month from o.order_date) as order_month, count(o.order_id) as order_count, sum(o.order_amount) as order_sum, count(e.order_id) as expiry_count, sum(e.amount) as expiry_sum
from orders o
left join events e on e.order_id = o.order_id
where o.merchant_id = '111111'
and o.order_date >= '2021-07-01'
group by o.merchant_id, order_month
order by o.merchant_id, order_month
However that outputs the exact same values for order_count & expiry_count, as well as order_sum & expiry_sum. Additionally I need to only retrieve events where event = 'EXPIRY' however I get no results when I add that filter.
Any help would be much appreciated!
Add the condition on event to the join (not the where):
select o.merchant_id, extract(month from o.order_date) as order_month, count(o.order_id) as order_count, sum(o.order_amount) as order_sum, count(e.order_id) as expiry_count, sum(e.amount) as expiry_sum
from orders o
left join events e on e.order_id = o.order_id
and e.event = 'EXPIRY'
where o.merchant_id = '111111'
and o.order_date >= '2021-07-01'
group by o.merchant_id, order_month
order by o.merchant_id, order_month
If you put a condition on an outer joined table in the where clause, you force the join to behave as an inner join (as if you deleted the left keyword).

how to calculate Sum for two different table with different containing table field

I have two table "tbl_In_Details" and "tbl_Out_Details"
Sample data for tbl_In_Details
ID Item_Name Rate Quantity Source_company
1 wire 10 4 2020-04-21 22:47:29.083
2 Tea 4 20 2020-04-21 22:47:52.823
Sample data for tbl_Out_Details
ID Item_Name Quantity Created_Date
1 wire 1 2020-04-21 22:48:48.233
2 wire 2 2020-04-21 22:50:16.367
3 Tea 2 2020-04-21 23:48:39.943
Now i want to calculate SUM of price and price (RATE*QUANTITY) from out going table
i tried in such a way but not getting result
select o.Item_Name, SUM(o.quantity) as Total_quantity ,SUM(o.quantity * i.Rate) as Expenses
from tbl_In_Details i inner join tbl_Out_Details o
ON i.Item_Name = o.Item_Name group by o.Item_Name,o.quantity, i.Rate
My Output should be
Item_Name Total_quantity Expenses
Tea 2 8
wire 3 30
Your query is completely fine. The only thing you need to get the desired result is to delete grouping by 'quantity' and 'rate'.
select o.Item_Name, SUM(o.quantity) as Total_quantity ,SUM(o.quantity * i.Rate) as Expenses
from tbl_In_Details i inner join tbl_Out_Details o
ON i.Item_Name = o.Item_Name group by o.Item_Name;
One method uses union all and group by:
Aggregate before joining:
select i.item_name, o.quantity, i.expenses
from (select id, item_name, sum(rate*quantity) as expenses
from tbl_In_Details
group by id, item_name
) i join
(select id, item_name, sum(quantity) as quantity
from tbl_Out_Details
group by id, item_name
) o
on i.id = o.id

pgsql -Showing top 10 products's sales and other products as 'others' and its sum of sales

I have a table called "products" where it has 100 records with sales details. My requirement is so simple that I was not able to do it.
I need to show the top 10 product names with sales and other product names as "others" and its sales. so totally my o/p will be 11 rows. 11-th row should be others and sum of sales of all remaining products. Can anyone give me the logic?
O/p should be like this,
Name sales
------ -----
1 colgate 9000
2 pepsodent 8000
3 closeup 7000
4 brittal 6000
5 ariies 5000
6 babool 4000
7 imami 3000
8 nepolop 2500
9 lactoteeth 2000
10 menwhite 1500
11 Others 6000 (sum of sales of remaining 90 products)
here is my sql query,
select case when rank<11 then prod_cat else 'Others' END as prod_cat,
total_sales,ID,rank from (select ROW_NUMBER() over (order by (sum(i.grandtotal)) desc) as rank,pc.name as prod_cat,sum(i.grandtotal) as total_sales, pc.m_product_category_id as ID`enter code here`
from adempiere.c_invoice i join adempiere.c_invoiceline il on il.c_invoice_id=i.c_invoice_id join adempiere.m_product p on p.m_product_id=il.m_product_id join adempiere.m_product_category pc on pc.m_product_category_id=p.m_product_category_id
where extract(year from i.dateacct)=extract(year from now())
group by pc.m_product_category_id) innersql
order by total_sales desc
o/p what i got is,
prod_cat total_sales id rank
-------- ----------- --- ----
BSHIRT 4511697.63 460000015 1
BT-SHIRT 2725167.03 460000016 2
SHIRT 2630471.56 1000003 3
BJEAN 1793514.07 460000005 4
JEAN 1115402.90 1000004 5
GT-SHIRT 1079596.33 460000062 6
T SHIRT 446238.60 1000006 7
PANT 405189.00 1000005 8
GDRESS 396789.02 460000059 9
BTROUSER 393739.48 460000017 10
Others 164849.41 1000009 11
Others 156677.00 1000008 12
Others 146678.00 1000007 13
As #e4c5 suggests, use UNION:
select id, prod_cat, sum(total_sales) as total_sales
with
totals as (
select --pc.m_product_category_id as id,
pc.name as prod_cat,
sum(i.grandtotal) as total_sales,
ROW_NUMBER() over (order by sum(i.grandtotal) desc) as rank
from adempiere.c_invoice i
join adempiere.c_invoiceline il on (il.c_invoice_id=i.c_invoice_id)
join adempiere.m_product p on (p.m_product_id=il.m_product_id)
join adempiere.m_product_category pc on (pc.m_product_category_id=p.m_product_category_id)
where i.dateacct >= date_trunc('year', now()) and i.dateacct < date_trunc('year', now()) + interval '1' year
group by pc.m_product_category_id, pc.name
),
rankedothers as (
select prod_cat, total_sales, rank
from totals where rank <= 10
union
select 'Others', sum(total_sales), 11
from totals where rank > 10
)
select prod_cat, total_sales
from ranked_others
order by rank
Also, I recommend using sargable conditions like the one above, which is slightly more complicated than the one you implemented, but generally worth the extra effort.

Build customers report of last years with T-SQL

I've 3 tables (simplified):
-----------Orders--------------------
Id | Total_Price | Customer_Id | Date
--------Order Details---------------------
Id | Order_Id | Product Name | Qty | Value
----Customers------
Id | Name | Address
I take a total order value of single customer with this query:
SELECT C.ID, C.NAME , SUM(O.TOTAL_PRICE)
FROM CUSTOMERS C
JOIN ORDERS O ON O.CUSTOMER_ID = C.ID
GROUP BY C.ID, C.NAME
Now, I want to build a report with total order value filtered by a range of dates:
SELECT C.ID, C.NAME , SUM(O.TOTAL_PRICE)
FROM CUSTOMERS C
JOIN ORDERS O ON O.CUSTOMER_ID = C.ID
WHERE O.DATE BETWEEN #value1 AND #value2
GROUP BY C.ID, C.NAME
this works OK, but I want to select last 3 year sums of total orders value grouped by customer, this is the results that I want:
1Year | 2Year | 3Year | Customer_Name
-------------------------------------------------
XXX | YYY | ZZZZ | Customer1
XYX | YYZ | ZZTZ | Customer2
....
I've this cardinality:
Customer table with 22.000 rows
Orders table with 87.000 rows
Orders details with 600.000
It is possible without temptable,vartable or stored procedure with long execution time?
In my report I want also to calculate total Qty of last 3 years grouped by customer of a product, but this is the next step.
Any ideas?
Thanks
You can use a case statement to get the result you want. Since there is some ambiguity in your post about how the year ranges are defined, I've left out any calculations to get those year end/starts and just put variables in. You can revise to suit your need.
SELECT C.ID
,C.NAME
,SUM(CASE
WHEN o.DATE BETWEEN #year1start
AND #year1end
THEN O.TOTAL_PRICE
ELSE 0
END) Year1
,SUM(CASE
WHEN o.DATE BETWEEN #year2start
AND #year2end
THEN O.TOTAL_PRICE
ELSE 0
END) Year2
,SUM(CASE
WHEN o.DATE BETWEEN #year3start
AND #year3end
THEN O.TOTAL_PRICE
ELSE 0
END) Year3
FROM CUSTOMERS C
INNER JOIN ORDERS O ON O.CUSTOMER_ID = C.ID
GROUP BY C.ID
,C.NAME
Another option is to use pivot statement. I assume every your date range equals to one year (e.g. 2013, 2014 and so on).
If these years are strongly determined pivot isn't very beautiful option (look at full sqlfiddle example, it has possible solution for your additional question):
select
c.Id, c.Name, c.Address, CostByYear.[2013], CostByYear.[2014], CostByYear.[2015]
from Customers c
left join (
select
pt.Customer_Id, isnull(pt.[2013], 0) as [2013],
isnull(pt.[2014], 0) as [2014], isnull(pt.[2015], 0) as [2015]
from (
select
o.Customer_Id, year(o.Date) [Year], sum(o.Total_Price) [TotalCost]
from Orders o
group by
o.Customer_Id, year(o.Date)
) src
pivot (
sum(TotalCost) for [Year] in ([2013], [2014], [2015])
) pt
) CostByYear on
c.Id = CostByYear.Customer_Id
order by
c.Name
Also you can do both approaches (mine and prev answer) with dynamically created queries if year ranges aren't known and strongly defined.