Nested Aggregate function in SQL Server - sql

I have a table Customers which contains customerID and Country columns. I would like to return the country with the most customers and the count of the customers.
This is what I tried:
SELECT COUNT(CustomerID), Country
FROM Customers
GROUP BY Country
HAVING COUNT(CustomerID) = MAX(COUNT(CustomerID));
How would I correctly implement the code? Thank you in advance.

Just use ORDER BY (to sort) and TOP (1) to get the single top row:
SELECT TOP (1) COUNT(CustomerID), Country
FROM Customers
GROUP BY Country
ORDER BY COUNT(CustomerID) DESC;

Marc_s's solution was my first thought +1
However, if your CustomerID is not unique and/or you want to see ties (edge case)
Example
Declare #YourTable Table ([Country] varchar(50),CustomerID varchar(50)) Insert Into #YourTable Values
('USA','AA')
,('USA','AB')
,('USA','AB') -- USA has 3 CustomerIDs (2 distinct)
,('France','AC')
,('France','AD') -- France has 2 CustomerIDs (2 distinct)
,('Singapore','AE')
;with cte as (
Select country
,CustCnt = count(Distinct CustomerID) -- Distinct may not be necessary if already unique
from #YourTable
Group By Country
)
Select top 1 with ties *
From cte
Order by dense_rank() over (order by CustCnt desc)
Results
country CustCnt
France 2
USA 2

SELECT COUNT (CustomerID),Country FROM Customers
GROUP BY Country
HAVING COUNT (CustomerID) = (SELECT MAX (tt) c FROM ((SELECT count (CustomerID) tt FROM Customers GROUP BY Country)) AS a)

Related

Grouping in SQL using CASE Statements

Hello I am trying to group multiple customer orders into buckets in SQL, the output should look something like it does below. Do I have to use a case statement to group them?
Table1 looks like:
CustomerID
Order_date
1
somedate
2
somedate
3
somedate
2
somedate
Edit: # of customers meaning if CustomerID 2 had 2 orders he/she would be of the in the bucket of #of orders of 2.
Output should be something like this?
# of Customers
# of Orders
2
1
1
2
My code so far is:
select count(*) CustomerID
FROM Table1
GROUP BY CustomerID;
Use a double aggregation:
SELECT COUNT(*) AS num_customers, cnt AS num_orders
FROM
(
SELECT CustomerID, COUNT(*) AS cnt
FROM Table1
GROUP BY CustomerID
) t
GROUP BY cnt;
The inner subquery finds the number of orders for each customer. The outer query then aggregates by number of orders and finds out the number of customers having each number of orders.
If you want to sort your tables and your users depending on the number of orders they made, this query should work:
SELECT CustomerID, COUNT(CustomerID) as NbOrder
FROM Table1
GROUP BY(NbOrder)
I believe what you want to do is get the count of orders by customer, first, via aggregation. Then get the count of customers by order count from that query.
SELECT count(*) as count_of_customers, count_of_orders
FROM
(
SELECT customerid, count(*) as count_of_orders
FROM your_table
GROUP BY customerid
) sub
GROUP BY count_of_orders
ORDER BY count_of_orders

Return row with the highest count (like city with most transactions, when there are multiple cities)

I have a table with the following rows:
customer_id
city
transaction_id
date
I want to pull the city which has the highest number of transactions (count(transaction_id)) over a certain time period. A customer can have transactions in multiple cities.
What's the right syntax to achieve this?
UPDATE:
customer A can have 5 transactions in NYC and 10 transactions in Boston. I just want the customer record of the 10 transactions in Boston to be returned. How do I do this and what would have to cities that have the same number of transactions per customer_id?
if you need just top city name count then you can use below approach
select city,count(transaction_id) as cnt
From YourTable
where date=condition --- that you need
group by city
order by cnt desc
limit 1
if you need customerwise highest city name then use row_number()
with cte as
(
select customerid,city,count(*) as cnt
from table_name group by customerid,city
), cte1 as
(
select * ,row_number()over(partition by cusomerid order by cnt desc) rn
from cte
) select * from cte1 where rn=1

SQL show orders where 2 values are distinct and match the first

