【问题标题】:MySQL ON DUPLICATE KEY UPDATE for multiple rows insert in single queryMySQL ON DUPLICATE KEY UPDATE 在单个查询中插入多行
【发布时间】:2011-02-12 11:06:42
【问题描述】:

我有一个 SQL 查询,我想在单个查询中插入多行。所以我使用了类似的东西:

$sql = "INSERT INTO beautiful (name, age)
  VALUES
  ('Helen', 24),
  ('Katrina', 21),
  ('Samia', 22),
  ('Hui Ling', 25),
  ('Yumie', 29)";

mysql_query( $sql, $conn );

问题是当我执行这个查询时,我想检查是否有UNIQUE 键(不是PRIMARY KEY),例如'name' 以上,应该检查,如果这样的'name' 已经存在,则应更新相应的整行,否则插入。

例如,在下面的示例中,如果'Katrina' 已存在于数据库中,则无论字段数如何,都应更新整行。同样,如果'Samia' 不存在,则应插入该行。

我想过使用:

INSERT INTO beautiful (name, age)
      VALUES
      ('Helen', 24),
      ('Katrina', 21),
      ('Samia', 22),
      ('Hui Ling', 25),
      ('Yumie', 29) ON DUPLICATE KEY UPDATE

这是陷阱。我对如何进行感到困惑和困惑。我一次要插入/更新多行。请给我一个方向。谢谢。

