sql: query to find max count with extra columns as well - sql

Input table:events
month user
2020-11 user_1
2020-11 user_5
2020-11 user_3
2020-12 user_2
2020-10 user_4
2020-09 user_6
GOAL
I want max(distinct user) grouped by month column.
my final result need two columns one is month and another one is max_count
I need output similar to this
month max_count
2020-11 3
I followed some approach
Approach1:
select max(cnt) max_count
from
(
select month,
count(distinct user) as cnt
from events
group by 1
)
if i follow this approach, it is just giving me only max_count but i need month column as well
I know we can use something like order by and limit to get the result. But i dont want that hacky way.
Can anyone suggest a solution for this?

Use a window function:
select month, cnt
from
(
select month,
count(distinct "user") as cnt,
dense_rank() over (order by count(distinct "user") desc) as rnk
from events
group by month
) t
where rnk = 1;
user is a reserved keyword in SQL and should be quoted (or better: find a different name)

If I understand correctly, you can use order by and some clause to limit the results:
select month, count(distinct user)
from events
group by month
order by count(distinct user) desc
fetch first 1 row only;
Note that not all databases support the standard fetch clause. You might want limit or select top (1) or something similar.

Related

Multiple Aggregation in SQL

loan_no loan_amt contact date customer_id salesman_id
I have the following table. I need to somehow get the average of loan_no and the average of loan_amt for the people with more than one loan_no. I need to somehow plug in the avg and count functions.
I am seriously struggling with that. I was also thinking of a pivot function.
I would really appreciate it if someone can suggest a SQL code
My efforts so far:
select count (loan_no), tcustomer_id
from table
group by customer_id
having count (loan_no) > 1
Now I just do not know how to also include the avg function.
Not sure why do you need average Loan_no but you can still get it through -
select customer_id, avg(loan_no), avg(loan_amt)
from (select *, count(*) over(partition by customer_id) cnt
from table)
where cnt > 1
group by customer_id
You can use two levels of aggregation:
select avg(num_loans), sum(total) / sum(num_loans)
from (select customer_id, count(*) as num_loans, sum(loan_amt) as total
from table
group by customer_id
) t
where num_loans > 1;

SQL PIVOT group by 2 columns

I have a attendance table as below i want group them by time and section,status is null mean that the employee is absent :
Any idea how to generate output like below?
my current code :
SELECT TIME,COUNT(SECTION) AS SECTION,COUNT(STATUS) AS COUNT
FROM attendance_record
GROUP BY TIME,SECTION
ORDER BY TIME
If I understand your question, just use conditional aggregation:
SELECT TIME, SECTION, COUNT(*) as TOTAL,
COUNT(STATUS) AS IN, ( COUNT(*) - COUNT(STATUS) ) as ABSENT
FROM attendance_record
GROUP BY TIME, SECTION
ORDER BY TIME

SQL Max Value of Column to choose which row is selected

I'm having a problem with selecting a single row when there are multiple entries for one "callid"
In this case there are two rows being selected:
Assignee maxTime
Jim Smith 11:31:05
James Smith 17:50:16
I want to only select a single row that has the greatest time.
Output I want:
Assignee maxTime
James Smith 17:50:16
This is my code:
select Assignee, MAX(TimeResolv) as maxTime
from heat8..asgnmnt
where callid ='00539265'
and GroupName like '%cs%'
Group by Assignee
Help would be appreciated.
You can use TOP :
SELECT TOP 1 *
FROM heat8..asgnmntt t
ORDER BY t.timeResolv DESC
Or less efficient with NOT EXISTS():
SELECT * FROM heat8..asgnmntt t
WHERE NOT EXISTS(SELECT 1 FROM heat8..asgnmnt s
WHERE s.timeResolv > t.timeResolv)
Or with window function ROW_NUMBER() :
SELECT s.Assignee, s.TimeResolv
FROM (
SELECT t.*,
ROW_NUMBER() OVER(ORDER BY t.timeResolv) as rnk
FROM heat8..asgnmntt t) s
WHERE s.rnk = 1
ROW_NUMBER() is also good to do it with one query for results per group.
You can use a subquery. Your subquery will look exactly like your current query, but you will need to select top 1 from it.
select TOP 1 Assignee, (TimeResolv) as maxTime
FROM (
select Assignee, MAX(TimeResolv) as maxTime
from heat8..asgnmnt
where callid ='00539265'
and GroupName like '%cs%'
Group by Assignee
)
ORDER BY TimeResolv DESC
EDIT: no need for subquery, but if you want to use the same code you did above for easy readability or it just makes sense to you. You can continue to use it with this method.

Is it possible to calculate the sum of each group in a table without using group by clause

I am trying to find out if there is any way to aggregate a sales for each product. I realise I can achieve it either by using group-by clause or by writing a procedure.
example:
Table name: Details
Sales Product
10 a
20 a
4 b
12 b
3 b
5 c
Is there a way possible to perform the following query with out using group by query
select
product,
sum(sales)
from
Details
group by
product
having
sum(sales) > 20
I realize it is possible using Procedure, could it be done in any other way?
You could do
SELECT product,
(SELECT SUM(sales) FROM details x where x.product = a.product) sales
from Details a;
(and wrap it into another select to simulate the HAVING).
It's possible to use analytic functions to do the sum calculation, and then wrap that with another query to do your filtering.
See and play with the example here.
select
running_sum,
OwnerUserId
from (
select
id,
score,
OwnerUserId,
sum(score) over (partition by OwnerUserId order by Id) running_sum,
last_value(id) over (partition by OwnerUserId order by OwnerUserId) last_id
from
Posts
where
OwnerUserId in (2934433, 10583)
) inner_q
where inner_q.id = inner_q.last_id
--and running_sum > 20;
We keep a running sum going on the partition of the owner (product), and we tally up the last id for the same window, which is the ID we'll use to get the total sum. Wrap it all up with another query to make sure you get the "last id", take the sum, and then do any filtering you want on the result.
This is an extremely round-about way to avoid using GROUP BY though.
If you don't want nested select statements (run slower), use CASE:
select
sum(case
when c.qty > 20
then c.qty
else 0
end) as mySum
from Sales.CustOrders c

Oracle - Selecting the n-1 record from a table

I have a table of data and want to retrieve the penultimate record.
How is this done?
TABLE: results
-------
30
31
35
I need to get 31.
I've been trying with rownum but it doesn't seem to work.
Assuming you want the second highest number and there are no ties
SELECT results
FROM (SELECT results,
rank() over (order by results desc) rnk
FROM your_table_name)
WHERE rnk = 2
Depending on how you want to handle ties, you may want either the rank, dense_rank, or row_number analytic function. If there are two 35's for example, would you want 35 returned? Or 31? If there are two 31's, would you want a single row returned? Or would you want both 31's returned.
This can use for n th rank ##
select Total_amount from (select Total_amount, rank() over (order by Total_amount desc) Rank from tbl_booking)tbl_booking where Rank=3