使用 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 查询:如果满足搜索条件,则应返回单条记录,否则返回多条记录

如果是单条记录,不适用where条件;如果是多条记录,应用where条件,返回单条记录

SQL Server:根据应用程序用户记录数据库更改

使用JDBC一次插入多条记录(以MySQL为例)

从可能产生多条记录的连接表中获取单条记录

在 SQL Server 中,您可以根据计算插入多条记录吗?