Count Distinct Window Function with Groupby - sql

I have a table that contains a user's name, market, and purchase_id. I'm trying to use a window function in SnowSql without a subquery to count the distinct number of purchases a user bought as well as the total number of unique purchases for the market.
Initial Table
User
Market
Purchase_ID
John Smith
NYC
1
John Smith
NYC
2
Bob Miller
NYC
2
Bob Miller
NYC
4
Tim Wilson
NYC
3
The desired result would look like this:
User
Purchases
Unique Market Purchases
John Smith
2
4
Bob Miller
2
4
Tim Wilson
1
4
The query I've been attempting without a subquery looks like the below but receives an error with the groupby.
SELECT
user,
COUNT(DISTINCT purchase_id),
COUNT(DISTINCT purchase_id) OVER (partition by market)
FROM table
GROUP BY 1
Appreciate any assistance with this. Thanks!

This might work , you could wrangle to get into the format you're after but it produces the answer without subquery.
Uses the awesome GROUPING SETS which allows multiple group-by clauses in a single statement - the exact error you were hitting :-).
Awesome question!
SELECT
COUNT(DISTINCT PURCHASE_ID)
, USER_NAME
, MARKET
FROM
CTE
GROUP BY
GROUPING SETS (USER_NAME, MARKET);
Copy|Paste|Run
WITH CTE AS (SELECT 'JOHN SMITH' USER_NAME, 'NYC' MARKET, 1
PURCHASE_ID
UNION SELECT 'JOHN SMITH' USER_NAME, 'NYC' MARKET, 2 PURCHASE_ID
UNION SELECT 'BOB MILLER' USER_NAME, 'NYC' MARKET, 2 PURCHASE_ID
UNION SELECT 'BOB MILLER' USER_NAME, 'NYC' MARKET, 4 PURCHASE_ID
UNION SELECT 'TIM WILSON' USER_NAME, 'NYC' MARKET, 3 PURCHASE_ID)
SELECT
COUNT(DISTINCT PURCHASE_ID)
, USER_NAME
, MARKET
FROM
CTE
GROUP BY
GROUPING SETS (USER_NAME, MARKET);

I don't think you can do this simply as an aggregation. But you can get the answer like this:
SELECT user,
SUM( (seqnum = 1)::INT ) as purchases,
SUM(SUM( (seqnum = 1)::INT )) OVER (PARTITION BY market) as market_purchases
FROM (SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY purchase_id ORDER BY purchase_id) as seqnum
FROM table t
) t
GROUP BY 1

DISTTNCT in a window function is not allowed, so you need to use the subquery
CREATE TABLE table1
(`User` varchar(10), `Market` varchar(3), `Purchase_ID` int)
;
INSERT INTO table1
(`User`, `Market`, `Purchase_ID`)
VALUES
('John Smith', 'NYC', 1),
('John Smith', 'NYC', 2),
('Bob Miller', 'NYC', 2),
('Bob Miller', 'NYC', 4),
('Tim Wilson', 'NYC', 3)
;
SELECT
user,
COUNT(DISTINCT purchase_id)
,MAX((SELECT COUNT(DISTINCT purchase_id) FROM table1 WHERE `Market` = t1.`Market` )) bymarkte
FROM table1 t1
GROUP BY 1
user | COUNT(DISTINCT purchase_id) | bymarkte
:--------- | --------------------------: | -------:
Bob Miller | 2 | 4
John Smith | 2 | 4
Tim Wilson | 1 | 4
SELECT
user,
COUNT(DISTINCT purchase_id)
,MAX(countr) bymarkte
FROM table1 t1
INNER JOIN (SELECT `Market`,COUNT(DISTINCT purchase_id) countr FROM table1 GROUP BY `Market` ) ta ON t1.`Market` = ta.`Market`
GROUP BY 1
user | COUNT(DISTINCT purchase_id) | bymarkte
:--------- | --------------------------: | -------:
Bob Miller | 2 | 4
John Smith | 2 | 4
Tim Wilson | 1 | 4
db<>fiddle here

Related

Select records with fewer than 10 entries sql server

i want only to display the The ID which have record less than 10 entries for each ID, an ID may have several values as you see in the data below. i want
i have tried this query but it selects also the record for ID 2
select ID, Name ,LastName ,PaymentDate,POSITION
From ( select ID, Name ,LastName ,PaymentDate ,ROW_NUMBER() OVER(PARTITION BY ID ORDER BY PaymentDate DESC) AS POSITION
)
where Position < 10
any help please
ID Name LastName PaymentDate
1 John Abraham 2015-05-08
1 John Abraham 2014-05-08
1 John Abraham 2013-05-08
1 John Abraham 2012-05-08
1 John Abraham 2011-05-08
1 John Abraham 2010-05-08
------------------------------
2 Adam White 2015-05-08
2 Adam White 2014-05-08
2 Adam White 2013-05-08
2 Adam White 2012-05-08
2 Adam White 2011-05-08
2 Adam White 2010-05-08
2 Adam White 2009-05-08
2 Adam White 2008-05-08
2 Adam White 2007-05-08
2 Adam White 2006-05-08
2 Adam White 2005-05-08
2 Adam White 20004-05-08
SELECT ID, COUNT(ID)
FROM sometable
GROUP BY ID
HAVING COUNT(ID) < 10
You want count(*), not row_number():
select ID, Name, LastName, PaymentDate
from (select ID, Name, LastName, PaymentDate,
count(*) over (partition by ID) as cnt
from . . .
) t
where cnt < 10;
This displays the rows (which your question suggests is what you want). If you want only the ids, then aggregation is better:
select id
from t
group by id
having count(*) < 10;
Please try:
Select ID, Name, LastName, PaymentDate
From MyTable
Where ID in (Select ID From MyTable Group By ID Having Count(*) < 10);

