Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sqlite/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
SQLite-向上插入*不是*插入或替换_Sql_Sqlite_Upsert - Fatal编程技术网

SQLite-向上插入*不是*插入或替换

SQLite-向上插入*不是*插入或替换,sql,sqlite,upsert,Sql,Sqlite,Upsert,在SQLite中是否有一些我没有想到的聪明方法可以做到这一点 基本上我想更新四列中的三列如果记录存在, 如果它不存在,我想在第四列插入默认(NUL)值的记录 ID是主键,因此只有一条记录需要上传 (我试图避免SELECT的开销,以确定是否需要更新或插入) 建议 我无法确认SQLite站点上用于表创建的语法。 我还没有建立一个演示来测试它,但它似乎不受支持 如果是的话,我有三列,所以它实际上看起来像: CREATE TABLE table1( id INTEGER PRIMARY KE

在SQLite中是否有一些我没有想到的聪明方法可以做到这一点

基本上我想更新四列中的三列如果记录存在, 如果它不存在,我想在第四列插入默认(NUL)值的记录

ID是主键,因此只有一条记录需要上传

(我试图避免SELECT的开销,以确定是否需要更新或插入)

建议


我无法确认SQLite站点上用于表创建的语法。 我还没有建立一个演示来测试它,但它似乎不受支持

如果是的话,我有三列,所以它实际上看起来像:

CREATE TABLE table1( 
    id INTEGER PRIMARY KEY ON CONFLICT REPLACE, 
    Blob1 BLOB ON CONFLICT REPLACE, 
    Blob2 BLOB ON CONFLICT REPLACE, 
    Blob3 BLOB 
);
但是前两个blob不会引起冲突,只有ID会引起冲突 因此,我假设Blob1和Blob2不会被替换(根据需要)


当绑定数据是一个完整事务时,在SQLite中进行更新,这意味着 要更新的每个发送行都需要:Prepare/Bind/Step/Finalize语句 与允许使用重置功能的插入不同

语句对象的生命周期如下所示:

CREATE TABLE table1( 
    id INTEGER PRIMARY KEY ON CONFLICT REPLACE, 
    field1 TEXT 
); 
  • 使用sqlite3\u prepare\u v2()创建对象
  • 使用sqlite3_Bind_接口将值绑定到主机参数
  • 通过调用sqlite3_step()运行SQL
  • 使用sqlite3_Reset()重置语句,然后返回步骤2并重复
  • 使用sqlite3_finalize()销毁语句对象
  • 我猜更新速度比插入慢,但它与使用主键选择相比如何


    也许我应该使用select读取第4列(Blob3),然后使用REPLACE编写一条新记录,将原始第4列与前3列的新数据混合在一起?

    我知道的最佳方法是进行更新,然后插入。 “选择的开销”是必要的,但这不是一个可怕的负担,因为您正在快速搜索主键

    您应该能够使用表名和字段名修改以下语句,以执行所需操作

    --first, update any matches
    UPDATE DESTINATION_TABLE DT
    SET
      MY_FIELD1 = (
                  SELECT MY_FIELD1
                  FROM SOURCE_TABLE ST
                  WHERE ST.PRIMARY_KEY = DT.PRIMARY_KEY
                  )
     ,MY_FIELD2 = (
                  SELECT MY_FIELD2
                  FROM SOURCE_TABLE ST
                  WHERE ST.PRIMARY_KEY = DT.PRIMARY_KEY
                  )
    WHERE EXISTS(
                SELECT ST2.PRIMARY_KEY
                FROM
                  SOURCE_TABLE ST2
                 ,DESTINATION_TABLE DT2
                WHERE ST2.PRIMARY_KEY = DT2.PRIMARY_KEY
                );
    
    --second, insert any non-matches
    INSERT INTO DESTINATION_TABLE(
      MY_FIELD1
     ,MY_FIELD2
    )
    SELECT
      ST.MY_FIELD1
     ,NULL AS MY_FIELD2  --insert NULL into this field
    FROM
      SOURCE_TABLE ST
    WHERE NOT EXISTS(
                    SELECT DT2.PRIMARY_KEY
                    FROM DESTINATION_TABLE DT2
                    WHERE DT2.PRIMARY_KEY = ST.PRIMARY_KEY
                    );
    

    我想这可能就是你想要的:

    如果您这样定义表:

    CREATE TABLE table1( 
        id INTEGER PRIMARY KEY ON CONFLICT REPLACE, 
        field1 TEXT 
    ); 
    
    现在,如果您使用已经存在的id进行插入,SQLite会自动进行更新,而不是插入


    Hth…

    如果您一般都在做更新,我会

  • 开始交易
  • 进行更新
  • 检查行数
  • 如果为0,则执行插入
  • 承诺

  • 如果你一般都做插页,我会

  • 开始交易
  • 尝试插入
  • 检查主键冲突错误
  • 如果出现错误,请执行更新
  • 承诺

  • 这样,您就避免了select,并且在Sqlite上事务性地表现良好

    刚读了这篇文章,对这篇文章的“升级”并不容易感到失望,我进一步调查了

    实际上,您可以在SQLITE中直接轻松地完成这项工作

    而不是使用:
    插入到

    使用:
    插入或替换到

    这正是你想要它做的

    插入或替换不等同于“向上插入”

    假设我拥有具有字段id、名称和角色的表Employee:

    INSERT OR REPLACE INTO Employee ("id", "name", "role") VALUES (1, "John Foo", "CEO")
    INSERT OR REPLACE INTO Employee ("id", "role") VALUES (1, "code monkey")
    
    砰,你把1号员工的名字弄丢了。SQLite已将其替换为默认值

    INSERT OR REPLACE INTO Employee (id, name, role) 
      VALUES (  1, 
                'Susan Bar',
                COALESCE((SELECT role FROM Employee WHERE id = 1), 'Benchwarmer')
              );
    

    UPSERT的预期输出是更改角色并保留名称。

    假设表中有三列:ID、name和role


    错误:这将插入所有列,或用ID=1的新值替换所有列:

    INSERT OR REPLACE INTO Employee (id, name, role) 
      VALUES (1, 'John Foo', 'CEO');
    

    错误:这将插入或替换2列。。。“名称”列将设置为NULL或默认值:

    INSERT OR REPLACE INTO Employee (id, role) 
      VALUES (1, 'code monkey');
    

    :在冲突子句上使用SQLite UPSERT语法已添加到版本为3.24.0的SQLite中

    UPSERT是对INSERT的特殊语法添加,如果INSERT违反唯一性约束,则会导致INSERT表现为更新或NOOP。UPSERT不是标准的SQL。SQLite中的UPSERT遵循PostgreSQL建立的语法

    很好但很乏味:这将更新其中的两列。 当ID=1存在时,名称将不受影响。 当ID=1不存在时,名称将是默认值(NULL)

    这将更新其中的两列。 当ID=1存在时,角色将不受影响。 当ID=1不存在时,角色将设置为“Benchwarmer”,而不是默认值

    INSERT OR REPLACE INTO Employee (id, name, role) 
      VALUES (  1, 
                'Susan Bar',
                COALESCE((SELECT role FROM Employee WHERE id = 1), 'Benchwarmer')
              );
    

    我意识到这是一个旧线程,但我最近一直在使用sqlite3,并提出了这个方法,它更适合我动态生成参数化查询的需要:

    insert or ignore into <table>(<primaryKey>, <column1>, <column2>, ...) values(<primaryKeyValue>, <value1>, <value2>, ...); 
    update <table> set <column1>=<value1>, <column2>=<value2>, ... where changes()=0 and <primaryKey>=<primaryKeyValue>; 
    
    插入或忽略(、、…)值(、…);
    更新集=,=。。。其中changes()=0和=;
    
    更新时仍然有2个查询带有where子句,但似乎做到了这一点。我脑子里也有这样的想法,如果对changes()的调用大于零,sqlite可以完全优化update语句。我不知道这是否真的会发生,但一个人可以做梦,不是吗

    对于奖励积分,您可以附加此行,该行返回您的行id,无论是新插入的行还是现有行

    select case changes() WHEN 0 THEN last_insert_rowid() else <primaryKeyValue> end;
    
    在0时选择case changes(),然后选择last_insert_rowid(),否则结束;
    
    如果只想保留现有行中的一列或两列,则可以。如果您想保留大量列,那么很快就会变得太麻烦

    这里有一种方法可以很好地扩展到任意数量的列。为了说明这一点,我将采用以下模式:

     CREATE TABLE page (
         id      INTEGER PRIMARY KEY,
         name    TEXT UNIQUE,
         title   TEXT,
         content TEXT,
         author  INTEGER NOT NULL REFERENCES user (id),
         ts      TIMESTAMP DEFAULT CURRENT_TIMESTAMP
     );
    
    请特别注意,
    name
    是该行的自然键–
    id
    仅用于外键,因此SQLite在插入新行时自行选择id值。但是,当根据现有行的
    名称
    更新该行时,我希望它同时
     REPLACE INTO page (
       id,
       name,
       title,
       content,
       author,
       date_added)
     SELECT
       old.id,
       "about",
       "About this site",
       old.content,
       42,
       IFNULL(old.date_added,"21/05/2013")
     FROM singleton
     LEFT JOIN page AS old ON old.name = "about";
    
    --Create sample table and records (and drop the table if it already exists)
    DROP TABLE IF EXISTS Contact;
    CREATE TABLE [Contact] (
      [Id] INTEGER PRIMARY KEY, 
      [Name] TEXT
    );
    INSERT INTO Contact (Id, Name) VALUES (1, 'Mike');
    INSERT INTO Contact (Id, Name) VALUES (2, 'John');
    
    -- Try to update an existing record
    UPDATE Contact
    SET Name = 'Bob'
    WHERE Id = 2;
    
    -- If no record was changed by the update (meaning no record with the same Id existed), insert the record
    INSERT INTO Contact (Id, Name)
    SELECT 2, 'Bob'
    WHERE NOT EXISTS(SELECT changes() AS change FROM Contact WHERE change <> 0);
    
    --See the result
    SELECT * FROM Contact;
    
    --Create sample table and records (and drop the table if it already exists)
    DROP TABLE IF EXISTS Contact;
    CREATE TABLE [Contact] (
      [Id] INTEGER PRIMARY KEY, 
      [Name] TEXT
    );
    INSERT INTO Contact (Id, Name) VALUES (1, 'Mike');
    INSERT INTO Contact (Id, Name) VALUES (2, 'John');
    
    -- Try to update an existing record
    UPDATE Contact
    SET Name = 'Bob'
    WHERE Id = 3;
    
    -- If no record was changed by the update (meaning no record with the same Id existed), insert the record
    INSERT INTO Contact (Id, Name)
    SELECT 3, 'Bob'
    WHERE NOT EXISTS(SELECT changes() AS change FROM Contact WHERE change <> 0);
    
    --See the result
    SELECT * FROM Contact;
    
    SELECT COUNT(*) FROM table1 WHERE id = 1;
    
    INSERT INTO table1(col1, col2, cole) VALUES(var1,var2,var3);
    
    UPDATE table1 SET col1 = var4, col2 = var5, col3 = var6 WHERE id = 1;
    
    INSERT OR REPLACE INTO employees (employee_id, first_name, last_name)
    WITH registered_employees AS ( --CTE for checking if the row exists or not
        SELECT --this is needed to ensure that the null row comes second
            *
        FROM (
            SELECT --an existing row
                *
            FROM
                employees
            WHERE
                employee_id = '300'
    
            UNION
    
            SELECT --a dummy row if the original cannot be found
                NULL AS employee_id,
                NULL AS first_name,
                NULL AS last_name
        )
        ORDER BY
            employee_id IS NULL --we want nulls to be last
        LIMIT 1 --we only want one row from this statement
    )
    SELECT --this is where you provide defaults for what you would like to insert
        registered_employees.employee_id, --if this is null the SQLite default will be used
        COALESCE(registered_employees.first_name, 'SALLY'),
        'DAVIS'
    FROM
        registered_employees
    ;
    
    function    addOrUpdateRecords(tableName, values, callback) {
    get_columnNames(tableName, function (data) {
        var columnNames = data;
        myDb.transaction(function (transaction) {
            var query_update = "";
            var query_insert = "";
            var update_string = "UPDATE " + tableName + " SET ";
            var insert_string = "INSERT INTO " + tableName + " SELECT ";
            myDb.transaction(function (transaction) {
                // Data from the array [[data1, ... datan],[()],[()]...]:
                $.each(values, function (index1, value1) {
                    var sel_str = "";
                    var upd_str = "";
                    var remoteid = "";
                    $.each(value1, function (index2, value2) {
                        if (index2 == 0) remoteid = value2;
                        upd_str = upd_str + columnNames[index2] + "='" + value2 + "', ";
                        sel_str = sel_str + "'" + value2 + "', ";
                    });
                    sel_str = sel_str.substr(0, sel_str.length - 2);
                    sel_str = sel_str + " WHERE NOT EXISTS(SELECT changes() AS change FROM "+tableName+" WHERE change <> 0);";
                    upd_str = upd_str.substr(0, upd_str.length - 2);
                    upd_str = upd_str + " WHERE remoteid = '" + remoteid + "';";                    
                    query_update = update_string + upd_str;
                    query_insert = insert_string + sel_str;  
                    // Start transaction:
                    transaction.executeSql(query_update);
                    transaction.executeSql(query_insert);                    
                });
            }, function (error) {
                callback("Error: " + error);
            }, function () {
                callback("Success");
            });
        });
    });
    }
    
    function get_columnNames(tableName, callback) {
    myDb.transaction(function (transaction) {
        var query_exec = "SELECT name, sql FROM sqlite_master WHERE type='table' AND name ='" + tableName + "'";
        transaction.executeSql(query_exec, [], function (tx, results) {
            var columnParts = results.rows.item(0).sql.replace(/^[^\(]+\(([^\)]+)\)/g, '$1').split(','); ///// RegEx
            var columnNames = [];
            for (i in columnParts) {
                if (typeof columnParts[i] === 'string')
                    columnNames.push(columnParts[i].split(" ")[0]);
            };
            callback(columnNames);
        });
    });
    }
    
    WITH new (id, name, title, content, author)
         AS ( VALUES(100, 'about', 'About this site', 'Whatever new content here', 42) )
    INSERT OR REPLACE INTO page (id, name, title, content, author)
    SELECT
         old.id, COALESCE(old.name, new.name),
         new.title, new.content,
         COALESCE(old.author, new.author)
    FROM new LEFT JOIN page AS old ON new.name = old.name;
    
    INSERT INTO table_name (id, column1, column2) 
    VALUES ("youruuid", "value12", "value2")
    ON CONFLICT(id) DO UPDATE 
    SET column1 = "value1", column2 = "value2"
    
    -- https://www.db-fiddle.com/f/7jyj4n76MZHLLk2yszB6XD/22
     
    DROP TABLE IF EXISTS db;
    
    CREATE TABLE db
    (
     id PRIMARY KEY,
     updated_at,
     other
    );
    
    -- initial INSERT
    INSERT INTO db (id,updated_at,other) VALUES(1,1,1);
    
    SELECT * FROM db;
    
    -- INSERT without WHERE
    INSERT INTO db (id,updated_at,other) VALUES(1,2,2)
    ON CONFLICT(id) DO UPDATE SET updated_at=excluded.updated_at;
    
    SELECT * FROM db;
    
    -- WHERE is FALSE
    INSERT INTO db (id,updated_at,other) VALUES(1,2,3)
    ON CONFLICT(id) DO UPDATE SET updated_at=excluded.updated_at, other=excluded.other
    WHERE excluded.updated_at > updated_at;
    
    SELECT * FROM db;
    
    -- ok to SET a PRIMARY KEY. WHERE is TRUE
    INSERT INTO db (id,updated_at,other) VALUES(1,3,4)
    ON CONFLICT(id) DO UPDATE SET id=excluded.id, updated_at=excluded.updated_at, other=excluded.other
    WHERE excluded.updated_at > updated_at;
    
    SELECT * FROM db;