sqlpostgresqljoininner-join

UPDATE Statement ignores INNER JOIN


I expect the SQL statement to update a limited amounts of rows, instead of every row:

UPDATE Join_Test_1
SET ANumber = 3
FROM Join_Test_1 AS ST1
INNER JOIN Join_Test_2 AS ST2 ON ST1.PKID = ST2.PKID;

I expect the statement to behave the same as the statements:

UPDATE Join_Test_1
SET ANumber = 3
FROM Join_Test_2
WHERE Join_Test_1.PKID = Join_Test_2.PKID;

UPDATE Join_Test_1
SET ANumber = 3
WHERE EXISTS (SELECT * FROM Join_Test_2 
              WHERE Join_Test_2.PKID = Join_Test_1.PKID);

UPDATE Join_Test_1
SET ANumber = 3
WHERE PKID IN (SELECT PKID FROM Join_Test_2);

The statements using the WHERE clause only update the ANumber column where the PKID matches.

The statement using the INNER JOIN updates all rows in the table.

Why does the INNER JOIN not limit the number of rows updated?

Can the statement with the INNER JOIN be rewritten to use the JOIN to limit the number of rows updated?

/* Expansive Example */
CREATE TABLE Join_Test_1 (PKID SERIAL, ANumber INTEGER);
CREATE TABLE Join_Test_2 (PKID SERIAL, ANumber INTEGER);

INSERT INTO Join_Test_1 (ANumber) VALUES (1), (1);      
INSERT INTO Join_Test_2 (ANumber) VALUES (2);       

UPDATE Join_Test_1
SET ANumber = 3
FROM Join_Test_1 AS ST1
INNER JOIN Join_Test_2 AS ST2 ON ST1.PKID = ST2.PKID; -- Updates 2

SELECT * 
FROM Join_Test_1 
ORDER BY PKID;

-- 1, 3
-- 2, 3

UPDATE Join_Test_1 
SET ANumber = 1; -- Update 2

UPDATE Join_Test_1
SET ANumber = 3
FROM Join_Test_2
WHERE Join_Test_1.PKID = Join_Test_2.PKID;

SELECT * 
FROM Join_Test_1 
ORDER BY PKID;

-- 1, 1
-- 2, 3     

UPDATE Join_Test_1 SET ANumber = 1; -- Update 2

UPDATE Join_Test_1
SET ANumber = 3
WHERE PKID IN (SELECT PKID FROM Join_Test_2);

SELECT * 
FROM Join_Test_1 
ORDER BY PKID;

-- 1, 1
-- 2, 3     

DROP TABLE IF EXISTS Join_Test_1;
DROP TABLE IF EXISTS Join_Test_2;

Solution

  • In plpgSQL there is an way to update a single table during joing as follows:

    CREATE TABLE Join_Test_1 (PKID SERIAL,ANumber INTEGER);
    CREATE TABLE Join_Test_2 (PKID SERIAL,ANumber INTEGER);
    
    INSERT INTO Join_Test_1 (ANumber) VALUES (1),(1);       
    INSERT INTO Join_Test_2 (ANumber) VALUES (2); 
    
    select * from Join_Test_1;
    -- 1    1
    -- 2    1
    
    select * from Join_Test_2;
    -- 1    2
    
    WITH TT AS (
        UPDATE Join_Test_1
        SET ANumber = 3
        RETURNING *
    )
    SELECT *
    FROM TT          AS ST1
    INNER JOIN 
         Join_Test_2 AS ST2
    ON   ST1.PKID = ST2.PKID;
    -- 1    3   1   2
    
    SELECT * FROM Join_Test_1 ORDER BY PKID;
    -- 1    3
    -- 2    3
    

    Process-01: db<>fiddle - Update During joining

    Process-02: db<>fiddle - Update During joining

    Process-03: db<>fiddle - Normal way