Get number of different values in a column in Access

I've tried more or less all combinations of count and distinct (except the correct one :) ) in order to get the example below.
Input: table t1
NAME | FOOD
Mary | Apple
Mary | Banana
Mary | Apple
Mary | Strawberry
John | Cherries
Expected output:
NAME | FOOD
Mary | 3
John | 1
N.B. Mary has Apple in two rows but she has 3 as we have 3 different values in the column.
I only managed to get 4 in FOOD Column for her, but I need 3 :(
select a.name as NAME, a.count(name) as Food
from
(SELECT distinct NAME,Food from table)a
Start with a query which gives you unique combinations of NAME and FOOD:
SELECT DISTINCT t1.NAME, t1.FOOD
FROM t1
Then you can use that as a subquery in another where you can GROUP BY and Count:
SELECT sub.NAME, Count(*) AS [FOOD]
FROM
(
SELECT DISTINCT t1.NAME, t1.FOOD
FROM t1
) AS sub
GROUP BY sub.NAME;
select a.name, sum(a.FoodCount) from(
select distinct name,COUNT(food) as FoodCount from #t1 group by name, food ) as a group by a.name order by 2 desc

sql that identifies which account numbers have multiple agents

I dont think a count will work here, can someone help me get an sql that identifies which account numbers have multiple agents, more than two agents in the where condition.
AGENT_NAME ACCOUNT_NUMBER
Clemons, Tony 123
Cipollo, Michael 123
Jepsen, Sarah 567
Joanos, James 567
McMahon, Brian 890
Novak, Jason 437
Ralph, Melissa 197
Reitwiesner, John 221
Roman, Marlo 123
Rosenzweig, Marcie 890
Results should be something like this.
ACCOUNT_NUMBER AGENT_NAME
123 Cipollo, Michael
123 Roman, Marlo
123 Clemons, Tony
890 Rosenzweig, Marcie
890 McMahon, Brian
567 Joanos, James
567 Jepsen, Sarah
You can do this using window functions:
select t.account_number, t.agent_name
from (select t.*, min(agent_name) over (partition by account_number) as minan,
max(agent_name) over (partition by account_number) as maxan
from table t
) t
where minan <> maxan;
If you know the agent names are never duplicated, you could just do:
select t.account_number, t.agent_name
from (select t.*, count(*) over (partition by account_number) as cnt
from table t
) t
where cnt > 1;
Assuming your table name is test, this should pull all the records with duplicate ACCOUNT_NUMBER:
select * from test where ACCOUNT_NUMBER in
(select ACCOUNT_NUMBER from test
group by ACCOUNT_NUMBER having
count(ACCOUNT_NUMBER)>1)
order by ACCOUNT_NUMBER
Using count function u can get the result
CREATE TABLE #TEMP
(
AGENT_NAME VARCHAR(100),
ACCOUNT_NUMBER INT
)
INSERT INTO #TEMP
VALUES ('CLEMONS, TONY',123),
('CIPOLLO, MICHAEL',123),
('JEPSEN, SARAH',567),
('JOANOS, JAMES',567),
('MCMAHON, BRIAN',890),
('NOVAK, JASON',437),
('RALPH, MELISSA',197),
('REITWIESNER, JOHN',221),
('ROMAN, MARLO',123),
('ROSENZWEIG, MARCIE',890)
SELECT a.ACCOUNT_NUMBER,a.AGENT_NAME
FROM #TEMP A
JOIN(SELECT COUNT(1) CNT,
ACCOUNT_NUMBER
FROM #TEMP
GROUP BY ACCOUNT_NUMBER) B
ON A.ACCOUNT_NUMBER = B.ACCOUNT_NUMBER
WHERE B.CNT != 1

SQL:How to get min Quantity?

I got this problem. When i tried to summarize the min quatity of nations's products and it did not work.
I have 2 tables below
PRODUCT:
ID|NAME |NaID|Qty
-------------------
01|Fruit|JP |50
02|MEAT |AUS |10
03|MANGA|JP |80
04|BOOK |AUS |8
NATION:
NaID |NAME
-------------------
AUS |Australia
JP |Japan
I want my result like this:
ID|NAME |Name|minQty
-------------------
01|Fruit|JP |50
04|BOOK |AUS |8
and i used:
select p.id,p.name, p.NaID,n.name,min(P.Qty)as minQty
from Product p,Nation n
where p.NaID=n.NaID
group by p.id,p.name, p.NaID,n.name,p.Qty
and i got this (T_T):
ID|NAME |NaID|minQty
-------------------
01|Fruit|JP |50
02|MEAT |AUS |10
03|MANGA|JP |80
04|BOOK |AUS |8
Please,Could soneone help me? I am thinking that i am bad at SQL now.
SQL Server 2005 supports window functions, so you can do something like this:
select id,
name,
NaID,
name,
qty
from (
select p.id,
p.name,
p.NaID,
n.name,
min(P.Qty) over (partition by n.naid) as min_qty,
p.qty
from Product p
join Nation n on p.NaID=n.NaID
) t
where qty = min_qty;
If there is more than one nation with the same minimum value, you will get each of them. If you don't want that, you need to use row_number()
select id,
name,
NaID,
name,
qty
from (
select p.id,
p.name,
p.NaID,
n.name,
row_number() over (partition by n.naid order by p.qty) as rn,
p.qty
from Product p
join Nation n on p.NaID = n.NaID
) t
where rn = 1;
As your example output with only includes the NaID but not the nation's name you don't really need the the join between product and nation.
(There is no DBMS product called "SQL 2005". SQL is just a (standard) for a query language. The DBMS product you mean is called Microsoft SQL Server 2005. Or just SQL Server 2005).
In Oracle, you can use several techniques. You can use subqueries and analytic functions, but the most efficient one is to use aggregate functions MIN and FIRST.
Your tables:
SQL> create table nation (naid,name)
2 as
3 select 'AUS', 'Australia' from dual union all
4 select 'JP', 'Japan' from dual
5 /
Table created.
SQL> create table product (id,name,naid,qty)
2 as
3 select '01', 'Fruit', 'JP', 50 from dual union all
4 select '02', 'MEAT', 'AUS', 10 from dual union all
5 select '03', 'MANGA', 'JP', 80 from dual union all
6 select '04', 'BOOK', 'AUS', 8 from dual
7 /
Table created.
The query:
SQL> select max(p.id) keep (dense_rank first order by p.qty) id
2 , max(p.name) keep (dense_rank first order by p.qty) name
3 , p.naid "NaID"
4 , n.name "Nation"
5 , min(p.qty) "minQty"
6 from product p
7 inner join nation n on (p.naid = n.naid)
8 group by p.naid
9 , n.name
10 /
ID NAME NaID Nation minQty
-- ----- ---- --------- ----------
01 Fruit JP Japan 50
04 BOOK AUS Australia 8
2 rows selected.
Since you're not using Oracle, a less efficient query, but probably working in all RDBMS:
SQL> select p.id
2 , p.name
3 , p.naid
4 , n.name
5 , p.qty
6 from product p
7 inner join nation n on (p.naid = n.naid)
8 where ( p.naid, p.qty )
9 in
10 ( select p2.naid
11 , min(p2.qty)
12 from product p2
13 group by p2.naid
14 )
15 /
ID NAME NAID NAME QTY
-- ----- ---- --------- ----------
01 Fruit JP Japan 50
04 BOOK AUS Australia 8
2 rows selected.
Note that if you have several rows with the same minimum quantity per nation, all those rows will be returned, instead of just one as in the previous "Oracle"-query.
with cte as (
select *,
row_number() over (partition by Nation order by qty) as [rn]
from product
)
select * from cte where [rn] = 1

SQL Split Multiple Columns into Multiple Rows

I'm having difficulty with this problem.
I have a table with this structure:
OrderID | Manager | Worker
1 | John | Sally
2 | Tim | Kristy
I need a SQL query to get a result set like this:
OrderID | Employee
1 | John
1 | Sally
2 | Tim
2 | Kristy
Is this possible to perform?
Simplest way I can think of is (assuming you don't care if Tim is listed before or after Kristy):
SELECT OrderID, Employee = Manager FROM dbo.table
UNION ALL
SELECT OrderID, Employee = Worker FROM dbo.table
ORDER BY OrderID;
If order matters, and you want manager first always, then:
SELECT OrderID, Employee FROM
(
SELECT r = 1, OrderID, Employee = Manager
FROM dbo.Table
UNION ALL
SELECT r = 2, OrderID, Employee = Worker
FROM dbo.table
) AS x
ORDER BY OrderID, r;
You can use UNPIVOT for this.
SELECT p.OrderID, p.Employee
FROM (SELECT OrderID, Manager, Worker FROM table) a
UNPIVOT (Employee FOR FieldName IN (Manager, Worker)) p
Try something like
SELECT OrderID, Manager AS Employee, 'Manager' AS EmployeeRole From Employess
UNION ALL
SELECT OrderID, Worker AS Employee, 'Worker' AS EmployeeRole From Employess