36

我有一个带有表myTable和列的 SQLite 数据库id, posX, posY. 行数不断变化(可能增加或减少)。如果我知道id每行的值和行数,我可以执行单个 SQL 查询来根据 id 更新具有不同值的所有posX和字段吗?posY

例如:

---------------------
myTable:

id   posX    posY

1      35     565
3      89     224
6      11     456
14     87     475
---------------------

SQL查询伪代码:

UPDATE myTable SET posX[id] = @arrayX[id], posY[id] = @arrayY[id] "

@arrayXand@arrayY是存储posXandposY字段的新值的数组。

例如,如果arrayX并且arrayY包含以下值:

arrayX = { 20, 30, 40, 50 }
arrayY = { 100, 200, 300, 400 }

...那么查询后的数据库应如下所示:

---------------------
myTable:

id   posX    posY

1      20     100
3      30     200
6      40     300
14     50     400
---------------------

这可能吗?我现在正在为每个查询更新一行,但随着行数的增加,它将需要数百个查询。顺便说一句,我正在 AIR 中完成所有这些工作。

4

8 回答 8

40

有几种方法可以有效地完成此任务

首先 -
如果可能,您可以对临时表进行某种批量插入。这在一定程度上取决于您的 RDBMS/宿主语言,但在最坏的情况下,这可以通过简单的动态 SQL(使用VALUES()子句),然后使用标准的 update-from-another-table 来完成。大多数系统提供用于批量加载的实用程序,尽管

第二 -
这在某种程度上也依赖于 RDBMS,您可以构建动态更新语句。在这种情况下,VALUES(...)CTE 中的子句是动态创建的:

WITH Tmp(id, px, py) AS (VALUES(id1, newsPosX1, newPosY1), 
                               (id2, newsPosX2, newPosY2),
                               ......................... ,
                               (idN, newsPosXN, newPosYN))

UPDATE TableToUpdate SET posX = (SELECT px
                                 FROM Tmp
                                 WHERE TableToUpdate.id = Tmp.id),
                         posY = (SELECT py
                                 FROM Tmp
                                 WHERE TableToUpdate.id = Tmp.id)


WHERE id IN (SELECT id
             FROM Tmp)

(根据文档,这应该是有效的 SQLite 语法,但我无法让它在小提琴中工作)

于 2012-07-19T15:47:36.650 回答
23

一种方式:SET x=CASE..END(任何 SQL)

是的,你可以这样做,但我怀疑它会提高性能,除非你的查询有很大的延迟。

如果查询是根据搜索值索引的(例如,如果id是主键),那么定位所需的元组非常非常快,并且在第一次查询之后,表将保存在内存中。

因此,在这种情况下,多个 UPDATE 并不是那么糟糕。

另一方面,如果条件需要全表扫描,更糟糕的是,表的内存影响很大,那么使用单个复杂查询会更好,即使评估 UPDATE 比简单 UPDATE 更昂贵(这得到内部优化)。

在后一种情况下,您可以这样做:

 UPDATE table SET posX=CASE
      WHEN id=id[1] THEN posX[1]
      WHEN id=id[2] THEN posX[2]
      ...
      ELSE posX END [, posY = CASE ... END]
 WHERE id IN (id[1], id[2], id[3]...);

总成本或多或少由以下公式给出:NUM_QUERIES * (COST_QUERY_SETUP + COST_QUERY_PERFORMANCE)。这样,您可以减少 NUM_QUERIES(从 N 个单独的 id 到 1),但 COST_QUERY_PERFORMANCE 上升(在 MySQL 5.28 中约为 3 倍;尚未在 MySQL 8 中测试)。

否则,我会尝试对 id 进行索引,或者修改架构。

这是一个 PHP 示例,我想我们有一个条件已经需要全表扫描,我可以将其用作键:

// Multiple update rules 
$updates = [
   "fldA='01' AND fldB='X'" => [ 'fldC' => 12, 'fldD' => 15 ],
   "fldA='02' AND fldB='X'" => [ 'fldC' => 60, 'fldD' => 15 ],
   ...
];

右侧表达式中更新的字段可以是一个或多个,必须始终相同(在这种情况下始终为 fldC 和 fldD)。可以删除此限制,但需要修改算法。

然后我可以通过循环构建单个查询:

$where = [ ];
$set   = [ ];
foreach ($updates as $when => $then) {
    $where[] = "({$when})";
    foreach ($then as $fld => $value) {
       if (!array_key_exists($fld, $set)) {
           $set[$fld] = [ ];
       }
       $set[$fld][] = $value;
    }
}

