Sum Values From Specific Group of Rows - SQL - sql

I am trying to sum all Sales/TXNs and count the distinct IDs for the whole month, and not just the individual row, in which the rank is "1". So for customer "ABC", I want to retrieve all their data for Jan, and for customer "DEF" I want all their data for Feb.
Below is an example table as well as what my desired result set would be (apologies for the formatting).
Sales Table:
Month|ID |Dte |TXNs|Sales|Rank
Jan |ABC|1-5-17|1 |$15 |1
Jan |ABC|1-8-17|2 |$10 |2
Feb |ABC|2-6-17|1 |$20 |3
Feb |DEF|2-6-17|2 |$10 |1
Mar |DEF|3-5-17|1 |$40 |2
May |DEF|5/2/17|3 |$20 |3
Desired Answer:
Month|IDs|TXNs|Sales
Jan |1 |3 |$25
Feb |1 |2 |$10

You can use group by and in clause
select month, count(ID), sum(TNXs), sum(sales)
from my_table where ( month, ID ) in (
select distinct Month, ID
from my_table
where rank = 1
)
group by month

I think the IDs you listed in your table aren't right? Should the first row in your result be ABC and the second result be DEF?
Anyhow, I think this should work:
select month, ID, sum(TXNs), sum(SALES)
from SALES_TABLE
where
(
ID='ABC'
and MONTH='Jan'
)
or (
ID='DEF'
and MONTH='Feb'
)
group by ID, MONTH
edit: I missed the count part. How's this?
select month, count(ID), sum(TXNs), sum(SALES)
from SALES_TABLE
where rank = 1
group by month

Count Distinct should get you what you're looking for:
SELECT Month, COUNT(DISTINCT ID) AS UniqueIds, COUNT(*) AS Transactions, SUM(Sales) AS TotalSales
FROM t
INNER JOIN (
SELECT Month, ID FROM t WHERE Rank = 1
)firstRank WHERE t.ID = firstRank.ID AND t.Month = firstRank.Month
GROUP BY Month

It's hard to follow your description, but this seems to match:
select Month
,count(*) -- number of IDs
,sum(sumTXN)
,sum(sumSales)
from
(
select Month, ID, sum(TXNs) as sumTXN, sum(Sales) as sumSales
from tab
group by Month, ID
having min(Rank) = 1 -- only those IDs with a Rank = 1 within this month
) as dt
group by Month

Related

How to get value from a query of another table to create a new column (postgresql)

