DENSE_RANK or ROW by AccountNo not quite right - sql-server-2016

Need to create a row or ranking to repeat by account. (See below for desired result)
Tried Various DENSE_RANKS, RANK, ROW_NUMBER but keep getting incorrect values!
, DENSE_RANK() OVER ( ORDER BY a.accountNo )
, DENSE_RANK() over (partition by a.accountNo order by (SELECT NULL) ASC) dr1
, ROW_NUMBER() OVER(ORDER BY a.accountNo ASC) AS Ro
, RANK() OVER (ORDER BY a.accountNo DESC) AS xRank
So the data would appear like below:
Account | Rnk
12345 | 1
12345 | 2
12345 | 3
23456 | 1
23456 | 2
23456 | 3
23456 | 4
I know I'm missing something simple...

Assuming arbitrary order of row numbers, you can do this:
, ROW_NUMBER() OVER(PARTITION BY a.accountNo ORDER BY ##SPID) As Rn
##SPID Returns the session ID of the current user process, which means it's a fixed value within the query - so the order is actually arbitrary (and it's shorter than writing (SELECT NULL)).

Related

Numbering rows based on multiple fields changes (incluging an "invisible" one) in PostgreSQL

I had a look at the previous topics, but I cannot achieve what I want.
I have a table like this :
id status update_date
--- --- ---
A PENDING 2020-11-01
A PENDING 2020-11-02
A CONFIRMED 2020-11-03
A CONFIRMED 2020-11-04
A CONFIRMED 2020-11-05
A PENDING 2020-11-06
A PAID 2020-11-07
B CONFIRMED 2020-11-02
etc.
and I want to have this :
id status rank
--- --- ---
A PENDING 1
A CONFIRMED 2
A PENDING 3
A PAID 4
B CONFIRMED 1
etc.
meaning taking into account the update_date (and of course the status change) to sort and number the rows, but NOT having the order date in the final result
PS: as you can see, I can go back and forth from one status to the other ( PENDING -> CONFIRMED -> PENDING -> etc.) multiple times
Thanks lot !
You can address this as a gaps-and-island problem. The difference between row numbers gives you the group each record belongs to, that you can then use to aggregate:
select id, status,
row_number() over(partition by id order by min(update_date)) as rn
from (
select t.*,
row_number() over(partition by id order by update_date) rn1,
row_number() over(partition by id, status order by update_date) rn2
from mytable t
) t
group by id, status, rn1 - rn2
order by id, min(update_date)
Demo on DB Fiddle:
id | status | rn
:- | :-------- | -:
A | PENDING | 1
A | CONFIRMED | 2
A | PENDING | 3
A | PAID | 4
B | CONFIRMED | 1
step-by-step demo:db<>fiddle
SELECT
id,
status,
row_number() OVER (PARTITION BY id) -- 3
FROM (
SELECT
*,
lead(status) OVER (PARTITION BY id ORDER BY update_date) AS next -- 1
FROM
mytable
) s
WHERE status != next OR next is null -- 2
lead() window function copies the next status value to the current record
Remove all records, where the current and the next status equal (no change of status)
add a row count with row_number() window function

Removing duplicate values from sql server on condition of 2 columns

|Rownumber |OldIdassigned |commoncode |
------------------------------------------
| 1 |FLEX |Y2573F102 |
------------------------------------------
| 2 |RCL |Y2573F102 |
------------------------------------------
| 3 |FLEX |Y2573F102 |
------------------------------------------
| 4 |QGEN |N72482123 |
------------------------------------------
| 5 |QGEN |N72482123 |
------------------------------------------
| 6 |QGEN |N72482123 |
------------------------------------------
| 7 |RACE |N72482123 |
------------------------------------------
| 8 |CLB |N22717107 |
------------------------------------------
| 9 |CLB |N22717107 |
------------------------------------------
<b>| 10 |CLB |N22717107 |
I need to delete the duplicate records based on Common code and a condition that - if oldidassigned is same then delete else don't delete.
For example Y2573F102 has 3 duplicate records rows 1,2,3 .... 1,2 need not to be deleted , only 3rd row has to be deleted.
I like updatable CTEs and window functions for this purpose:
with todelete as (
select t.*,
row_number() over (partition by commoncode order by rownumber) as seqnum
from t
)
delete todelete
where seqnum > 1;
Use ROW_NUMBER() :
DELETE t
FROM (SELECT t.*, ROW_NUMBER() OVER (PARTITION BY OldIdassigned, commoncode ORDER BY rownumber) AS Seq
FROM table t
) t
WHERE t.seq > 1;
EDIT : If you want to check the duplication based on commoncode only then remove OldIdassigned from PARTITION clause :
DELETE t
FROM (SELECT t.*, ROW_NUMBER() OVER (PARTITION BY commoncode ORDER BY rownumber DESC) AS Seq
FROM table t
) t
WHERE t.seq > 1;
use window function row_number, according to your description and comments it seems you need change in partition clause
delete t
from
(select t1.*,row_number() over(partition by commoncode order by Rownumber) rn from table t1
)t where rn<>1
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=eacc0688efb534a0addee68678f323fe
Use Row_Number()
delete t from
(select *, row_number() over(partition by commoncode order by
rownumber) as rn) t
where rn<>1
Since all answers are similar (and correct), I will post one alternative way:
DELETE FROM TableA
WHERE EXISTS ( SELECT * FROM TableA AS A2
WHERE A2.commoncode = TableA.commoncode
AND A2.OldIdassigned = TableA.OldIdassigned
AND A2.Rownumber < TableA.Rownumber )

How to compare the column values of two last inserted specified rows on the same table?

I have a data set that is being updated on each operation maden by customers.
For example, I am getting a customer's last two operations by
select id,
referance
from (select id,
referance,
row_number()
over (order by time desc) as seqnum
from mytable where id=':id')
al where seqnum <= 2
where id is getting from a feature file. But now I need to compare the referance values of these two operations.
mytable:
id | name | referance | time |
-------------------------------------
11 | abc | 4589 | 09:05 |
11 | abc | 1234 | 09:04 |
10 | xyz | 0185 | 09:02 |
15 | qpr | 9564 | 08:54 |
so on...
Again, I can get the last two rows with id = 11; and, as far as all columns are not (null), it is returning "true" which is what I want literally.
But also I'd like to compare if their referances are the same or not; and, when I call the query, it has to return "true" or "false".
Thanks in advance
P.S. I actually just need a useful function or idea. I've already try to use inner join but couldnt manage it:
select table1.id,
table1.referance,
table2.id,
table2.referance
from (select id,
referance,
row_number()
over (order by time desc) as seqnum
from mytable where id=':id') table1
inner join (select id,
referance,
row_number()
over (order by time desc) as seqnum
from mytable where id=':id') table2
on table1.referance != table2.referance
al where seqnum <= 2 order by seqnum
Aggregate your current query over the id and check if the two reference values be the same or not.
select
id,
case when count(distinct reference) = 1
then 'true' else 'false' end as result
from
(
select id, reference,
row_number() over (order by time desc) as seqnum
from table
where id=':id'
) al
where seqnum <= 2
group by id;
If the distinct count of reference over the two records be 1 then it implies that they have the same value. Otherwise, we can assume that the values are different.
Why are you using row_nubmer()? You can get the last two rows as:
select top 2 id, referance
from mytable
where id=':id'
order by time desc;
You can then determine if these are the same using aggregation:
select (case when min(reference) <> max(reference) then 'false'
else 'true'
end) as is_same
from (select top 2 id, referance
from mytable
where id=':id'
order by time desc
) t;
Note: This doesn't take NULL values for reference into account, but that is easily incorporated into the logic.

Rank function for date in Oracle SQL

I have the following code for example:
SELECT id, order_day, purchase_id FROM d
customer_id and purchase_id are unique. Each customer_id could have multiple purchase_id. Assume every one has made at least 5 orders.
Now, I just want to pull the first 5 purchase IDs of each customers ID (this depends on the earliest dates of purchases). I want the result to look like this:
id | purchase_id | rank
-------------------------
A | WERFEW43 | 1
A | ERTGDSFV | 3
A | FDGRT45 | 2
A | BRTE4TEW | 4
A | DFGDV | 5
B | DSFSF | 1
B | CF345 | 2
B | SDFSDFSDFS | 4
I thought of Ranking order_day, but my knowledge is not good enough to pull this off.
select id,purchase_id, rank() over (order by order_day)
from d
you also can try dense_rank() over (order by order_day) and row_number() over (order by order_day) and choose which one will be more suitable for you
select *
from
( SELECT
id
,order_day
,purchase_id
,row_number() -- ranking
over (partition by id -- each customer
order by order_day) as rn -- based on oldest dates
FROM d
) as dt
where rn <= 5

Renumber dynamic column without update in SQL Server

I have this data
5 | Batman
5 | Superman
5 | Wonderwomen
6 | Green Lantern
6 | Green Arrow
7 | Cyborg
when I do select query, I want renumber to
1 | Batman
1 | Superman
1 | Wonderwomen
2 | Green Lantern
2 | Green Arrow
3 | Cyborg
thought?
EDIT:
thanks to vittore, so i came up with this solution. I'm not sure if my query is good.
I do ROW_NUMBER() twice. In case my sequence Id is jumping, this query will renumbering perfectly.
WITH cte AS
(
SELECT ROW_NUMBER() OVER(PARTITION BY id ORDER BY id asc) AS CteId
FROM MyTable
)
SELECT
ROW_NUMBER() OVER(PARTITION BY CteId ORDER BY CteId asc) AS RenumberColumn
FROM cte
RANK function is what you are looking for
select RANK() OVER (ORDER BY id), name
from t
Check row_number() and dense_rank() when you reading about it as well.
UPDATE: If you just use rank alone, it will give you not the values you want ( 1 1 1 2 2 3 ), but ranked values ( 1 1 1 4 4 6 )
So in order to get (1 2 3) group, rank and join:
select a.r, t.name from t
inner join (select id, rank() over (order by id asc) r
from t group by id) a
on t.id = a.id
If it's always -4, then:
Select (number-4), name
from table
But I doubt it's that simple.