简体   繁体   中英

Dynamically update table with column from another table

I have a table customer like this:

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

and a voucher table like this:

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

Now imagine that the customer table always has rows with id and name filled in, however the voucher needs to be inserted periodically from the tbl_voucher table.

Important: every voucher may only be assigned to one specific customer (ie must be unique)

I wrote a query like this:

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;

However this is not working as expected, since the part that looks for an unused voucher is executed once and said voucher is then applied to every customer.

Any ideas on how I can solve this without using programming structures such as loops?

Also, some example data so you can imagine what I would like to happen:

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');

After the wanted query is executed, I'd expect Andy to have the voucher LOL and Alice should get ZZZ

I am going to guess this is MySQL. The answer is that this is a pain. The following assigns the values in a 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;

You can now use this for the 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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM