How to select "best" (Max) value by day for each category? In MS SQL - sql

I'm using MS SQL and am trying to find the "best" (Max sales volume) day for customer reps. I currently have a table with the columns:
RepName, Date, Volume
That has the following data
- Joe Salesman, 01/01/2021, 2
- Mike Moneymaker, 01/01/2021, 14
- Rachel Revenue, 01/01/2021, 7
- Joe Salesman, 01/02/2021, 9
- Mike Moneymaker, 01/02/2021, 8
- Rachel Revenue, 01/02/2021, 12
- Joe Salesman, 01/03/2021, 7
- Mike Moneymaker, 01/03/2021, 11
- Rachel Revenue, 01/03/2021, 14
with this continuing up to today's data.
I'm trying to list the Max Volume per Rep, and the respective date the rep obtained that volume. This would look like:
- Joe Salesman, 01/02/2021, 9
- Mike Moneymaker, 01/01/2021, 14
- Rachel Revenue, 01/03/2021, 14
Current Code I'm using is more or less in this format:
SELECT RepName, Date, MAX(Volume)
FROM MyTable
GROUP BY RepName, Date
However, this is returning all rows of data in MyTable, instead of showing the maximums.
Anybody have any insight or tips?

Use window functions:
SELECT t.*
FROM (SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY RepName ORDER BY Volume DESC) as seqnum
FROM MyTable t
) t
WHERE seqnum = 1;

Related

SQL How to take the minium for multiple fields?

Consider the following data set that records the product sold, year, and revenue from that particular product in thousands of dollars. This data table (YEARLY_PRODUCT_REVENUE) is stored in SQL and has many more rows.
Year | Product | Revenue
2000 Table 100
2000 Chair 200
2000 Bed 150
2010 Table 120
2010 Chair 190
2010 Bed 390
Using SQL, for every year I would like to find the product that has the maximum revenue.
That is, I would like my output to be the following:
Year | Product | Revenue
2000 Chair 200
2010 Bed 390
My attempt so far has been this:
SELECT year, product, MIN(revenue)
FROM YEARLY_PRODUCT_REVENUE
GROUP BY article, month;
But when I do this, I get multiple-year values for distinct products. For instance, I'm getting the output below which is an error. I'm not entirely sure what the error here is. Any help would be much appreciated!
Year | Product | Revenue
2000 Table 100
2000 Bed 150
2010 Table 120
2010 Chair 190
You don't mention the database so I'll assume it's PostgreSQL. You can do:
select distinct on (year) * from t order by year, revenue desc
You want filtering rather than aggregation. We can use window functions (which most databases support) to rank yearly product sales, and then retain only the top selling product per year.
select *
from (
select r.*, rank() over(partition by year order by revenue desc) rn
from yearly_product_revenue r
) r
where rn = 1;
Here is a shorter solution if your database support the standard WITH TIES clause:
select *
from yearly_product_revenue r
order by rank() over(partition by year order by revenue desc)
fetch first row with ties

Select car of max(date) for every employee [duplicate]

This question already has answers here:
Fetch the rows which have the Max value for a column for each distinct value of another column
(35 answers)
Select First Row of Every Group in sql [duplicate]
(2 answers)
Return row with the max value of one column per group [duplicate]
(3 answers)
GROUP BY with MAX(DATE) [duplicate]
(6 answers)
How to return only latest record on join [duplicate]
(6 answers)
Closed last year.
I would need a code for the following problem:
I have a table like this:
Employee
Year
Month
Car
Tom
2021
9
Ford
Tom
2021
10
Ford
Tom
2021
11
Ford
Tom
2021
12
Renault
Tom
2022
1
Renault
Mark
2021
12
VW
Mark
2022
1
VW
Mark
2022
2
VW
Joe
2021
8
Opel
Joe
2021
9
Tesla
Joe
2021
10
Ferrari
And I would need the car used by the employee for the last possible date. So the result should be:
Employee
Car
Tom
Renault
Mark
VW
Joe
Ferrari
With:
select employee, max(year || month) from table.cars
group by employee
I get the max(date) for every employee, but I do not know how to join the cars to the max(date).
How can I get the result I want?
You can use ROW_NUMBER() analytic function such as
SELECT Employee, Car
FROM (SELECT ROW_NUMBER() OVER
(PARTITION BY Employee ORDER BY year DESC, month DESC) AS rn,
c.*
FROM cars c)
WHERE rn = 1
provided that the data type of the year and month are of string type, then you can replace the part ORDER BY year DESC, month DESC with
ORDER BY TO_NUMBER(TRIM(year)) DESC, TO_NUMBER(TRIM(month)) DESC
with t as
(
select *,
row_number() over (partition by employee order by year desc, month desc) rn
from cars
)
select employee, car
from t
where rn = 1
Try this:
select employee, car
from (
Select *, ROW_NUMBER(partition by employee order by year, month DESC) as row_number
from cars
)a
Where row_number = 1

Cumulative Sum Query in SQL table with distinct elements