I am new to postgres and I want to be able to set value to Y if order (order table) is a first month order (first month order table)
first month order table is as per below. It will only show the order placed by user the first time in the month:
customer_id | order_date | order_id
--------------------------------------------------
a1 | December 6, 2015, 8:30 PM | orderA1
order table is as per below. It shows all the order records:
customer_id | order_date | order_id
-----------------------------------------------------
a1 | December 6, 2020, 8:30 PM | orderA1
a1 | December 7, 2020, 8:30 PM | orderA2
a2 | December 11, 2020, 8:30 PM | orderA3
To get the first month order column in the order table, I tried using case as below. But then it will give the error more than one row returned by a subquery.
SELECT DISTINCT ON (order_id) order_id, customer_id,
(CASE when (select distinct order_id from first_month_order_table) = order_id then 'Y' else 'N'
END)
FROM order_table
ORDER BY order_id;
I also tried using count but then i understand that this is quite inefficient and overworks the database i think.
SELECT DISTINCT ON (order_id) order_id, customer_id,
(CASE when (select count order_id from first_month_order_table) then 'Y' else 'N'
END)
FROM order_table
ORDER BY order_id;
How can I determine if the order is first month order and set the value as Y for every order in the order table efficiently?
Use the left join as follows:
SELECT o.order_id, o.customer_id,
CASE when f.order_id is not null then 'Y' else 'N' END as flag
FROM order_table o left join first_month_order_table f
on f.order_id = o.order_id
ORDER BY o.order_id;
If you have all orders in the orders table, you don't need the second table. Just use window functions. The following returns a boolean, which I find much more convenient than a character flag:
select o.*,
(row_number() over (partition by customer_id, date_trunc('month', order_date order by order_date) = 1) as flag
from orders o;
If you want a character flag, then you need case:
select o.*,
(case when row_number() over (partition by customer_id, date_trunc('month', order_date order by order_date) = 1
then 'Y' else 'N'
end) as flag
from orders o;

Count by week between dates

I'm trying to show a count by week but I am unsure of how to find the week that isn't showing between effdate and expdat. How do show the week and count shown below? Thanks.
You could use a recursive query to enumerate the weeks, then join it with the table
with cte as (
select min(effweek) week, max(expweek) max_week from mytable
union all
select week + 1, max_week from cte where week < max_week
)
select c.week, count(t.id_num) cnt
from cte c
left join mytable t on c.week between t.effweek and t.expweek
group by c.week
order by c.week
(Simplified) demo on DB Fiddle:
week | cnt
---: | --:
12 | 2
13 | 1
14 | 1

Getting Average based on 2 conditions (columns and rows)

My data is looking like this:
PRODUCT DEPT DATE PERCENTAGE
1 A JAN 2
1 B FEB 4
1 A MAR 1
1 B JAN 5
1 A FEB 3
1 B MAR 7
1 A JAN 3
1 B FEB 4
1 A MAR 2
1 B JAN 8
1 A FEB 9
1 B MAR 6
... ... ... ...
With thousands of different products and dozens of departments.
The calculation I have to go through is:
1 - Sum the percentages as follow: by product, dept and date (so Product 1 / DEPT A / JAN => SUM(PERCENTAGE). For each PRODUCT, DEPT and DATE.
2 - When I have my sums, get the average of the 3 months for each product and dept (product 1 dept A: JAN / FEB / MAR, and so on)
3 - Get the max average (for each product, which dept has the highest average).
I have something which works but it's so long I am sure I can learn and make something better:
Select
Verylong_q.TFC,
Round(MAX(verylong_q.average),2) AS HIGHEST_AVERAGE
FROM
(
SELECT
Long_Q.TFC,
Long_Q.DEPT,
Long_Q.Percentage1,
Long_Q.Percentage2,
Long_Q.Percentage3,
((Percentage1 + Percentage2 + Percentage3)/3) AS Average
FROM
(
SELECT
t_Month1.TFC,
t_Month1.DEPT,
t_Month1.Percentage1,
t_Month2.Percentage2,
t_Month3.Percentage3
From
(
Select
pos.TFC,
mv.Dept AS Sector,
sum(pos.percentage) AS Percentage3
FROM
TBO_POS pos,
TBL_MV mv
Where
pos.IV_ID = mv.IV_ID
and Date = […]
and TFC in […]
group by pos.TFC, mv.Dept, pos.Date
order by 1 DESC ) t_Month1
LEFT JOIN
(
Select
pos.TFC,
mv.Dept AS Sector,
sum(pos.percentage) AS Percentage2
FROM
TBO_POS pos,
TBL_MV mv
Where
pos.IV_ID = mv.IV_ID
and Date = […]
and TFC in […]
group by pos.TFC, mv.Dept, pos.Date
order by 1 DESC ) t_Month2
On t_month1.DEPT = t_month2.DEPT and t_month1.TFC = t_month2.TFC
LEFT JOIN
(
Select
pos.TFC,
mv.Dept AS Sector,
sum(pos.percentage) AS Percentage3
FROM
TBO_POS pos,
TBL_MV mv
Where
pos.IV_ID = mv.IV_ID
and Date = […]
and TFC in […]
group by pos.TFC, mv.Dept, pos.Date
order by 1 DESC ) t_Month3
on t_month1.DEPT = t_month3.DEPT and t_month1.TFC = t_month3.TFC
) Long_Q
) VeryLong_Q
Group by verylong_q.TFC
How could I do this in a better way? Thanks!
Isn't that simply:
Sum the percentages by product, dept and date in the innermost subquery
Get the average of the months for each product and dept in the next subquery
Get the max average for each product in the main query.
Query:
select product, max(avg_sum_percentage)
from
(
select product, dept, avg(sum_percentage) as avg_sum_percentage
from
(
select product, dept, date, sum(percentage) as sum_percentage
from mytable
group by product, dept, date
) per_product_dept_date
group by product, dept
) per_product_dept
group by product;
From what you describer lag() seems like the appropriate method, along with aggregation and selection of the best:
select *
from (select product, dept, (sump_1 + sump_2 + sump_3) /3 as avg_max,
row_number() over (partition by product order by (sump_1 + sump_2 + sump_3) /3 desc) as seqnum
from (select product, dept, date, sum(percentage) as sump,
lag(sum(percentage)) over (partition by product, dept order by date) as sump_1,
lag(sum(percentage, 2)) over (partition by product, dept order by date) as sump_2
from TBO_POS pos join
TBL_MV mv
on pos.IV_ID = mv.IV_ID
where Date = […] and TFC in […]
group by product, dept, date
) t
) t
where seqnum = 1;
This solution follows the description of the problem. It produces one row for each month and product. This version does not take into account missing values and other issues. I think this is the logic you want, but without expected results the question might be ambiguous.

Oracle SQL Query:Find out which year total sales amount is maximum

my working table, Table name: sales
Here Is MY TABLE, [sl_no is primary key] table structure:
CREATE TABLE SALES
( SL_NO NUMBER PRIMARY KEY, REGION VARCHAR2(10) NOT NULL,
MONTH VARCHAR2(20) NOT NULL, YEAR NUMBER NOT NULL,
SALES_AMOUNT NUMBER NOT NULL )
and here is table data:
SQL> select * from sales;
SL_NO REGION MONTH YEAR SALES_AMOUNT
---------- ---------- -------------------- ---------- ------------
1 east december 2011 750000
2 east august 2011 800000
3 west january 2012 640000
5 east march 2012 1200000
6 west february 2011 580000
4 west april 2011 555000
6 rows selected.
I have tried this query to view total sales amount of those[2011,2012] year;
SELECT year, SUM(sales_amount) FROM sales GROUP BY year;
YEAR SUM(SALES_AMOUNT)
---------- -----------------
2011 2685000
2012 1840000
MY GOAL:> I want to find out the year of maximum sales amount.
I tried this,and work perfectly...but when i want to display that year also, it gives an Error.
SQL> select max(sum(sales_amount)) from sales group by year;
MAX(SUM(SALES_AMOUNT))
----------------------
2685000
SQL> select year, max(sum(sales_amount)) from sales group by year;
select year, max(sum(sales_amount)) from sales group by year
*
ERROR at line 1:
ORA-00937: not a single-group group function
Extra addition: if multiple rows have same value means....when sales amount of both year[2011,2012] remain same, Then....
plZ help me to Solve this problem.
This should work.
with yr_agg as (
select year, sum(sales_amount) as total
from sales
group by year
)
select year, total as max_total
from yr_agg
where total = (select max(total)
from yr_agg);
I think the simplest way is to order the results and take the first row:
select year, sales_amount
from (SELECT year, SUM(sales_amount) as sales_amount
FROM sales
GROUP BY year
order by sum(sales_amount) desc
) t
where rownum = 1;
EDIT:
If you need to display all the matching rows (which isn't mentioned in the question), I would suggest using the dense_rank() analytic function:
select year, sales_amount
from (SELECT year, SUM(sales_amount) as sales_amount,
dense_rank(over order by SUM(sales_amount) desc) as seqnum
FROM sales
GROUP BY year
order by sum(sales_amount) desc
) t
where seqnum = 1;
Or, you might like the max() version instead:
select year, sales_amount
from (SELECT year, SUM(sales_amount) as sales_amount,
max(sum(sales_amount)) over () as maxsa
FROM sales
GROUP BY year
order by sum(sales_amount) desc
) t
where sales_amount = maxsa;
Following select should do what you need (untested, do not have Oracle at home):
select year, total
from (
select year, sum(sales_amount) total
from sales
group by year
)
where total = (select max(total_amount)
from (
select year, sum(sales_amount) total_amount
from sales
group by year
))
Take in account, though, that it might give you different years in each execution if two of them have exactly the same total amount. You might want to include some more conditions to avoid this.
Here is my Query where multiple row can select
SELECT year,MAX(total_sale) as max_total
FROM
(SELECT year,SUM(sales_amount) AS total_sale FROM sales GROUP BY year)
GROUP BY
year HAVING MAX(total_sale) =
(SELECT MAX(total_sale) FROM (SELECT SUM(sales_amount) AS total_sale FROM sales GROUP BY year));

SQL Join Not Returning What I Expect

I have a temp table I am creating a query off of in the following format. That contains a record for every CustomerID, Year, and Month for several years.
#T
Customer | CustomerID | Year | Month
ex.
Foo | 12345 | 2008 | 12
Foo | 12345 | 2008 | 11
Bar | 11224 | 2007 | 7
When I join this temp table to another table of the following format I get many more results than I am expecting.
Event
EventID | CustomerID | DateOpened
ex.
1100 | 12345 | '2008-12-11 10:15:43'
1100 | 12345 | '2008-12-11 11:25:17'
I am trying to get a result set of the count of events along with the Customer, Year, and Month like this.
SELECT COUNT(EventID), Customer, Year, Month
FROM [Event]
JOIN #T ON [Event].CustomerID = #T.CustomerID
WHERE [Event].DateOpened BETWEEN '2008-12-01' AND '2008-12-31'
GROUP BY Customer, Year, Month
ORDER BY Year, Month
I am getting a record for every Year and Month instead of only for December 2008.
You're specifying the date on the event table but not on the join -- so it's joining all records from the temp table with a matching customerid.
Try this:
SELECT COUNT(e.EventID), T.Customer, T.Year, T.Month
FROM [Event] e
INNER JOIN #T T ON (
T.CustomerID = e.CustomerID and
T.Year = year(e.DateOpened) and
T.Month = month(e.DateOpened)
)
WHERE T.Year = 2008
and T.Month = 12
GROUP BY T.Customer, T.Year, T.Month
ORDER BY T.Year, T.Month
Perhaps what you mean is:
SELECT COUNT(EventID)
,Customer
,Year
,Month
FROM [Event]
INNER JOIN #T
ON [Event].CustomerID = #T.CustomerID
AND YEAR([Event].DateOpened) = #T.YEAR
AND MONTH([Event].DateOpened) = #T.MONTH
WHERE [Event].DateOpened >= '2008-12-01'
AND [Event].DateOpened < '2009-01-01'
GROUP BY Customer
,Year
,Month
ORDER BY Year
,Month
Note, I've fixed another latent bug in your code: your BETWEEN is going to exclude datetimes like '2008-12-31 10:15:43' You can use this or similar technique.
The problem is that there are two rows in #T with CustomerID = 12345. Each of those rows joins with each of the rows in Event. If you only want the CustomerID in December, then you need to filter #T too:
SELECT COUNT(EventID), Customer, Year, Month
FROM [Event]
JOIN #T ON [Event].CustomerID = #T.CustomerID
WHERE [Event].DateOpened BETWEEN '2008-12-01' AND '2008-12-31'
AND #T.Year = 2008
AND #T.Month = 12
GROUP BY Customer, Year, Month
ORDER BY Year, Month
If you have some other expectation, you'd better clarify your question.
It seams that #T has redundant information if you only want the customer name.
You can solve it with a subquery
SELECT COUNT(EventID), (select TOP 1 #T.Customer from #T Where #T.CustomerID = [Event].CustomerID ), Year, Month
FROM [Event]
WHERE [Event].DateOpened BETWEEN '2008-12-01' AND '2008-12-31'
GROUP BY CustomerID, Year, Month
ORDER BY Year, Month