简体   繁体   中英

Why does this SQLite UPDATE statement write changes to all records in the table?

This query returns 40319 records:

SELECT artist,
       title,
       composer AS alib_composer,
       master.composer AS master_composer
  FROM alib
       INNER JOIN
       master ON hex(alib.__path) = hex(master.__path) AND 
                 alib.l_title = lower(master.title) AND 
                 alib.composer != master.composer AND 
                 length(alib.composer) != length(master.composer) AND 
                 master.composer LIKE "% %" AND 
                 alib.composer NOT LIKE "% %";

Translating above into the following UPDATE query results in all records in alib being updated. Could someone help me understand why this is the case?

UPDATE alib
   SET composer = (
           SELECT composer
             FROM master
            WHERE hex(alib.__path) = hex(master.__path) AND 
                  alib.l_title = lower(master.title) AND 
                  alib.composer != master.composer AND 
                  length(alib.composer) != length(master.composer) AND 
                  master.composer LIKE "% %" AND 
                  alib.composer NOT LIKE "% %"
       );

I suspect I know why, but turning the update into something like this:

UPDATE alib
   SET composer = master.composer
 WHERE hex(alib.__path) = hex(master.__path) AND 
        alib.l_title = lower(master.title) AND 
        alib.composer != master.composer AND 
        length(alib.composer) != length(master.composer) AND 
        master.composer LIKE "% %" AND 
        alib.composer NOT LIKE "% %";

causes SQLite to complain:

Error while executing SQL query on database 'x': no such column: master.composer

Because there is no WHERE clause on the outer/main UPDATE SQL ie it is basiclly

UPDATE alib SET composer = the_result_of_the_subquery;
  • ie UPDATE every row.

You want a WHERE clause to say which rows to update along the lines of :-

UPDATE alib SET composer = the_result_of_the_subquery WHERE ??????;

Or to use your code :-

UPDATE alib
    SET composer = (
       SELECT composer
         FROM master
        WHERE hex(alib.__path) = hex(master.__path) AND 
              alib.l_title = lower(master.title) AND 
              alib.composer != master.composer AND 
              length(alib.composer) != length(master.composer) AND 
              master.composer LIKE "% %" AND 
              alib.composer NOT LIKE "% %"
   )
WHERE the_expression_to_restrict_which_rows_are_updated
;

This hasn't been fully tested but this may be solution you are looking for :-

WITH cte AS 
    (
        SELECT 
            master.composer AS master_composer,
            alib.rowid AS arowid
        FROM alib
            INNER JOIN
                master ON hex(alib.__path) = hex(master.__path) 
                    AND alib.l_title = lower(master.title) 
                    AND alib.composer != master.composer 
                    AND length(alib.composer) != length(master.composer) 
                    AND master.composer LIKE '% %' 
                    AND alib.composer NOT LIKE '% %'
    )
UPDATE alib 
SET composer = 
    (
        SELECT cte.master_composer FROM cte WHERE cte.arowid = alib.rowid
    ) 
WHERE alib.rowid IN (SELECT arowid FROM cte);
  • this uses a Common Table Expresssion(CTE) (temporay table that exists just for the query).
  • It utlises the rowid (a hidden column that every table has other than WITHOUT ROWID tables).
  • That is the CTE is based upon your original query that extracts the rows from alib that need to be updated. It only needs the composer from the master table and the rowid of the alib table for the update.
  • The UPDATE sets the composer to the value of the master_composer column of the respective row from the CTE according to the rowid of the row being updated.
  • The UPDATEs are only applied to rows that have a matching row in the CTE.

Testing (limited)

The following SQL was used for testing :-

DROP TABLE IF EXISTS alib;
DROP TABLE IF EXISTS master;
CREATE TABLE IF NOT EXISTS alib (__path,l_title,composer);
CREATE TABLE IF NOT EXISTS master (__path,title,artist,composer);

INSERT INTO alib VALUES
    ('path1','this','bert'),
    ('path2','something else','Joe'),
    ('path2','something else','Joe'), -- duplicate likely irrelevant but still updated
    ('path2','something else','Joe Someone'), -- already has space in composer skipped
    ('path100','an so on','x') -- no equivalent in master
;
INSERT INTO master VALUES 
    ('path1','this','fred','bert'), -- ignored as no space
    ('path1','this','fred bloggs','Julia smith'),
    ('path2','something else','Alison','Joe Brown')
;

/* DEMO OF EXTRACTED ROWS */
WITH cte AS 
    (
        SELECT 
            master.composer AS master_composer,
            alib.rowid AS arowid 
        FROM alib
            INNER JOIN
                master ON hex(alib.__path) = hex(master.__path) 
                    AND alib.l_title = lower(master.title) 
                    AND alib.composer != master.composer 
                    AND length(alib.composer) != length(master.composer) 
                    AND master.composer LIKE '% %' 
                    AND alib.composer NOT LIKE '% %'
    )
SELECT * FROM cte;

WITH cte AS 
    (
        SELECT 
            master.composer AS master_composer,
            alib.rowid AS arowid 
        FROM alib
            INNER JOIN
                master ON hex(alib.__path) = hex(master.__path) 
                    AND alib.l_title = lower(master.title) 
                    AND alib.composer != master.composer 
                    AND length(alib.composer) != length(master.composer) 
                    AND master.composer LIKE '% %' 
                    AND alib.composer NOT LIKE '% %'
    )
UPDATE alib 
SET composer = 
    (
        SELECT cte.master_composer FROM cte WHERE cte.arowid = alib.rowid
    ) 
WHERE alib.rowid IN (SELECT arowid FROM cte);

SELECT * FROM alib;
DROP TABLE IF EXISTS alib;
DROP TABLE IF EXISTS master;

Testing Results

The extracted data :-

在此处输入图片说明

alib table after UPDATES :-

在此处输入图片说明

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