I don't think I fully understand what you are trying to achieve here, but here's something to start a discussion:
--Create sample data
IF OBJECT_ID('tempdb..#temp') IS NOT NULL
DROP TABLE #temp;
CREATE TABLE #temp (ID INT, NAME VARCHAR(50), [VALUE] INT);
INSERT INTO #temp SELECT 1, 'a', 10;
INSERT INTO #temp SELECT 2, 'b', NULL;
INSERT INTO #temp SELECT 3, 'c', NULL;
INSERT INTO #temp SELECT 4, 'd', NULL;
INSERT INTO #temp SELECT 5, 'a', 10;
INSERT INTO #temp SELECT 6, 'b', NULL;
INSERT INTO #temp SELECT 7, 'c', NULL;
INSERT INTO #temp SELECT 8, 'd', NULL;
INSERT INTO #temp SELECT 9, 'a', NULL;
All this does is to swap a's and d's around if there is a value in the a's:
WITH Best AS (
SELECT NAME, MAX([VALUE]) AS MAXVALUE FROM #temp GROUP BY NAME)
UPDATE
t
SET
[VALUE] =
CASE
WHEN t.NAME = 'a' AND b.MAXVALUE IS NOT NULL THEN NULL
WHEN t.NAME = 'd' AND b.MAXVALUE IS NOT NULL THEN b.MAXVALUE
END
FROM
#temp t
CROSS JOIN Best b
WHERE
t.NAME IN ('a', 'd')
AND b.NAME = 'a';
SELECT * FROM #temp;
On completion I can see that all the a values have moved to the d rows. That doesn't cope with b to c, c to d, etc. though. Is this anything like what you want?
Now it looks like you actually need each group of four rows to be handled separately, so this might work better?
WITH Groups AS (
SELECT ID, (ID - 1) / 4 AS group_id, NAME, [VALUE] FROM #temp),
Best AS (
SELECT group_id, NAME, MAX([VALUE]) AS MAXVALUE FROM Groups GROUP BY group_id, NAME)
UPDATE
t
SET
[VALUE] =
CASE
WHEN t.NAME = 'a' AND b.MAXVALUE IS NOT NULL THEN NULL
WHEN t.NAME = 'd' AND b.MAXVALUE IS NOT NULL THEN b.MAXVALUE
END
FROM
#temp t
INNER JOIN Groups g ON g.ID = t.ID
INNER JOIN Best b ON b.group_id = g.group_id
WHERE
t.NAME IN ('a', 'd')
AND b.NAME = 'a';
Here's a new query for you, now I finally think I understand what this is supposed to do. First here's my understanding of what you actually want:
- you have data that is ordered by an ID;
- the data is organised into groups of 4, starting at "a", then "b", then "c", then "d";
- for each group of 4 items find the single item that has a non-NULL value;
- move this one forward, i.e. if the a value is there move it to b, etc.
- the d value moves back round to a again;
- each group is independent of the others.
I created a slightly different set of data to show this working in practice:
--Create sample data
IF OBJECT_ID('tempdb..#temp') IS NOT NULL
DROP TABLE #temp;
CREATE TABLE #temp (ID INT, NAME VARCHAR(50), [VALUE] INT);
INSERT INTO #temp SELECT 1, 'a', 10;
INSERT INTO #temp SELECT 2, 'b', NULL;
INSERT INTO #temp SELECT 3, 'c', NULL;
INSERT INTO #temp SELECT 4, 'd', NULL;
INSERT INTO #temp SELECT 5, 'a', NULL;
INSERT INTO #temp SELECT 6, 'b', NULL;
INSERT INTO #temp SELECT 7, 'c', NULL;
INSERT INTO #temp SELECT 8, 'd', 5;
INSERT INTO #temp SELECT 9, 'a', NULL;
INSERT INTO #temp SELECT 10, 'b', 90;
INSERT INTO #temp SELECT 11, 'c', NULL;
INSERT INTO #temp SELECT 12, 'd', NULL;
...and here's my amended query:
WITH Groups AS (
SELECT
(ID - 1) / 4 AS group_id,
ID,
NAME,
[VALUE]
FROM
#temp),
Best AS (
SELECT
group_id,
ID,
NAME,
[VALUE]
FROM
Groups
WHERE
[VALUE] IS NOT NULL)
UPDATE
t
SET
[VALUE] =
CASE
WHEN t.NAME = 'a' AND b.NAME = 'a' THEN NULL
WHEN t.NAME = 'a' AND b.NAME = 'd' THEN b.[VALUE]
WHEN t.NAME = 'b' AND b.NAME = 'b' THEN NULL
WHEN t.NAME = 'b' AND b.NAME = 'a' THEN b.[VALUE]
WHEN t.NAME = 'c' AND b.NAME = 'c' THEN NULL
WHEN t.NAME = 'c' AND b.NAME = 'b' THEN b.[VALUE]
WHEN t.NAME = 'd' AND b.NAME = 'd' THEN NULL
WHEN t.NAME = 'd' AND b.NAME = 'c' THEN b.[VALUE]
END
FROM
#temp t
INNER JOIN Groups g ON g.ID = t.ID
INNER JOIN Best b ON b.group_id = g.group_id;
So my data starts off like this:
ID NAME VALUE
1 a 10
2 b NULL
3 c NULL
4 d NULL
5 a NULL
6 b NULL
7 c NULL
8 d 5
9 a NULL
10 b 90
11 c NULL
12 d NULL
...and when I run the UPDATE query:
ID NAME VALUE
1 a NULL
2 b 10
3 c NULL
4 d NULL
5 a 5
6 b NULL
7 c NULL
8 d NULL
9 a NULL
10 b NULL
11 c 90
12 d NULL
If I run the query again then everything moves one more step:
ID NAME VALUE
1 a NULL
2 b NULL
3 c 10
4 d NULL
5 a NULL
6 b 5
7 c NULL
8 d NULL
9 a NULL
10 b NULL
11 c NULL
12 d 90
If this still isn't what you want then either show me what you think the values should be after the first/ second iteration, or come up with a new before -> after example in your question.
Hopefully this will be the final attempt :D
WITH Groups AS (
SELECT
(ID - 1) / 4 AS group_id,
ID,
NAME,
[VALUE]
FROM
#temp),
Best AS (
SELECT
group_id,
ID,
NAME,
[VALUE]
FROM
Groups
WHERE
[VALUE] IS NOT NULL)
UPDATE
t
SET
[VALUE] =
CASE
--a to d
WHEN t.NAME = 'd' AND b.NAME = 'a' THEN b.[VALUE]
--b to c
WHEN t.NAME = 'c' AND b.NAME = 'b' THEN b.[VALUE]
--c to d (seems wrong?)
WHEN t.NAME = 'd' AND b.NAME = 'c' THEN b.[VALUE]
--d to a
WHEN t.NAME = 'a' AND b.NAME = 'd' THEN b.[VALUE]
--Set the moving item to NULL
WHEN t.NAME = b.NAME THEN NULL
END
FROM
#temp t
INNER JOIN Groups g ON g.ID = t.ID
INNER JOIN Best b ON b.group_id = g.group_id;