$set1 = [ ];
foreach ($set as $fld => $values) {
    $set2 = "{$fld} = CASE";
    foreach ($values as $i => $value) {
        $set2 .= " WHEN {$where[$i]} THEN {$value}";
    }
    $set2 .= ' END';
    $set1[] = $set2;
}

// Single query
$sql  = 'UPDATE table SET '
      . implode(', ', $set1)
      . ' WHERE '
      . implode(' OR ', $where);

另一种方式:ON DUPLICATE KEY UPDATE (MySQL)

在 MySQL 中,我认为您可以使用 multiple 更轻松地做到这一点INSERT ON DUPLICATE KEY UPDATE假设 id 是主键,请记住不存在的条件(“id = 777”,没有 777)将被插入到表中,如果出现错误,可能会导致错误,例如,查询中未指定其他必需的列(声明为 NOT NULL):

INSERT INTO tbl (id, posx, posy, bazinga)
     VALUES (id1, posY1, posY1, 'DELETE'),
     ...
ON DUPLICATE KEY SET posx=VALUES(posx), posy=VALUES(posy);

DELETE FROM tbl WHERE bazinga='DELETE';

上面的 'bazinga' 技巧允许删除任何可能因不存在 id 而被无意插入的行(但在其他情况下,您可能希望插入的行保留)。

例如,来自一组收集的传感器的定期更新,但某些传感器可能尚未传输:

INSERT INTO monitor (id, value)
VALUES (sensor1, value1), (sensor2, 'N/A'), ...
ON DUPLICATE KEY UPDATE value=VALUES(value), reading=NOW();

(这是一个人为的情况,可能更合理的是锁定表,将所有传感器更新为 N/A 和 NOW(),然后只插入我们拥有的那些值)。

第三种方式:CTE(PostgreSQL,不确定SQLite3)

这在概念上与 INSERT MySQL 技巧几乎相同。如所写,它适用于 PostgreSQL 9.6:

WITH updated(id, posX, posY) AS (VALUES
    (id1, posX1, posY1), 
    (id2, posX2, posY2),
    ...
)
UPDATE myTable
    SET 
    posX = updated.posY,
    posY = updated.posY
FROM updated
WHERE (myTable.id = updated.id);
于 2012-07-19T15:33:09.613 回答
6

这样的事情可能对你有用:

"UPDATE myTable SET ... ;
 UPDATE myTable SET ... ;
 UPDATE myTable SET ... ;
 UPDATE myTable SET ... ;"

如果任何 posX 或 posY 值相同,则可以将它们组合成一个查询

UPDATE myTable SET posX='39' WHERE id IN('2','3','40');
于 2012-07-19T15:25:26.163 回答
1

我实际上无法让@Clockwork-Muse 工作。但我可以使这种变化起作用:

WITH Tmp AS (SELECT * FROM (VALUES (id1, newsPosX1, newPosY1), 
                                   (id2, newsPosX2, newPosY2),
                                   ......................... ,
                                   (idN, newsPosXN, newPosYN)) d(id, px, py))

UPDATE t

SET posX = (SELECT px FROM Tmp WHERE t.id = Tmp.id),
    posY = (SELECT py FROM Tmp WHERE t.id = Tmp.id)

FROM TableToUpdate t

我希望这也适用于你!

于 2019-12-16T22:47:19.717 回答
0

使用逗号“,”

eg: 
UPDATE my_table SET rowOneValue = rowOneValue + 1, rowTwoValue  = rowTwoValue + ( (rowTwoValue / (rowTwoValue) ) + ?) * (v + 1) WHERE value = ?
于 2020-11-04T23:05:49.943 回答
0

要为 column1 更新具有不同值的表,给定 column2 上的值,可以对 SQLite 执行以下操作:

"UPDATE table SET column1=CASE WHEN column2<>'something' THEN 'val1' ELSE 'val2' END"
于 2021-04-06T20:44:47.260 回答
0

在最新版本的 SQLite(从 2018 年的 3.24.0 开始)中,您可以使用UPSERT子句。假设只有现有数据集被更新为具有唯一id列,您可以使用这种方法,这类似于@LSerni 的ON DUPLICATE建议:

INSERT INTO myTable (id, posX, posY) VALUES
  ( 1, 35, 565),
  ( 3, 89, 224),
  ( 6, 11, 456),
  (14, 87, 475)
ON CONFLICT (id) DO UPDATE SET
  posX = excluded.posX, posY = excluded.posY
于 2022-02-21T15:49:56.450 回答
-13

尝试使用“更新平板电脑集 (row='value' where id=0001'), (row='value2' where id=0002'), ...

于 2013-11-04T10:05:35.017 回答