Reputation: 10063
I want to count the number of the number of the maximum value of the minimum values in a particular field.
This select is broken if I were to uncomment the lines:
select clientnumber
,count(distinct clientname) as NumberUnique
,max(clientname) as UniqueSample1
-- ,sum(case clientname when min(clientname) then 1 else 0 end) as UniqueCount1
,min(clientname) as UniqueSample2
-- ,sum(case clientname when min(clientname) then 1 else 0 end) as UniqueCount2
FROM dbo.InvoicesSent dt
group by clientnumber
having count(distinct clientname) > 1
Background: We are reviewing records that have two different names on the invoices for the same clientnumber. Knowing which one of the two has the higher count is handy for the analysis.
Note: I know there are other solutions that can take into account more than 2 unique values but at this point, I want to solve this problem for its academic value. I also know I could solve this with a subselect but I am holding out for some graceful solution.
Upvotes: 0
Views: 66
Reputation: 1269953
If you know that there are two names, then you could do this without a subselect:
select top (1) with ties clientnumber,
min(clientname) over (partition by clientnumber) as min_name,
max(clientname) over (partition by clientnumber) as max_name,
(case when clientname = min(clientname) over (partition by clientnumber)
then count(*)
else sum(count(*)) over (partition by clientnumber) - count(*)
end) as min_count,
(case when clientname = max(clientname) over (partition by clientnumber)
then count(*)
else sum(count(*)) over (partition by clientnumber) - count(*)
end) as max_count
from InvoicesSent i
group by clientnumber, clientname
order by row_number() over (partition by clientnumber order by clientname) +
(case when min(clientname) over (partition by clientnumber) = max(clientname) over (partition by clientnumber) then 1 else 0 end);
I am not arguing that this is elegant. Merely that it is possible for your conditions.
Here is a slightly simpler form:
select top (1) with ties clientnumber,
clientname as min_name,
max(clientname) over (partition by clientnumber) as max_name,
count(*) as min_count,
sum(count(*)) over (partition by clientnumber) - count(*) max_count
from InvoicesSent i
group by clientnumber, clientname
order by row_number() over (partition by clientnumber order by clientname) +
(case when count(*) over (partition by clientnumber) >= 2 then 0 else 1 end)
And a db<>fiddle.
Actually, this isn't so bad. It is aggregating by the name column and the client, putting all the data on one row using window functions and then selecting the one row. There is a little trick in the order by
to only select clients with more than one name.
Upvotes: 2
Reputation: 5922
If you are making use of sql server that supports string_agg the following can be done
Here is a db fiddble link https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=4184c994d37e0dc7178212cd657ba15f
create table invoices(id int identity, clientnumber int, clientname varchar(100));
insert into invoices(clientnumber,clientname)
select t.clientnumber,clientname
from (values(100,'Adam')
,(100,'Adam')
,(100,'Abraham')
,(101,'Grace')
,(101,'Grace')
,(102,'Paul')
)t(clientnumber,clientname);
select clientnumber
,string_agg(concat(clientname,'-',cnt),';') as clientname_and_cnt
from(
select distinct
clientnumber
,clientname
,count(*) over(partition by clientnumber,clientname) as cnt
from invoices
)x
group by clientnumber
order by 2 desc
Output
+--------------+--------------------+
| clientnumber | clientname_and_cnt |
+--------------+--------------------+
| 102 | Paul-1 |
| 101 | Grace-2 |
| 100 | Abraham-1;Adam-2 |
+--------------+--------------------+
And if you want to show up those which have two or more clientnames by clientnumber then Add the following in the having clause
having(charindex(';',string_agg(concat(clientname,'-',cnt),';')))<>0
If you would like ordering based on the number of invoices by the clientname then you may also do the following (within group clause)
select clientnumber
,string_agg(concat(clientname,'-',cnt),';') within group(order by cnt desc) as clientname_and_cnt
from(
select distinct
clientnumber
,clientname
,count(*) over(partition by clientnumber,clientname) as cnt
from invoices
)x
group by clientnumber
order by 2 desc
Upvotes: 0