I'm looking for a way to let me select all orders that have multiple distinct names within the same order-number, it looks like this:
order - name
111-Paul
112-Paula
113-John
113-John
113-Jessica
114-Eric
114-Eric
114-John
115-Zack
115-Zack
115-Zack
etc.
so that i would get all the orders that have 2 or more distinct names in it:
113-John
113-Jessica
114-Eric
114-John
with which I could do further queries but I'm stuck. Can anyone give me some hints on how to tackle this problem please? I've tried it with count(*) which looked like this:
select order, name, count(name) from dbo.orders
group by order, name
having count(name) > 1
which gave me all the orders which had more than 1 name in it but I don't know how to let it only show orders with the distinct names.
Here's one approach using exists:
select distinct [order], name
from orders o
where exists (
select 1
from orders o2
where o.[order] = o2.[order] and o.name != o2.name)
Fiddle Demo
I would use windows functions for this
For example:
select distinct order
from
(select
order,
row_number() over(partition by order, name order by order asc) as rn
) as t1
where rn > 1
you can do the same with count
count(*) over(partition by order,name order by order asc) as cnt
Here's a straight forward implementation in Sql Server:
select distinct *
from table1
where [order] in (
select [order]
from (select distinct * from table1) iq
group by [order]
having count(*) > 1)
It's essentially breaking down the problem into:
Finding the orders that have more than one distinct value.
Finding the pairs of distinct order - name that belong to the list previously calculated.
When you use HAVING COUNT(name) > 1, it is counting all of the rows in those groups, including duplicate rows (rows 113-John and 113-John are 2 rows for order 113). I would query distinct rows from your table, and then select from that:
SELECT [order], [name] FROM (
SELECT DISTINCT [order], [name] FROM dbo.orders
) A
GROUP BY [order], [name]
HAVING COUNT([name]) > 1
As a note, if a [name] is null, then it will not be counted with COUNT(name). If you want nulls to be counted, use COUNT(*) instead.
You can use count(distinct name) to get the number of unique names for each order:
select [order], count(distinct name)
from orders
group by [order]
To just get the order for those you can use having:
select [order]
from orders
group by [order]
having count(distinct name) > 1
To get the details for those orders you can put that in the where clause to just return the rows with order in that list:
select *
from orders
where [order] in (
select [order]
from orders
group by [order]
having count(distinct name) > 1
)
sqlfiddle
I would use RANK (or DENSE_RANK) for this as shown below.
SELECT [Order]
FROM (SELECT
[Order],
RANK() OVER(PARTITION BY [Order] ORDER BY Name) AS NameRank
FROM [StackOverflow].[dbo].[OrderAndName]) ranked
WHERE ranked.NameRank > 1
GROUP BY [Order]
The sub-query ranks (gives a seeding) to the names in an order according to their value. Names with the same value would have the same rank i.e. when an order has one name multiple times (like 115) the rank of all names would be 1.
The partition is important here as otherwise you would get the rank for all names for all orders which wouldn't give you the result you'd like.
It is then just a case of pulling out the orders that have a RANK greater than 1 and grouping (could use distinct if that's a preference).
You can then join to this table to get get the orders and names as follows;
SELECT oan.[Order], [Name]
FROM [StackOverflow].[dbo].[OrderAndName] oan
INNER JOIN (SELECT [Order]
FROM (SELECT [Order],
RANK() OVER(PARTITION BY [Order] ORDER BY Name) AS NameRank
FROM [StackOverflow].[dbo].[OrderAndName]) ranked
WHERE ranked.NameRank > 1
GROUP BY [Order]) twoOrMore ON oan.[Order] = twoOrMore.[Order]

Add a total at the bottom

I need to add a Total at the bottom of my table. Is this possible in my scenario?
select country, count(*) from customer
group by country
Country Count
USA 5
UK 10
Canada 15
Russia 25
55 (Requested from SO community)
Use rollup()
select country, count(*)
from customer
group by rollup (country )
If you want the label "Total" as well, you can use the grouping function:
select case
when grouping(country) = 1 then 'Total'
else country
end as country,
count(*)
from customer
group by rollup (country )
Online example: http://rextester.com/PKFE63954
You can artificially generate a row by using something like
select country
,count(*)
from customer
group by country
UNION ALL
SELECT 'Total'
,COUNT(*)
FROM customer
Although this will affect any future calculations you make on this result set as it is a new row in the data.
something like:
SELECT country
, count(ยด1)
FROM customer
GROUP BY country
UNION
SELECT 'TOTAL'
, count(1)
FROM customer;
You could add another column to the row named Total
DECLARE #Total int = 0;
SET #Total = (SELECT COUNT(*) FROM customer);
SELECT country, COUNT(*), [Total] = #Total
FROM customer
GROUP BY country

How to count how many unique records with multiple records in one table

I have customer table as the following:
CustomerID, ReferenceID
1 ,101
2 ,101
3 ,101
4 ,102
5 ,102
6 ,103
I want to count how many ReferenceID has multiple customerID and I write the following query:
SELECT CustomerID, ReferenceID, Count(1)
FROM Customer
group by CustomerID, ReferenceID
having Count(1) >1
I should get number 2 because of 2 ReferenceID have multiple customerID , but I do not get any number
DECLARE #ReferenceIdCount INT
SELECT #ReferenceIdCount = COUNT(*)
FROM
(
SELECT
ReferenceId
,COUNT(DISTINCT CustomerId) as ReferenceCount
FROM
#Table
GROUP BY
ReferenceId
HAVING
COUNT(DISTINCT CustomerId) > 1
) t
First you have to find the ReferenceId's that have multiple CustomerIds then next you have to count them. So you can do this many ways but the nested select is an easy way to show you.
Can I set this number as variable ?
Declare #RFCount
set #RFCount = (
SELECT COUNT(*)
FROM
(
SELECT
ReferenceId
,COUNT(DISTINCT CustomerId) as ReferenceCount
FROM
#Table
GROUP BY
ReferenceId
HAVING
COUNT(DISTINCT CustomerId) > 1
) t)
print #FCount
I get error message for the above statement