Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CTE Row Number Partitioning

I'm struggling to get to the correct logic with regards to my CTE.

Some background information:

A task is generated in the system for all the members that has the sales role. This basically inserts multiple records into my Task table. The Task tabel has amongst other, the following columns: AssignedTo and RequestedBy - whereby AssignedTo will have each of the sale members Id's. Currently all the sales people can see the task, because no one has claimed it:

ApprovalStatusId EntityType     EntityId    AssignedTo      RequestedBy
18               | FooBar       | 281       | 4             | 6
18               | FooBar       | 281       | 9             | 6
18               | FooBar       | 281       | 17            | 6
18               | FooBar       | 281       | 26            | 6
18               | FooBar       | 281       | 39            | 6

Now that a task has been generated for each sales person, one of them, doesn't matter who, can react on the task, which will change the AssignedTo as well as the task status:

ApprovalStatusId  EntityType    EntityId    AssignedTo    RequestedBy
18               | FooBar       | 281       | 4         | 6
18               | FooBar       | 281       | 9         | 6
18               | FooBar       | 281       | 17        | 6
18               | FooBar       | 281       | 26        | 6
18               | FooBar       | 281       | 39        | 6
17               | FooBar       | 281       | 26        | 6
1                | FooBar       | 281       | 6         | 6

As you can see from the above table, user 26 reacted to the task and changed the status. Once that was done, the system redirected the task back to the original requested, which was user 6 - the status also changed to indicate this action.

Issues:

  1. Currently, only one user (Id 4) sees the task assigned to all sales users

What I've tried:

To address #1:

;WITH cte AS
(
   SELECT task.*, stat.Name AS StatusName,
         ROW_NUMBER() OVER (PARTITION BY EntityId, EntityType ORDER BY ModifiedData DESC) AS rn
   FROM dbo.Task task
   INNER JOIN dbo.ApprovalStatus stat on task.ApprovalStatusId = stat.ApprovalStatusId
)

SELECT *
FROM cte    
WHERE AssignedTo = @resourceId 
AND StatusName like 'Pending%'
AND rn = 1

So one of the issues with this piece of SQL is AND rn = 1, because this is what causes the issue raised in point 1, but if I remove it and the status changes, it won't get the newest task item. On the other-hand, it also causes this issue.

I've also tried to change the partitioning to: PARTITION BY EntityId, EntityType, AssignedTo ORDER BY ModifiedData DESC, but the problem with that is, it will produce a rn that is equal to 1 in most cases, which won't produce the correct result based on the sales group.

[Update]:

Task Table Definition: enter image description here

Sample Data:

Comments     InsertDate                     ModifiedData                CommentUserId   ApprovalStatusId    EntityType      EntityId     TenantId    AssignedTo  RequestedBy
...         |   2017-03-20 11:18:06.343|    2017-03-20 11:18:06.343|    NULL            |   18              |   FooBar     |    75      |7              |4              |42
...         |   2017-03-20 11:18:06.343|    2017-03-20 11:18:06.343|    NULL            |   18              |   FooBar     |    75      |7              |6              |42
...         |   2017-03-20 11:18:06.343|    2017-03-20 11:18:06.343|    NULL            |   18              |   FooBar     |    75      |7              |9              |42
...         |   2017-03-20 11:18:06.343|    2017-03-20 11:18:06.343|    NULL            |   18              |   FooBar     |    75      |7              |17             |42
...         |   2017-03-20 11:18:06.343|    2017-03-20 11:18:06.343|    NULL            |   18              |   FooBar     |    75      |7              |26             |42
...         |   2017-03-20 11:18:06.343|    2017-03-20 11:18:06.343|    NULL            |   18              |   FooBar     |    75      |7              |39             |42
...         |   2017-03-20 11:18:06.343|    2017-03-20 11:18:06.343|    NULL            |   18              |   FooBar     |    75      |7              |67             |42
...         |   2017-03-20 11:18:06.343|    2017-03-20 11:18:06.343|    NULL            |   18              |   FooBar     |    75      |7              |10073          |42
...         |   2017-03-20 11:18:06.343|    2017-03-20 11:18:06.343|    NULL            |   18              |   FooBar     |    75      |7              |10164          |42

Desired output:

The sales user that claimed the task should be the only one that can see it. In other words, the sales user should be able to see this record (visible in second table example) - all the other sales users should see nothing

17               | FooBar       | 281       | 26        | 6
like image 938
Richard Bailey Avatar asked Mar 20 '17 11:03

Richard Bailey


People also ask

What is ROW_NUMBER () over partition by?

The Row_Number function is used to provide consecutive numbering of the rows in the result by the order selected in the OVER clause for each partition specified in the OVER clause. It will assign the value 1 for the first row and increase the number of the subsequent rows.

How do I use a row number in CTE?

Using SQL Server ROW_NUMBER() for pagination In this example: First, the CTE used the ROW_NUMBER() function to assign every row in the result set a sequential integer. Second, the outer query returned the rows of the second page, which have the row number between 11 to 20.

What is ROW_NUMBER () in SQL?

ROW_NUMBER numbers all rows sequentially (for example 1, 2, 3, 4, 5). RANK provides the same numeric value for ties (for example 1, 2, 2, 4, 5). ROW_NUMBER is a temporary value calculated when the query is run. To persist numbers in a table, see IDENTITY Property and SEQUENCE.

How do I partition a row in SQL?

PARTITION BY It is an optional clause in the ROW_NUMBER function. It is a clause that divides the result set into partitions (groups of rows). The ROW_NUMBER() method is then applied to each partition, which assigns a separate rank number to each partition.


1 Answers

Your question would be better if you showed your desired results, but I think what you might want to do is consider using a different ranking window function other than row_number(), such as dense_rank()

;WITH cte AS
(
   SELECT task.*, stat.Name AS StatusName,
         dense_rank() OVER (PARTITION BY EntityId, EntityType ORDER BY ModifiedData DESC) AS dr
   FROM dbo.Task task
   INNER JOIN dbo.ApprovalStatus stat on task.ApprovalStatusId = stat.ApprovalStatusId
)

SELECT *
FROM cte    
WHERE AssignedTo = @resourceId 
AND StatusName like 'Pending%'
AND dr = 1

When the multiple records are initially inserted into your task table, instead of your cte returning:

+------------------+------------+----------+------------+-------------+----+
| ApprovalStatusId | EntityType | EntityId | AssignedTo | RequestedBy | rn |
+------------------+------------+----------+------------+-------------+----+
|               18 | FooBar     |     281  |          4 |           6 |  1 |
|               18 | FooBar     |     281  |          9 |           6 |  2 |
|               18 | FooBar     |     281  |         17 |           6 |  3 |
|               18 | FooBar     |     281  |         26 |           6 |  4 |
|               18 | FooBar     |     281  |         39 |           6 |  5 |
+------------------+------------+----------+------------+-------------+----+

It would return:

+------------------+------------+----------+------------+-------------+----+
| ApprovalStatusId | EntityType | EntityId | AssignedTo | RequestedBy | dr |
+------------------+------------+----------+------------+-------------+----+
|               18 | FooBar     |     281  |          4 |           6 |  1 |
|               18 | FooBar     |     281  |          9 |           6 |  1 |
|               18 | FooBar     |     281  |         17 |           6 |  1 |
|               18 | FooBar     |     281  |         26 |           6 |  1 |
|               18 | FooBar     |     281  |         39 |           6 |  1 |
+------------------+------------+----------+------------+-------------+----+

So that would solve issue #1, where only one of the users is seeing the pending task.

like image 63
SqlZim Avatar answered Sep 19 '22 21:09

SqlZim