使用来自另一个 table 的列动态更新 table

Dynamically update table with column from another table

我有一个这样的 table 客户:

CREATE TABLE tbl_customer (
  id INTEGER,
  name VARCHAR(16),
  voucher VARCHAR(16)
);

和这样的代金券 table:

CREATE TABLE tbl_voucher (
  id INTEGER,
  code VARCHAR(16)
);

现在假设客户 table 总是填写了 id 和 name 的行,但是需要定期从 tbl_voucher table 插入凭证。

重要提示:每张优惠券只能分配给一个特定的客户(即必须是唯一的)

我写了这样的查询:

UPDATE tbl_customer
SET voucher = (
    SELECT code
    FROM tbl_voucher
    WHERE code NOT IN (
        SELECT voucher
        FROM tbl_customer
        WHERE voucher IS NOT NULL
    )
    LIMIT 1
)
WHERE voucher IS NULL;

然而,这并没有像预期的那样工作,因为查找未使用的优惠券的部分只执行一次,然后将所说的优惠券应用于每个客户。

关于如何在不使用循环等编程结构的情况下解决此问题的任何想法?

此外,还有一些示例数据,您可以想象我希望发生什么:

INSERT INTO tbl_customer VALUES (1, 'Sara', 'ABC');
INSERT INTO tbl_customer VALUES (1, 'Simon', 'DEF');
INSERT INTO tbl_customer VALUES (1, 'Andy', NULL);
INSERT INTO tbl_customer VALUES (1, 'Alice', NULL);

INSERT INTO tbl_voucher VALUES (1, 'ABC');
INSERT INTO tbl_voucher VALUES (2, 'LOL');
INSERT INTO tbl_voucher VALUES (3, 'ZZZ');
INSERT INTO tbl_voucher VALUES (4, 'BBB');
INSERT INTO tbl_voucher VALUES (5, 'CCC');

执行所需查询后,我希望 Andy 获得凭证 LOL 并且 Alice 应该获得 ZZZ

我猜这是 MySQL。答案是,这是一种痛苦。下面在select中赋值:

select c.*, v.voucher
from (select c.*, (@rnc := @rnc + 1) as rn
      from tbl_customer c cross join
           (select @rnc := 0) params
      where c.voucher is null
     ) c join
     (select v.*, (@rnv := @rnv + 1) as rn
      from tbl_vouchers v cross join
           (select @rnv := 0) params
      where not exists (select 1 from tbl_customers c where c.voucher = v.voucher)
     ) v
     on c.rn = v.rn;

您现在可以将其用于 update:

update tbl_customer c join 
       (select c.*, v.voucher
        from (select c.*, (@rnc := @rnc + 1) as rn
              from tbl_customer c cross join
                   (select @rnc := 0) params
              where c.voucher is null
             ) c join
             (select v.*, (@rnv := @rnv + 1) as rn
              from tbl_vouchers v cross join
                   (select @rnv := 0) params
              where not exists (select 1 from tbl_customers c where c.voucher = v.voucher)
             ) v
             on c.rn = v.rn
       ) cv
       on c.id = cv.id
    set c.voucher = cv.voucher;