Sort rows for duplicate ID numbers - sql

I have been trying to find ways to sort rows based on duplicate ID numbers per below but have been unsuccessful.
ID SortOrder PersonID
1 0 100
2 1 100
3 0 200
4 0 200
5 1 200
I am trying to sort the sortOrder column so the number 1 will display at the top for every PersonID. So the end results are like below:
ID SortOrder PersonID
1 1 100
2 0 100
3 1 200
4 0 200
5 0 200
Any Suggestions? Thanks!

You can use CASE like this.
ORDER BY PersonID,CASE WHEN SortOrder = 1 THEN 1 ELSE 2 END
You can change the CASE appropriately, if you want to change how following rows after SortOrder = 1 should be ordered for the same PersonID.
Like ORDER BY PersonID,CASE SortOrder WHEN 1 THEN 1 WHEN 0 THEN 2 ELSE 3 END

One other solution:
select id,SortOrder,PersonId from
(select id,SortOrder,PersonId,row_number() over (partition by PersonId order by SortOrder desc) as rn from table) A
order by A.rn asc

Based on your comments regarding wanting the ID value to stay in constant order, you're likely looking for something like this:
Select Row_Number() Over (Order By PersonID Asc, SortOrder Desc) As ID,
SortOrder,
PersonID
From YourTable
Order By PersonID Asc, SortOrder Desc
But, I'd caution against naming the ROW_NUMBER field ID, as it can be misleading since it's not actually the unique identifier for the stored record.

Related

How to apply DENSE_RANK() to records ordered by multiple columns?

I'm writing a SQL stored procedure that for each AliasName it will retrieve me MasterNames it's associated with in the NameAssociations table, which contains AliasNameId, MasterNameId, and MatchRank, and StatusCode columns. I'll be using the stored procedure for server-side paging the data in C#, so I'd like the startRow endRow part to stay the same.
CREATE PROCEDURE [dbo].[NameAssociationsGetNameAssociations]
#StartRow INT = 1,
#EndRow INT = 1
AS
WITH result_set AS (
SELECT DENSE_RANK() OVER (ORDER BY an.Id) AS rowNum,
an.Id as AliasId, an.AliasName,
mn.Id as MasterId, mn.MasterName, mn.CodeId, mn.RowUpdateVersion AS ConcurrencyToken, na.MatchRank
FROM
NameAssociations na
INNER JOIN AliasNames an
ON na.AliasNameId = an.Id
INNER JOIN MasterNames mn
ON na.MasterNameId = mn.Id
WHERE
na.StatusCode = 0
)
SELECT
rowNum,
AliasId as Id, AliasName,
MasterId as Id, MasterName, CodeId, ConcurrencyToken, MatchRank
FROM
result_set
WHERE
rowNum BETWEEN #StartRow AND #EndRow
This procedure works and retrieves rows numbered correctly:
rowNum
Id
AliasName
Id
MasterName
CodeId
ConcurrencyToken
MatchRank
1
5
BName
34
SomeName1
2
0x0000021
1
2
6
DName
21
SomeName2
3
0x0000003
2
2
6
DName
2
SomeName3
1
0x00000A2
1
2
6
DName
40
SomeName4
1
0x00000B4
3
3
7
AName
11
SomeName5
1
0x000005B
1
So basically, I get a list of MasterNames for every AliasName.
But the rows are not ordered in the way I would like them to be, which is by AliasName ASC, Id (of alias) ASC, and MatchRank ASC, which would look like this:
rowNum
Id
AliasName
Id
MasterName
CodeId
ConcurrencyToken
MatchRank
1
7
AName
11
SomeName5
1
0x000005B
1
2
5
BName
34
SomeName1
2
0x0000021
1
3
6
DName
2
SomeName3
1
0x00000A2
1
3
6
DName
21
SomeName2
3
0x0000003
2
3
6
DName
40
SomeName4
1
0x00000B4
3
You can see the names of aliases are ordered alphabetically, and then the associated masters were reordered so the MatchRank is in ASC order. All of this while the rowNum is correct, which is what I've been having trouble with attaining.
I've tried doing PARTITION BY an.Id ORDER BY an.AliasName ASC, an.Id ASC, na.MatchRank ASC, but I must be misunderstanding what ORDER BY does in this case because the results come out wrong. The results look like it ordered the records only by MatchRank ASC and partitioned by it. I'm expecting it to order the data by those three columns, and then partition it by the an.Id.
How can I write the query so that the output result looks like the second example of table? I hope this was all clear.
Ignoring what looks like a transcription/obfuscation error in your script that actually renders your SQL as invalid (dense_rank must have an order by), I think your solution is actually pretty simple.
The dense_rank function uses the values in the order by to determine which records get the same ranking value, so if you want to sort the output by the AliasName, but also want to make sure records with the same AliasName but different id values (not sure why this would be possible, but defensive coding is generally a good practice), you just need to include both values in your order by, in the order that you want to apply sorting. In your case this looks like the following minimal solution:
Query
declare #t table(id int, AliasName varchar(10), val int);
insert into #t values (1,'b',1),(2,'c',2),(2,'c',3),(2,'cc',4),(22,'c',5),(3,'a',6);
select *
,dense_rank() over (order by id) as rid
,dense_rank() over (order by AliasName, id) as rAliasName
from #t;
Output
id
AliasName
val
rid
rAliasName
3
a
6
3
1
1
b
1
1
2
2
c
2
2
3
2
c
3
2
3
22
c
5
4
4
2
cc
4
2
5

