I have a fairly complex query which results in a list of Customer Ids where they have not ordered within a defined period, have no Rep assigned to them, and are business customers rather than private. This simply returns a sequential list of Ids which match the BIGINT primary keys of the matching customers, like this:
CustomerId 13 17 51 76 87 97 123 131 285 315 381 433 464 466 536 539 549 604 681 729 792 etc.
I now want to update the records with those Ids and assign to each a RepId from one of three Sales Reps:
RepName RepId Fred 25 Doris 50 Sybil 99
My problem is to assign the rep Ids. Actually doing it is easy, as per this pseudocode:
UPDATE Customers SET RepId = [CalculatedRepId] WHERE CustomerId IN (QueryResult)
The problem is calculating the relevant Id. I have considered a number of approaches including selecting RowNumber() and using the number and its relation to three to select the number, and incrementing a variable and using that to assign the relevant Id, but I cannot get it to work. I also considered splitting the results into three and assigning separately but again cannot work out how to do it and in any case that is definitely not an ideal approach – for a start with imported ordered data that would skew the results giving each rep customers in a range depending on entry order.
The desired result would be something like this:
CustomerId RepId 13 25 17 50 51 99 76 25 87 50 97 99 123 25 131 50 etc.
The order of the RepIds is not necessarily critical, but shown as I originally envisaged it the idea being to share the customers between the Reps as evenly as possible.
EDIT – the final solution
I was able to apply the list of Ids by simply adding a WHERE clause to the cte_customers proposed by @forpas:
WITH Reps AS (SELECT * FROM (VALUES (1), (3), (12)) v(RepId)), -- your RepIds cte_reps AS (SELECT RepId, ROW_NUMBER() OVER (ORDER BY RepId) - 1 rn, COUNT(*) OVER () cnt FROM Reps), cte_customers AS (SELECT *, ROW_NUMBER() OVER (ORDER BY CustomerId) - 1 rn FROM Customers WHERE Customers.CustomerId IN ( SELECT CustomerId FROM Customers WHERE Inactive = 0 AND TotalBlock = 0 AND RepId < 1 AND TypeId > 0 AND TypeId <> 10 AND NOT Exists (SELECT CustomerId FROM @TransIds t WHERE t.CustomerId = Customers.CustomerId)) ) UPDATE c SET c.RepId = r.RepId FROM cte_customers c INNER JOIN cte_reps r ON r.rn = c.rn % r.cnt;
That worked perfectly!!
Advertisement
Answer
Assuming there is a table Reps
with the RepId
s that you want to use to update the table Customers
, you can use ROW_NUMBER()
window function to join the tables in the UPDATE
statement:
WITH cte_reps AS (SELECT RepId, ROW_NUMBER() OVER (ORDER BY RepId) - 1 rn, COUNT(*) OVER () cnt FROM Reps), cte_customers AS (SELECT *, ROW_NUMBER() OVER (ORDER BY CustomerId) - 1 rn FROM Customers) UPDATE c SET c.RepId = r.RepId FROM cte_customers c INNER JOIN cte_reps r ON r.rn = c.rn % r.cnt;
See the demo.
If you want to supply only specific RepId
s use an additional CTE:
WITH reps AS (SELECT * FROM (VALUES (25), (50), (99)) v(RepId)), -- your RepIds cte_reps AS (SELECT RepId, ROW_NUMBER() OVER (ORDER BY RepId) - 1 rn, COUNT(*) OVER () cnt FROM Reps), cte_customers AS (SELECT *, ROW_NUMBER() OVER (ORDER BY CustomerId) - 1 rn FROM Customers) UPDATE c SET c.RepId = r.RepId FROM cte_customers c INNER JOIN cte_reps r ON r.rn = c.rn % r.cnt;
See the demo.