function that allows grouping of rows - sql

I'm using SQL Server Management Studio 2012. I have a similar looking output from a query shown below. I want to eliminate someone from the query who has 2 contracts.
Select
Row_Number() over (partition by ID ORDER BY ContractypeDescription DESC) as [Row_Number],
Name,
ContractDescription,
Role
From table
Output
Row_Number ID Name Contract Description Role
1 1234 Mike FullTime Admin
2 1234 Mike Temp Manager
1 5678 Dave FullTime Admin
1 9785 Liz FullTime Admin
What I would like to see
Row_Number ID Name Contract Description Role
1 5678 Dave FullTime Admin
1 9785 Liz FullTime Admin
Is there a function rather than Row_Number that allows you to group rows together so I can then use something like 'where Row_Number not like 1 and 2'?

You can use HAVING as
SELECT ID,
MAX(Name) Name,
MAX(ContractDescription) ContractDescription,
MAX(Role) Role
FROM t
GROUP BY ID
HAVING COUNT(*) = 1;
Demo

Try this:
select * from (
Select
Count(*) over (partition by ID ) as [Row_Number],
Name,
ContractDescription,
Role
From table
)t where [Row_Number] = 1

You can check this option-
SELECT *
FROM table
WHERE ID IN
(
SELECT ID
FROM table
GROUP BY ID
HAVING COUNT(*) = 1
)

You can use a CTE to get all the ids of people who got only one contract and then just join the result of the CTE with your table.
;with cte as (
select
id
,COUNT(id) as no
from #tbl
group by id
having COUNT(id) = 1
)
select
t.id
,t.name
,t.ContractDescription
,t.role
from #tbl t
inner join cte
on t.id = cte.id

Basically you need those record who have exactly one contract.
Just extend your script, (My script is not tested)
;with CTE as
(
Select
Row_Number() over (partition by ID ORDER BY ContractypeDescription DESC) as [Row_Number],
Name,
ContractDescription,
Role
From table
)
select * from CTE c where [Row_Number]=1
and not exists(select 1 from CTE c1 where c.id=c1.id and c1.[Row_Number]>1 )

Is there a function rather than Row_Number that allows you to group
rows together so I can then use something like 'where Row_Number not
like 1 and 2'?
You can use a windowed COUNT(). The key is the OVER() clause.
;WITH WindowedCount AS
(
SELECT
T.*,
WindowCount = COUNT(1) OVER (PARTITION BY T.ID)
FROM
YourTable AS T
)
DELETE W FROM
WindowedCount AS W
WHERE
W.WindowCount > 1
The COUNT() will count the amount of rows for each different ID, so if the same ID appears in 2 or more rows, those rows will be deleted.

Related

How to group and pick only certain values based on a field using select query SQL

I have a table as follow
ID
ORDERNO
1
123
1
123
2
456
2
456
During every select query done via application using JDBC, only the grouped records based on ORDERNO should be picked.
That means, for example, during first select query only details related to ID = 1, but we cannot specify the ID number in where clause because we do not know how many number of IDs will be there in future. So the query should yield only one set of records; application will delete those records after picking, hence next select query will result in picking other set of records. How to achieve it?
You can use TOP WITH TIES for this
SELECT TOP (1) WITH TIES
t.ID,
t.ORDERNO
FROM YourTable t
ORDER BY
t.ID;
If you want to select and delete at the same time you could delete using an OUTPUT clause
WITH cte AS (
SELECT TOP (1) WITH TIES
t.ID,
t.ORDERNO
FROM YourTable t
ORDER BY
t.ID
)
DELETE cte
OUTPUT deleted.*;
As one option you could select on the MIN(ID) like:
SELECT *
FROM yourtable
WHERE ID = (SELECT MIN(ID) FROM yourtable);
You could also use window functions to do this:
SELECT ID, ORDERNO
FROM
(
SELECT ID, ORDERNO
DENSE_RANK() OVER (ORDER BY ID ASC) AS dr
FROM yourtable
)dt
WHERE dr = 1;
order your rows and select top n number of rows that you want :
select top (1) with ties ID, ORDERNO
from tablename
order by ID asc

How to display duplicates in SQL only if another column is different?

So say I have this table:
Name
Role
First
Science
First
Math
First
Science
First
Math
Second
Science
Third
Math
Third
Math
I want to display a column of duplicates for Name/Role ONLY if role is different in each group. So the final result should be like this:
Name
Role
First
Science
First
Math
This is the only person that has a different role for the same name (no matter how many times that specific combination is duplicated). That's why even though Third/Math is also duplicated, it doesn't matter because it's the same combination.
I tried doing a CTE as follows:
;with cte as (
Select Name, Role, ROW_NUMBER() over (partition by name order by name) as 'rownum1'
from U.Users
group by u.name, u.role)
so then select * from cte where rownum > 1 gets me my names of people that have this issue but it doesn't display the duplicate roles for that user. Not sure how I should approach it differently?
If I join the CTE table to the original Users table, I also get the single entries.
You can take advantage of the fact that window functions are applied after aggregation:
select name, role
from (
select name, role, count(1) over (partition by name) c
from user_role
group by name, role
) r
where c > 1
https://www.db-fiddle.com/f/vzRDgBXwYp3VpgNyfn9qzL/0
You can try something like this:
WITH cte1 as (
SELECT distinct *
FROM
table1
),
cte2 as
(
Select Name, Role, ROW_NUMBER() over (partition by name order by name) as rnk
from cte1 u
group by u.name, u.role
)
SELECT * FROM cte2
where name in
(select name
from cte2
WHERE rnk > 1
group by name
)
I used a distinct function to remove any duplicates, then use the ROW_NUMBER() like you to find Names with multiple rows.
db fiddle link
So after I posted question I tried this which isn't as elegant as Kurt's answer but did also work:
;with cte as (select name, role, row_number() over (partition by name order by name) rownum
from user_role
group by name, role)
select distinct user_role.name, user_role.role from user_role
join cte on cte.name=user_role.name and cte.role=user_role.role
where user_role.name in (select name from cte where rownum =2)
https://www.db-fiddle.com/f/vzRDgBXwYp3VpgNyfn9qzL/2

