使用 SQL 根据单条记录的更改对多条记录进行更改
Posted
技术标签:
【中文标题】使用 SQL 根据单条记录的更改对多条记录进行更改【英文标题】:Making changes to multiple records based on change of single record with SQL 【发布时间】:2017-03-30 02:53:01 【问题描述】:我有一张食物表。他们有一个“位置”字段,表示他们应该出现在列表中的顺序(listID 是他们所在的列表,我们不想在另一个列表中重新排序项目)。
+--id--+--listID--+---name---+--position--+
| 1 | 1 | cheese | 0 |
| 2 | 1 | chips | 1 |
| 3 | 1 | bacon | 2 |
| 4 | 1 | apples | 3 |
| 5 | 1 | pears | 4 |
| 6 | 1 | pie | 5 |
| 7 | 2 | carrots | 0 |
| 8,9+ | 3,4+ | ... | ... |
+------+----------+----------+------------+
我希望能够说“将梨移到筹码之前”,这涉及将梨的位置设置为位置 1,然后将中间的所有位置增加 1。这样我的结果表看起来像这样......
+--id--+--listID--+---name---+--position--+
| 1 | 1 | cheese | 0 |
| 2 | 1 | chips | 2 |
| 3 | 1 | bacon | 3 |
| 4 | 1 | apples | 4 |
| 5 | 1 | pears | 1 |
| 6 | 1 | pie | 5 |
| 7 | 2 | carrots | 0 |
| 8,9+ | 3,4+ | ... | ... |
+------+----------+----------+------------+
所以我需要做的就是SELECT name FROM mytable WHERE listID = 1 ORDER BY position
,我会按照正确的顺序得到我所有的食物。
是否可以通过单个查询来做到这一点?请记住,一条记录可能在列表中向上或向下移动,并且该表包含多个列表的记录,因此我们需要隔离 listID。
我对 SQL 的了解非常有限,所以现在我知道的唯一方法是 SELECT id, position FROM mytable WHERE listID = 1 AND position BETWEEN 1 AND 5
然后我可以使用 javascript (node.js) 将位置 5 更改为 1,并增加所有其他位置 +1 .然后更新我刚刚更改的所有记录。
只是每当我尝试阅读 SQL 的东西时,每个人都在说要避免多个查询并避免进行同步编码之类的东西。
谢谢
【问题讨论】:
你想要什么结果.. @reds 当我想将“Pears”移到“Chips”之前,让我的桌子看起来像我的问题中的第二个示例 您想让梨子成为您的第二行记录吗?你是这个意思吗? @reds - “Pears”不一定需要移动记录。因为两者之间可能有任意数量的不相关记录。我只需要更改“位置”字段,并且任何相关记录的“位置”字段相应增加。 @reds 我稍微更新了我的问题,希望我现在要问的内容更清楚一点。 【参考方案1】:这需要更新许多记录的复杂查询。但是,对数据的微小更改可能会改变事情,因此可以通过仅修改一条记录的简单查询来实现。
UPDATE my_table set position = position*10;
在过去,许多系统上的 BASIC 编程语言都有行号,它鼓励使用 spagetti 代码。许多人写了GOTO line_number
,而不是函数。如果您按顺序对行进行编号并且必须添加或删除几行,则会出现真正的麻烦。人们是如何绕过它的?通过增加 10 行!这就是我们在这里所做的。
所以你想让梨子成为第二个项目?
UPDATE my_table set position = 15 WHERE listId=1 AND name = 'Pears'
担心多次重新排序后项目之间的间隙最终会消失?不怕就行了
UPDATE my_table set position = position*10;
不时。
【讨论】:
这个答案很聪明,几乎回答了我的问题。但是,如果我现在尝试在梨之前移动一些东西怎么办?有没有简单的方法来设置 position = 12.5 或其他什么?我觉得这开始变得同样复杂。 您不需要输入小数。您可以将其设置为 12。仍然只需要一个查询(借助连接)来进行更新。 您能告诉我实现这一目标的查询吗?是否需要先检查位置 15 是否存在?然后是 12,然后是 11,我怎么知道什么时候再次设置 position * 10?【参考方案2】:我基本上已经解决了我的问题。所以我决定在这里给出一个答案,以防有人觉得它有帮助。 我可以在 SQL 中使用 CASE 语句。此外,通过预先使用 Javascript 构建我的 SQL 查询,我可以更改多条记录。
这构建了我的 SQL 查询:
var sql;
var incrementDirection = (startPos > endPos)? 1 : -1;
sql = "UPDATE mytable SET position = CASE WHEN position = "+startPos+" THEN "+endPos;
for(var i=endPos; i!=startPos; i+=incrementDirection)
sql += " WHEN position = "+i+" THEN "+(i+incrementDirection);
sql += " ELSE position END WHERE listID = "+listID;
如果我想把 Pears 移到 Chips 之前。我可以设置:
startPos = 4;
endPos = 1;
listID = 1;
我的代码将生成如下所示的 SQL 语句:
UPDATE mytable
SET position = CASE
WHEN position = 4 THEN 1
WHEN position = 1 THEN 2
WHEN position = 2 THEN 3
WHEN position = 3 THEN 4
ELSE position
END
WHERE listID = 1
我运行该代码,我的最终表格将如下所示:
+--id--+--listID--+---name---+--position--+
| 1 | 1 | cheese | 0 |
| 2 | 1 | chips | 2 |
| 3 | 1 | bacon | 3 |
| 4 | 1 | apples | 4 |
| 5 | 1 | pears | 1 |
| 6 | 1 | pie | 5 |
| 7 | 2 | carrots | 0 |
| 8,9+ | 3,4+ | ... | ... |
+------+----------+----------+------------+
之后,我所要做的就是运行SELECT name FROM mytable WHERE listID = 1 ORDER BY position
,输出将如下::
cheese
pears
chips
bacon
apples
pie
【讨论】:
【参考方案3】:我不认为这可以在少于两个查询中方便地完成,这没关系,应该尽可能少的查询,但不是不惜一切代价。这两个查询就像(根据你自己写的)
UPDATE mytable SET position = 1 WHERE listID = 1 AND name = 'pears';
UPDATE mytable SET position = position + 1 WHERE listID = 1 AND position BETWEEN 2 AND 4;
【讨论】:
我喜欢这个。它比我想出的解决方案更简单。您是否知道构建一个像我这样的更复杂的 SQL 是否有任何优点/缺点,而不是像您的 2 个更简单的查询? 我更喜欢它的主要原因是清晰,这意味着未来更好的可维护性。我并不完全了解 mysql 的内部结构,但我怀疑它可能比您使用 CASE 的解决方案执行得更好,因为像 UPDATE 这样的标准操作更侧重于并因此进行了优化。如果您没有数百万条记录,这可能没有实际意义。以上是关于使用 SQL 根据单条记录的更改对多条记录进行更改的主要内容,如果未能解决你的问题,请参考以下文章
SQL 查询:如果满足搜索条件,则应返回单条记录,否则返回多条记录