让我们处理一个比将 Category5 从优先级 5 移动到优先级 1 更一般的情况。有两个(镜像)更好的例子:
- 将类别 4 移至优先级 2。
- 将类别 2 移至优先级 4。
在每种情况下,有两排不受影响(优先级为 1 和 5 的排),一排指定为移动,两排受影响为“附带损坏”。
UPDATE 语句的大部分技巧是制定一个 SELECT 语句,该语句从“之前”状态生成“之后”状态的正确数据。另一个技巧是识别控制操作的参数。这里有两个参数:
- 类别(名称) - 或等效的类别 ID 号。
- 类别的新优先级。
对于“上移”(将较低优先级的类别移至较高优先级)和“下移”(将较高优先级的类别移至较低优先级),我们可能需要单独的算法。
以下是说明之前和之后的场景:
Before Category4 to Priority 2 Category2 to Priority 4
======================== ======================== ========================
| Category | Priority | | Category | Priority | | Category | Priority |
======================== ======================== ========================
| Category1 | 1 | | Category1 | 1 | | Category1 | 1 |
| Category2 | 2 | | Category4 | 2 | | Category3 | 2 |
| Category3 | 3 | | Category2 | 3 | | Category4 | 3 |
| Category4 | 4 | | Category3 | 4 | | Category2 | 4 |
| Category5 | 5 | | Category5 | 5 | | Category5 | 5 |
======================== ======================== ========================
让我们首先尝试“向上移动”算法。
提升
使用 TDQD——测试驱动的查询开发——我们可以分阶段生成列表:
高优先级不受影响的行:
SELECT cat_id, category, priority
FROM categories
WHERE priority < 2;
低优先级不受影响的行:
SELECT cat_id, category, priority
FROM categories
WHERE priority > (SELECT priority FROM categories WHERE category = 'Category4');
被移动的行:
SELECT cat_id, category, 2 AS priority
FROM categories
WHERE category = 'Category4';
必须调整其优先级的行:
SELECT cat_id, category, priority + 1 AS priority
FROM categories
WHERE priority >= 2
AND priority < (SELECT priority FROM categories WHERE category = 'Category4');
显然,这 4 个查询可以通过 4 路 UNION 组合以生成完整的新排序。同样清楚的是,由于查询 1 和 2 选择了不变的行,我们只需要处理由查询 3 和 4 生成的发生变化的行。
因此,更改的行是:
SELECT cat_id, category, 2 AS priority
FROM categories
WHERE category = 'Category4'
UNION
SELECT cat_id, category, priority + 1 AS priority
FROM categories
WHERE priority >= 2
AND priority < (SELECT priority FROM categories WHERE category = 'Category4');
现在我们只需要派生一个 UPDATE 语句来更改优先级(不更改类别 ID 或类别名称):
UPDATE categories
SET priority =
(SELECT x.priority
FROM (SELECT cat_id, 2 AS priority
FROM categories
WHERE category = 'Category4'
UNION
SELECT cat_id, priority + 1 AS priority
FROM categories
WHERE priority >= 2
AND priority < (SELECT priority FROM categories WHERE category = 'Category4')
) AS x
WHERE x.cat_id = categories.cat_id
)
WHERE cat_id IN
(SELECT cat_id
FROM categories
WHERE category = 'Category4'
UNION
SELECT cat_id
FROM categories
WHERE priority >= 2
AND priority < (SELECT priority FROM categories WHERE category = 'Category4')
);
主 UPDATE 语句中的 WHERE 子句可以简化,产生:
UPDATE categories
SET priority =
(SELECT x.priority
FROM (SELECT cat_id, 2 AS priority
FROM categories
WHERE category = 'Category4'
UNION
SELECT cat_id, priority + 1 AS priority
FROM categories
WHERE priority >= 2
AND priority < (SELECT priority FROM categories WHERE category = 'Category4')
) AS x
WHERE x.cat_id = categories.cat_id
)
WHERE cat_id IN
(SELECT cat_id
FROM categories
WHERE priority >= 2
AND priority <= (SELECT priority FROM categories WHERE category = 'Category4')
);
当针对在 Mac OS X 10.7.4 上运行的 IBM Informix Dynamic Server 11.70.FC2 进行测试时,这产生了正确答案。
下移
'Move Down' 的并行分析产生 UPDATE 语句:
UPDATE categories
SET priority =
(SELECT x.priority
FROM (SELECT cat_id, 4 AS priority
FROM categories
WHERE category = 'Category2'
UNION
SELECT cat_id, priority - 1 AS priority
FROM categories
WHERE priority <= 4
AND priority > (SELECT priority FROM categories WHERE category = 'Category2')
) AS x
WHERE x.cat_id = categories.cat_id
)
WHERE cat_id IN
(SELECT cat_id
FROM categories
WHERE priority <= 4
AND priority >= (SELECT priority FROM categories WHERE category = 'Category2')
);
代表作
最终的答案是以这样一种方式组合这些查询,无论命名的类别是向上还是向下移动,它都可以工作(如果该类别被提名留在同一个地方,它将什么都不做)。虽然我毫不怀疑可以开发这样的查询,但我认为不值得付出努力。您可以编写一个存储过程来获取类别名称和类别的新优先级,它会执行必要的 UPDATE 语句。这可以很容易地从 UI(Web 浏览器或更紧密耦合)驱动。你上面的内容很复杂,但是可以解释。而“可解释”意味着您也有机会“可维护”。