Just another SQL case (GROUP BY)

I'm stuck on an SQL problem that I don't know how to solve.
Let's say I have a table like this (concerning estimations on house prices):
estimationID | estimationDate | userID | cityID
1 | '2020-01-01' | 123456 | 987654
2 | '2020-12-01' | 135790 | 975310
...
With estimationDate being the date when the estimation was made, userID the ID of the user who made the estimation and cityID the ID of the city where the estimation was made.
I need to get the maximum number of estimations made by one user (I don't care which one, I don't need an ID) for each city.
Something like
SELECT cityID,*maximum number of estimations made by one user from this city* FROM estimationsTable GROUP BY cityID
Any idea?
Step by step:
Get the number of estimations per user and city.
Get the maximum of these numbers per city.
The query:
select cityid, max(cnt)
from
(
select cityid, userid, count(*) as cnt
from estimationstable
group by cityid, userid
) counted
group by cityid
order by cityid;
try like below
with cte as (
select userid,cityid,count(*) as cnt
from table_name group by userid,cityid
)
, cte2 as (
select *,
row_number() over(partition by cityid order by cnt desc) rn
from cte
) select * from cte2 where rn=1
sol 1:
SELECT id, MAX(maximum_number_of_estimations)
FROM (SELECT id,COUNT(*) AS maximum_number_of_estimations
FROM TABLE x)group by id as final_query
sol2:
use order by Count DESC with group by`
something like this should work
the idea is you count all the occurrences in the inner query with the group by on your id and another query to get the max of it OR you use ORDER BY [Field] DESC
with GROUP BY which will automatically put the highest ones on the top
In BigQuery, I think you can do this without a subquery:
select distinct cityid,
(array_agg(userid order by count(*) desc, userid))[ordinal(1)] as userid,
max(count(*)) over (order by count(*) desc) as cnt
from estimationstable
group by cityid, userid

How to find duplicate in one to one mappings in oracle using sql?

I have table as:
ID PAYOR_NAME
---------- ------------
4 AETNAU
4 AETNA
2 UMR
3 CIGNA
1 METLIFE
Id needs to be one to one mapping with payor_name.But Id 4 is associated with multiple payor_name ,so it is considered as duplicate. So,I tried to find the duplicate by using:
select id, count(*) duplic_data
from (
select distinct id, payor_name
from offc.payor_collec
order by id) t1
group by id;
It is giving me the duplicate Id,But i am wondering is there any also way where we can find duplicates in one to one mappings?
There are multiple ways of finding it:
using exists
using group by and having
using analytical function, if you want all column data for that duplicate values (same as exists)
-
select id, payor_name, cnt as count_
from (
select id, payor_name,
count(1) over (partition by id) as cnt
from offc.payor_collec) t1
Where cnt > 1;
It will give you following result:
ID PAYOR_NAME COUNT_
---------- ------------ -------
4 AETNAU 2
4 AETNA 2
Cheers!!
How about simply using exists:
select pc.id, pc.payor_name, count(*)
from offc.payor_collec pc
where exists (select 1
from offc.payor_collec pc2
where pc2.id = pc.id and pc2.payor_name <> pc.payor_name
)
group by pc.id, pc.payor_name
order by pc.id, count(*) desc;
This also orders by the most frequent value, which might be helpful in figuring out the best name.
This could be a way:
select ID, count(*)
from offc.payor_collec
group by ID
having count(distinct PAYOR_NAME) > 1
Another option would be using a subquery containing having clause with having count(ID)>1
select *
from payor_collec
where ID in
( select ID
from payor_collec t
group by ID
having count(ID)>1 )

Get all items with min values SQL Server

Here's what the table is like:
----------------------------------
EmployeeId Tasks_Count
1 1
2 1
3 2
4 1
5 3
I need a query to get all employees with min tasks count. Result should be like this:
---------------
EmployeeId
1
2
4
The problem is that i using a subquery to count tasks. Here's my code
SELECT *
FROM (SELECT EmployeeId,
COUNT(*) AS Tasks_count
FROM Tasks
INNER JOIN Status ON Tasks.StatusId=Status.Id
WHERE Status.Name != 'Closed'
GROUP BY EmployeeId
ORDER BY Tasks_count DESC) AS Employee_not_closed
WHERE Tasks_count IN (SELECT MIN(Tasks_count)
FROM Employee_not_closed)
Use FETCH FIRST WITH TIES:
select EmployeeId
from tablename
order by Tasks_Count
fetch first 1 row with ties
You can try below -
select * from tablename
where Tasks_Count in (select min(Tasks_Count) from tablename)
It can also be done using RANK() function like following.
;with cte as
(
select Employeeid, rank() over( order by Tasks_Count) rn
from #table
)
select * from cte where rn=1
You Can use the below code i have tested the code and its working fine.
select EmployeeId from StackOverFlow_3 where Tasks_Count in(select min(Tasks_Count) from StackOverFlow_3)
You can use a join on subquery
select m.EmployeeId
from my_table m
inner join
(
select min(task_count) min_task
from my_table
) t on t.min_task = m.task_count