【问题讨论】:

    标签: sql mysql duplicates


    【解决方案1】:

    从 MySQL 8.0.19 开始,您可以为该行使用别名(请参阅 reference)。

    INSERT INTO beautiful (name, age)
        VALUES
        ('Helen', 24),
        ('Katrina', 21),
        ('Samia', 22),
        ('Hui Ling', 25),
        ('Yumie', 29)
        AS new
    ON DUPLICATE KEY UPDATE
        age = new.age
        ...
    

    对于早期版本,请使用关键字VALUES (请参阅reference,MySQL 8.0.20 已弃用)。

    INSERT INTO beautiful (name, age)
        VALUES
        ('Helen', 24),
        ('Katrina', 21),
        ('Samia', 22),
        ('Hui Ling', 25),
        ('Yumie', 29)
    ON DUPLICATE KEY UPDATE
        age = VALUES(age),
         ...
    

    【讨论】:

    • 很好的建议。如果要在重复时增加一个值,请在“ON DUPLICATE KEY UPDATE”部分之后添加:total = total + VALUES(total)
    • 注意:The use of VALUES() to refer to the new row and columns is deprecated beginning with MySQL 8.0.20。请参阅:dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html
    • 注意:不要使用变量名new
    • @harlekintiger 他们似乎在我链接的手册中使用new。不过,我不使用 MySQL,所以请您详细说明为什么不应该将其命名为 new?谢谢!
    • @PeterLang 当然,我应该第一次指定。虽然它确实有效并且不会引发错误,但至少目前,在 MySQL 中,'new' 是一个保留关键字 MySQL 8.0 Manual: 9.3 Keywords and Reserved Words
    【解决方案2】:

    INSERT INTO ... ON DUPLICATE KEY UPDATE 仅适用于 MYSQL,不适用于 SQL Server。

    对于 SQL Server,解决此问题的方法是首先声明一个临时表,向该临时表插入值,然后使用 MERGE

    像这样:

    declare @Source table
    (
    name varchar(30),
    age decimal(23,0)
    )
    
    insert into @Source VALUES
    ('Helen', 24),
    ('Katrina', 21),
    ('Samia', 22),
    ('Hui Ling', 25),
    ('Yumie', 29);
    
    
    MERGE beautiful  AS Tg
    using  @source as Sc
    on tg.namet=sc.name 
    
    when matched then update 
    set tg.age=sc.age
    
    when not matched then 
    insert (name, age) VALUES
    (SC.name, sc.age);
    

    【讨论】:

    • 原来的问题是专门关于 MySQL 的
    【解决方案3】:

    我正在使用 jdbi 的 BindBeanList 寻找相同的行为,发现语法与上面 Peter Lang 的答案完全相同。如果有人遇到这个问题,这是我的代码:

      @SqlUpdate("INSERT INTO table_one (col_one, col_two) VALUES <beans> ON DUPLICATE KEY UPDATE col_one=VALUES(col_one), col_two=VALUES(col_two)")
    void insertBeans(@BindBeanList(value = "beans", propertyNames = {"colOne", "colTwo"}) List<Beans> beans);
    

    需要注意的一个关键细节是,您在 @BindBeanList 注释中指定的 propertyName 与您在更新时传递给 VALUES() 调用的列名称不同。

    【讨论】:

      【解决方案4】:

      让我在数据库中有 2 个表:(1)brand (2)brand_item。

      DROP TABLE IF EXISTS `brand`;
         CREATE TABLE `brand` (
        `brand_id` int(11) NOT NULL AUTO_INCREMENT,
        `brand_key` varchar(255) DEFAULT NULL,
        `brand_name` varchar(2048) DEFAULT NULL,
        `disclaimer` varchar(2048) DEFAULT NULL,
        `description` varchar(2048) DEFAULT NULL,
        `short_description` varchar(2048) DEFAULT NULL,
        `terms` varchar(2048) DEFAULT NULL,
        `created_date` datetime DEFAULT NULL,
        `last_updated_date` datetime DEFAULT NULL,
        `always_show_disclaimer` varchar(2048) DEFAULT NULL,
        `disclaimer_instructions` varchar(2048) DEFAULT NULL,
        `display_instructions` varchar(2048) DEFAULT NULL,
        `terms_and_conditions_instructions` varchar(2048) DEFAULT NULL,
        `image_urls` json DEFAULT NULL,
        `status` varchar(255) DEFAULT NULL,
        `feature` boolean DEFAULT '1',
        PRIMARY KEY (`brand_id`),
        UNIQUE KEY `brand_key` (`brand_key`)
      ) ENGINE=InnoDB AUTO_INCREMENT=91 DEFAULT CHARSET=latin1;
       
      DROP TABLE IF EXISTS `brand_item`;
      CREATE TABLE `brand_item` (
        `brand_id` int(11) DEFAULT NULL,
        `utid` varchar(255) DEFAULT NULL,
        `reward_name` varchar(2048) DEFAULT NULL,
        `currency_code` varchar(2048) DEFAULT NULL,
        `status` varchar(255) DEFAULT NULL,
        `value_type` varchar(255) DEFAULT NULL,
        `reward_type` varchar(255) DEFAULT NULL,
        `is_whole_amount_value_required` varchar(255) DEFAULT NULL,
        `face_value` double(16,2) DEFAULT '0.00',
        `min_value` double(16,2) DEFAULT '0.00',
        `max_value` double(16,2) DEFAULT '0.00',
        `created_date` datetime DEFAULT NULL,
        `last_updated_date` datetime DEFAULT NULL,
        `redemption_instructions` varchar(2048) DEFAULT NULL,
        `countries` varchar(2048) DEFAULT NULL,
        UNIQUE KEY `utid` (`utid`),
        KEY `brand_id` (`brand_id`),
        CONSTRAINT `brand_item_ibfk_1` FOREIGN KEY (`brand_id`) REFERENCES `brand` (`brand_id`)
      ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
      

      要插入品牌表的 Javascript 代码: 在 INSERT 查询中处理 ON DUPLICATE KEY UPDATE。

      async function addToDB(event, brandDetails) {
      return new Promise((resolve, reject) => {
      
      let values = `[brandDetails.key,brandDetails.name,brandDetails.disclaimer,brandDetails.description,brandDetails.shortDescription,brandDetails.terms,brandDetails.createdDate,brandDetails.lastUpdateDate,brandDetails.alwaysShowDisclaimer,brandDetails.disclaimerInstructions,brandDetails.displayInstructions,brandDetails.termsAndConditionsInstructions,brandDetails.imageUrls,brandDetails.status]` 
      
      let query = "INSERT INTO ?? (brand_key,brand_name,disclaimer,description,short_description,terms,created_date,last_updated_date,always_show_disclaimer,       disclaimer_instructions,display_instructions,terms_and_conditions_instructions,image_urls,status) VALUES (?) ON DUPLICATE KEY UPDATE brand_key=?, brand_name=?, disclaimer=?, description=?, short_description=?, terms=?, created_date=?,last_updated_date=?, always_show_disclaimer=?, disclaimer_instructions=?,\ display_instructions=?,terms_and_conditions_instructions=?, image_urls=?, status=?";
      
        MySQLController.executeQuery(event,query, [BRAND_TABLE, values, values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7],values[8],values[9],values[10],values[11],values[12],values[13],values[14]] )
        .then((res)=>{
            console.log("Success in Insertion ", res);
            resolve(res);
        })
        .catch((err)=>{
            console.log("error in DB ",err);
            reject(err);     
        })
      
        })}
      

      要插入brand_item 表的Javascript 代码: 在 INSERT 查询中处理 ON DUPLICATE KEY UPDATE。

       async function addToBrandItem(event, fkey, brandItemDetails) {
        return new Promise((resolve, reject) => {
            
             let values = [fkey, brandItemDetails.utid, brandItemDetails.rewardName, brandItemDetails.currencyCode, brandItemDetails.status, brandItemDetails.valueType, brandItemDetails.rewardType,brandItemDetails.isWholeAmountValueRequired, `${brandItemDetails.faceValue}`, `${brandItemDetails.minValue}`, `${brandItemDetails.maxValue}`, brandItemDetails.createdDate,brandItemDetails.lastUpdateDate,brandItemDetails.redemptionInstructions,`${brandItemDetails.countries}`]
            
        let query = "INSERT INTO ?? (brand_id,utid,reward_name,currency_code,status,value_type,reward_type,is_whole_amount_value_required,face_value,min_value,max_value,created_date,last_updated_date,redemption_instructions,countries) VALUES (?)";
        
      
        AuroraController.executeQuery(event,query , [BRAND_ITEM_TABLE, values, values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7],values[8],values[9],values[10],values[11],values[12],values[13],values[14]] )
        .then((res)=>{
            console.log("Success in Insertion in Bran_item", res);
            resolve(res);
        })
        .catch((err)=>{
            console.log("error in DB ",err);
            reject(err);     
        })
      
        })}
      

      注意:-为了在值数组中保留十进制值,我有刻度符号以使用${} 获取其值,否则它将被视为数组中的字符串。

      【讨论】:

        【解决方案5】:

        您可以使用Replace 代替 INSERT ... ON DUPLICATE KEY UPDATE。

        【讨论】:

        • 是的,但是使用 REPLACE 会在插入新行之前删除旧行。我想保留旧行来维护主 ID。
        • 我没有意识到保留旧的 PK 对你来说很重要...在这种情况下,REPLACE 肯定行不通...
        • PS:“替换成”需要删除和插入权限!
        • 由于上述删除/插入原因,在这些情况下它也慢得多。
        • 如果你想更新几列怎么办?替换将全部更新。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-05-01
        • 1970-01-01
        • 2017-02-09
        • 2014-06-23
        • 2012-06-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多