I have a table like this, with column names as Date of Sale and insurance Salesman Names -
Date of Sale | Salesman Name | Sale Amount
2021-03-01 | Jack | 40
2021-03-02 | Mark | 60
2021-03-03 | Sam | 30
2021-03-03 | Mark | 70
2021-03-02 | Sam | 100
I want to do a group by, using the date of sale. The next column should display the cumulative count of the sellers who have made the sale till that date. But same sellers shouldn't be considered again.
For example,
The following table is incorrect,
Date of Sale | Count(Salesman Name) | Sum(Sale Amount)
2021-03-01 | 1 | 40
2021-03-02 | 3 | 200
2021-03-03 | 5 | 300
The following table is correct,
Date of Sale | Count(Salesman Name) | Sum(Sale Amount)
2021-03-01 | 1 | 40
2021-03-02 | 3 | 200
2021-03-03 | 3 | 300
I am not sure how to frame the SQL query, because there are two conditions involved here, cumulative count while ignoring the duplicates. I think the OVER clause along with the unbounded row preceding may be of some use here? Request your help
Edit - I have added the Sale Amount as a column. I need the cumulative sum for the Sales Amount also. But in this case , all the sale amounts should be considered unlike the salesman name case where only unique names were being considered.
One approach uses a self join and aggregation:
WITH cte AS (
SELECT t1.SaleDate,
COUNT(CASE WHEN t2.Salesman IS NULL THEN 1 END) AS cnt,
SUM(t1.SaleAmount) AS amt
FROM yourTable t1
LEFT JOIN yourTable t2
ON t2.Salesman = t1.Saleman AND
t2.SaleDate < t1.SaleDate
GROUP BY t1.SaleDate
)
SELECT
SaleDate,
SUM(cnt) OVER (ORDER BY SaleDate) AS NumSalesman,
SUM(amt) OVER (ORDER BY SaleDate) AS TotalAmount
FROM cte
ORDER BY SaleDate;
The logic in the CTE is that we try to find, for each salesman, an earlier record for the same salesman. If we can't find such a record, then we assume the record in question is the first appearance. Then we aggregate by date to get the counts per day, and finally take a rolling sum of counts in the outer query.
The best way to do this uses window functions to determine the first time a sales person appears. Then, you just want cumulative sums:
select saledate,
sum(case when seqnum = 1 then 1 else 0 end) over (order by saledate) as num_salespersons,
sum(sum(sales)) over (order by saledate) as running_sales
from (select t.*,
row_number() over (partition by salesperson order by saledate) as seqnum
from t
) t
group by saledate
order by saledate;
Note that this in addition to being more concise, this should have much, much better performance than a solution that uses a self-join.

SQL COUNT the number purchase between his first purchase and the follow 10 months

every customer has different first-time purchase date, I want to COUNT the number of purchases they have between the following 10 months after the first purchase?
sample table
TransactionID Client_name PurchaseDate Revenue
11 John Lee 10/13/2014 327
12 John Lee 9/15/2015 873
13 John Lee 11/29/2015 1,938
14 Rebort Jo 8/18/2013 722
15 Rebort Jo 5/21/2014 525
16 Rebort Jo 2/4/2015 455
17 Rebort Jo 3/20/2016 599
18 Tina Pe 10/8/2014 213
19 Tina Pe 6/10/2016 3,494
20 Tina Pe 8/9/2016 411
my code below just use ROW_NUM function to identify the first purchase, but I don't know how to do the calculations or there's a better way to do it?
SELECT client_name,
purchasedate,
Dateadd(month, 10, purchasedate) TenMonth,
Row_number()
OVER (
partition BY client_name
ORDER BY client_name) RM
FROM mytable
You might try something like this - I assume you're using SQL Server from the presence of DATEADD() and the fact that you're using a window function (ROW_NUMBER()):
WITH myCTE AS (
SELECT TransactionID, Client_name, PurchaseDate, Revenue
, MIN(PurchaseDate) OVER ( PARTITION BY Client_name ) AS min_PurchaseDate
FROM myTable
)
SELECT Client_name, COUNT(*)
FROM myCTE
WHERE PurchaseDate <= DATEADD(month, 10, min_PurchaseDate)
GROUP BY Client_name
Here I'm creating a common table expression (CTE) with all the data, including the date of first purchase, then I grab a count of all the purchases within a 10-month timeframe.
Hope this helps.
Give this a whirl ... Subquery to get the min purchase date, then LEFT JOIN to the main table to have a WHERE clause for the ten month date range, then count.
SELECT Client_name, COUNT(mt.PurchaseDate) as PurchaseCountFirstTenMonths
FROM myTable mt
LEFT JOIN (
SELECT Client_name, MIN(PurchaseDate) as MinPurchaseDate GROUP BY Client_name) mtmin
ON mt.Client_name = mtmin.Client_name AND mt.PurchaseDate = mtmin.MinPurchaseDate
WHERE mt.PurchaseDate >= mtmin.MinPurchaseDate AND mt.PurchaseDate <= DATEADD(month, 10, mtmin.MinPurchaseDate)
GROUP BY Client_name
ORDER BY Client_name
btw I'm guessing there's some kind of ClientID involved, as nine character full name runs the risk of duplicates.

ORACLE SQL Running TOTAL and daytotal using window function

From the EMPLOYEE table, I want to group the amount of records(employees hired) AND also have the running TOTAL per day.
The format of the input is like this:
rownum Hired_date_time
1 1/10/2012 11:00
2 1/10/2012 13:00
3 20/11/2012 10:00
4 20/11/2012 15:00
5 20/11/2012 16:00
6 30/12/2012 1:00
The desired output:
Hired_date.......Hired_per_day.........TOTAL_number_of_employees
1/10/2012 ...................2 ........2
20/11/2012 ..................3 ........5
30/12/2012 ..................1 ....... 6
No problem for the GROUPING PER DAY:
select trunc(Hired_date_time) as "Hired_date" ,
count(*) as "Hired_per_day"
from employee
group by trunc(Hired_date_time)
order by trunc(Hired_date_time);
Question: how can I have a running total (in last column) using the window function
select trunc(hired),
count(*) hired_today,
sum(count(*)) over (order by trunc(hired)) as running_total
from emp
group by trunc(hired)
http://sqlfiddle.com/#!4/4bd36/9
select trunc(hire_date),
count(*) over (partition by trunc(hire_date)) as hired_per_day,
count(*) over (order by hire_date) as total_number_of_employees
from employee
order by trunc(hire_date)