Updating Rows in a normalized table

normalized table
ID SEQ Type Value Flag
1 1 a 100 -
1 2 a 200 -
1 3 a 250 -
1 4 b 200 -
2 1 a 150 -
2 2 b 100 -
2 3 b 200 -
How do I write a single update statement such that the resulting table is populated as follows
ID SEQ Type Value Flag
1 1 a 100 valid
1 2 a 200 repeat
1 3 a 250 repeat
1 4 b 200 valid
2 1 a 150 valid
2 2 b 100 valid
2 3 b 200 repeat
Edit: included seq column
only the first occurence of the value for a type for a ID group should have the valid flag
should it be written as two separate update statements?
can someone clarify me?
Much appreciated
Populate the table first using row_number() and then update the table.
Option 1:
select
Id,
Type,
Value,
null as Flag,
row_number() over (partition by ID, Type order by SEQ) as rnk
from yourTable
then you can use update
update yourTable
set flag = case
when rnk = 1 then 'valid'
else 'repeat'
end
Option 2:
You may be able to do without using update statement as following
select
Id,
SEQ,
Type,
Value,
case
when rnk = 1 then 'valid'
else 'repeat'
end as flag
from
(
select
Id,
SEQ,
Type,
Value,
row_number() over (partition by ID, Type order by SEQ) as rnk
from yourTable
) val

sql - select single ID for each group with the lowest value

Consider the following table:
ID GroupId Rank
1 1 1
2 1 2
3 1 1
4 2 10
5 2 1
6 3 1
7 4 5
I need an sql (for MS-SQL) select query selecting a single Id for each group with the lowest rank. Each group needs to only return a single ID, even if there are two with the same rank (as 1 and 2 do in the above table). I've tried to select the min value, but the requirement that only one be returned, and the value to be returned is the ID column, is throwing me.
Does anyone know how to do this?
Use row_number():
select t.*
from (select t.*,
row_number() over (partition by groupid order by rank) as seqnum
from t
) t
where seqnum = 1;

SQL MAX(column) With Additional Criteria

I have a single table, where I want to return a list of the MAX(id) GROUPed by another identifier. However I have a third column that, when it meets a certain criteria, "trumps" rows that don't meet that criteria.
Probably easier to explain with an example. Sample table has:
UniqueId (int)
GroupId (int)
IsPriority (bit)
Raw data:
UniqueId GroupId IsPriority
-----------------------------------
1 1 F
2 1 F
3 1 F
4 1 F
5 1 F
6 2 T
7 2 T
8 2 F
9 2 F
10 2 F
So, because no row in groupId 1 has IsPriority set, we return the highest UniqueId (5). Since groupId 2 has rows with IsPriority set, we return the highest UniqueId with that value (7).
So output would be:
5
7
I can think of ways to brute force this, but I am looking to see if I can do this in a single query.
SQL Fiddle Demo
WITH T
AS (SELECT *,
ROW_NUMBER() OVER (PARTITION BY GroupId
ORDER BY IsPriority DESC, UniqueId DESC ) AS RN
FROM YourTable)
SELECT UniqueId,
GroupId,
IsPriority
FROM T
WHERE RN = 1

Return results where first entry is 1 and all subsequent rows are 0

I m working on weird SQL query
Patient_ID Count order_no
1 1 1
2 1 2
2 0 3
2 0 4
3 1 5
3 0 6
where I need to count the patient as above, for every new patient , the count column is 1.
If repeated , the below entry it should be 0
I m confused how should make that work in SQL
In order to make the first entry 1 and all subsuqent entries 0, I believe you need a ranking with partition by the order number. Please checkout the sqlfiddle below to test results.
http://www.sqlfiddle.com/#!3/4e2e2/17/0
SELECT
patient_id
,CASE WHEN r.rank = 1
THEN 1
ELSE 0
END
, order_number
FROM
(
SELECT
order_number
,patient_id
,ROW_NUMBER() OVER (PARTITION BY patient_id ORDER BY order_number)[rank]
FROM
PatientTable
)r