根据值的范围更新行,而不更改最初不在范围内的行

Update rows based on range around values without changing rows that are not initially within range

在本地 SQLite (vs 3.29.0) 数据库中,有一个 table 有 3 列(不包括 rowID)。两个包含值,一个包含类别。我想根据一个特定类别的值周围的范围更新类别。 SET 的类别必须与确定范围的类别相同。

示例:

id Value Value2 Category
1 20 20 2
2 30 30 2
3 40 40 2
4 70 70 2
5 5 5 1
6 19 19 1
7 26 26 1
8 42 42 1
9 49 49 1
10 52 52 1
11 71 71 1
12 90 90 1
13 17 17 1

我希望将行更改为类别 2,基于 value 附近的 4 范围和 value2 附近的 2 范围。这应该只更改第 6、9 和 11 行:

id Value Value2 Category
1 20 20 2
2 30 30 2
3 40 40 2
4 70 70 2
5 5 5 1
6 19 19 2
7 26 26 1
8 42 42 2
9 49 49 1
10 52 52 1
11 71 71 2
12 90 90 1
13 17 17 1

我目前的SQL声明是:

UPDATE tablename
SET Category = 2
WHERE (Category != 2
  AND EXISTS (
        SELECT *
        FROM tablename t
        WHERE t.Category = 2
          AND tablename.Value BETWEEN t.Value - 4 AND t.Value + 4
          AND tablename.Value2 BETWEEN t.Value2 -2 AND t.Value2 +2)
          );

其中的结果是:

id Value Value2 Category
1 20 20 2
2 30 30 2
3 40 40 2
4 70 70 2
5 5 5 1
6 19 19 2
7 26 26 1
8 42 42 2
9 49 49 1
10 52 52 1
11 71 71 2
12 90 90 1
13 17 17 2

似乎正在发生的事情是,由于第 6 行更改为类别 2,第 13 行现在在类别 2 中的行的值范围内,因此也被分配为类别 2。我如何更改语句,以便 SET 仅应用于最初在范围内的值?

示例参见 demo

如果您的 SQLite 版本是 3.33.0+,您可以使用类似连接的 UPDATE...FROM 语法在 UPDATE 语句中执行自连接:

UPDATE tablename AS t1
SET Category = t2.Category
FROM tablename AS t2 
WHERE t2.Category = 2
  AND t1.Category <> t2.Category
  AND t1.Value BETWEEN t2.Value - 4 AND t2.Value + 4
  AND t1.Value2 BETWEEN t2.Value2 - 2 AND t2.Value2 + 2;

对于以前版本的 SQLite,首先创建一个临时 table,其中包含 table 和 Category = 2 的所有行:

CREATE TEMPORARY TABLE t AS 
SELECT * FROM tablename WHERE Category = 2;

然后更新 table:

UPDATE tablename
SET Category = 2
WHERE Category <> 2
  AND EXISTS (
        SELECT 1
        FROM t
        WHERE tablename.Value BETWEEN t.Value - 4 AND t.Value + 4
          AND tablename.Value2 BETWEEN t.Value2 -2 AND t.Value2 + 2
      );

参见demo