简体   繁体   中英

Create a view combining two tables, prioritizing rows from one

I'm altering a table with a crap ton of data, which is going to take weeks to run. In the meantime, I'm creating a new table to write to while the original table is locked.

CREATE TABLE NEW_TABLE LIKE (OLD_TABLE INCLUDING ALL);

If I do this, I need to update my queries to read from both tables. So I'm creating a view combining both tables, which I'll use to query for the next month.

CREATE VIEW TABLE_VIEW AS
   SELECT * FROM NEW_TABLE
   UNION ALL
   SELECT * FROM OLD_TABLE

On updates, I'll have to copy rows from OLD_TABLE into NEW_TABLE with certain fields changed. For example:

Old query:

UPDATE OLD_TABLE SET isPerson=false WHERE id=123 AND name=john

New query:

INSERT INTO NEW_TABLE (id, name, color, team, isPerson)
   SELECT id, name, color, team, false
   FROM OLD_TABLE
   WHERE id = 123
   AND name = john
ON DUPLICATE KEY UPDATE isPerson = false

This means there will be duplicate data (based on my primary keys) across the tables, in which case the corresponding rows in OLD_TABLE would be outdated. How do I create the view to only include the rows from NEW_TABLE when the same primary keys exist in both tables?

(Assume primary keys are id and name, and no other constraints on the table)

Preface

IDK why somebody minused OP question.

Consider scenario:

  • during day there is a lot of copies by single row from old table to new
  • combination of INSERT and DELETE is avoided because it has additional cost
  • during night there a massive cleanup of copied records from old_table
  • but users must see combined result without doubles

That's an absolutely valid use-case.

Answer

What OP needs is to use NOT EXISTS predicate:

CREATE VIEW TABLE_VIEW AS
SELECT * FROM NEW_TABLE
UNION ALL
SELECT * FROM OLD_TABLE OT
WHERE NOT EXISTS(
   SELECT 1 FROM NEW_TABLE NT
     WHERE
       NT.id = OT.id
       AND NT.name = OT.name
   )

Look: https://www.db-fiddle.com/f/xcEWWkaGputdHYnYqmwQhx/2

UPDATE . More pedantic and correct answer

Note that by SQL Standard order is not guaranteed without ORDER BY .

On most RDBMSs UNION ALL returns rows from top to bottom.

But don't forget that by SQL Standard order of rows is not guaranteed without ORDER BY

It could work now but it could be broken after even a minor patch.

So to make such view absolutely correct some changes to tables are needed.

Eg add a new field insert_date into tables or even:

CREATE VIEW TABLE_VIEW AS
SELECT * FROM (
    SELECT *, 0 as display_order FROM NEW_TABLE
    UNION ALL
    SELECT *, 1 as display_order FROM OLD_TABLE OT
    WHERE NOT EXISTS(
    SELECT 1 FROM NEW_TABLE NT
        WHERE
        NT.id = OT.id
        AND NT.name = OT.name
    )
)
ORDER BY display_order

https://www.db-fiddle.com/f/xcEWWkaGputdHYnYqmwQhx/3

Notes

Just using UNION could gives the same results

But:

  • IDK if it's guaranteed. Not by SQL standard that's for sure
  • it would be more performant with a lot of records (no hidden DISTINCT )

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