MySQL更新列与不相关的表数据

时间:2012-07-26 20:20:17

标签: mysql sql

我在mysql中有两个这样的表

a.cardnumber (unique)
a.position (numerical 3 digits or null)
a.serial

b.serial (unique)
b.lastused

我想更新位置大于600的“a”中的任何行,并且“a.serial”为空白,其中“b.serial”中的任何序列为“b.lastused”为null或超过30天前。当序列被复制到“a.serial”时,我想用今天的日期更新“b.lastused”,所以我知道今天已经使用了相关的“b.serial”。

除了序列之外,两个表没有任何关系,b中的任何序列都可以与a中的任何卡号一起使用。

我使用我对mysql的有限知识尝试了这一点,但我不断从我的mysql桌面程序中收到错误,说我的查询中有错误:(

非常感谢任何帮助!

2 个答案:

答案 0 :(得分:2)

我在此假设您要在b.serial中为每行使用单独的a。 (这没有具体说明,但在我看来最有可能;如果错误,请随意纠正我的假设。)

我设置了一个小例子。目前还不清楚每个列的数据类型是什么,所以我使用了INT,我不确定。我使用了DATE数据类型(而不是DATETIME)用于lastused。

CREATE TABLE a (`cardnumber` VARCHAR(10) NOT NULL PRIMARY KEY, `position` INT, `serial` INT);
CREATE TABLE b (`serial` INT NOT NULL PRIMARY KEY, lastused DATE);
INSERT INTO a VALUES ('x0000',555,NULL),('x0001',700,123),('a1111',601,NULL),('a2222',602,NULL);
INSERT INTO b VALUES (100,'2012-07-15'),(101,NULL),(102,'2010-01-01'),(103,NULL),(104,NULL);
SELECT * FROM a;
SELECT * FROM b;

根据您提供的条件,具有卡号“a1111”和“a2222”的行应该更新,其他两行不应该更新(位置< = 600,已经分配了序列号。)

在运行UPDATE之前,我们首先要运行一个SELECT,它返回要更新的行以及将要分配的值。一旦我们得到它,我们就可以将它转换为多表UPDATE语句。

SELECT a.cardnumber AS `a.cardnumber`
     , a.position   AS `a.position`
     , a.serial     AS `a.serial`
     , b.serial     AS `b.serial`
     , b.lastused   AS `b.lastused`
  FROM (
         SELECT @i := @i + 1 AS i
              , aa.*
           FROM a aa
           JOIN (SELECT @i := 0) ii
          WHERE aa.position > 600   /* assuming `position` is numeric datatype */
            AND aa.serial IS NULL   /* assuming 'blank' represented by NULL    */
          ORDER BY aa.cardnumber
       ) ia
  JOIN (
         SELECT @j := @j + 1 AS j
              , bb.serial
              , bb.lastused
           FROM b bb
           JOIN (SELECT @j := 0) jj
          WHERE bb.lastused IS NULL 
             OR bb.lastused < DATE_ADD(NOW(),INTERVAL -30 DAY)
          ORDER BY bb.serial
       ) jb
    ON ia.i = jb.j
  JOIN a ON a.cardnumber = ia.cardnumber
  JOIN b ON b.serial = jb.serial

要将其转换为UPDATE,请将SELECT ... FROM替换为UPDATE,并添加SET子句以为表分配新值。

UPDATE (
         SELECT @i := @i + 1 AS i
              , aa.*
           FROM a aa
           JOIN (SELECT @i := 0) ii
          WHERE aa.position > 600
            AND aa.serial IS NULL
          ORDER BY aa.cardnumber
       ) ia
  JOIN (
         SELECT @j := @j + 1 AS j
              , bb.serial
              , bb.lastused
           FROM b bb
           JOIN (SELECT @j := 0) jj
          WHERE bb.lastused IS NULL 
             OR bb.lastused < DATE_ADD(NOW(),INTERVAL -30 DAY)
          ORDER BY bb.serial
       ) jb
    ON ia.i = jb.j 
  JOIN a ON a.cardnumber = ia.cardnumber
  JOIN b ON b.serial = jb.serial
   SET a.serial = b.serial
     , b.lastused = DATE(NOW())

-- 4 row(s) affected

您可以单独运行内联视图的查询(ia,jb),以验证这些查询是否获得了要更新的行。

从ia到a,从jb到b的连接应该在主键唯一键上。

ia和jb内联视图的目的是为这些行分配序列号,以便我们可以将它们相互匹配。

ab的联接是返回原始表格中的行,这是我们想要更新的内容。

(显然,如果serial不是INT,或者lastused是DATETIME而不是DATE,则需要进行一些调整。)

但这是我想要做的更新的一个例子(我最了解它。)


注意:此方法适用于支持子查询的MySQL版本。对于MySQL 4.0,您需要分步运行,将“ia”和“jb”内联视图(子查询)的结果存储到实际表中。然后在查询中引用这些表来代替内联视图。可以删除ii和jj子查询,并在执行引用这些变量的查询之前用单独的SELECT @i := 0, @j := 0语句替换。

答案 1 :(得分:1)

让我知道这是否有效

Update table_a
set serial = 
(
    select b.serial from table_b b
    where b.lastused = NULL 
    OR b.lastused < (current date - 30) limit 1
)
where cardnumber in 
(
select a.cardnumber
from table_a a
where a.position > 600
and a.serial = NULL
)

update table_b b
set b.lastused = current date
where b.lastused = NULL 
      OR b.lastused < (current date - 30)