MYSQL 条件更新 table

MYSQL conditional update table

我正在尝试根据特定条件更新数据库 table。这是示例 table.

  fname mname lname
 1   RONALD D VALE
 2   RONALD  VALE
 3   JACK A SMITH
 4   JACK B SMITH
 5   JACK  SMITH

如果名字和姓氏匹配,我想更新中间名列。在此示例中,我希望得到以下输出。

  fname mname lname
 1   RONALD D VALE
 2   RONALD D VALE
 3   JACK A SMITH
 4   JACK B SMITH
 5   JACK  SMITH

我不清楚该怎么做。任意 suggestions/ideas...

编辑

请注意,如果有两个不同的中间名缩写,我也不想更新 table。

我正在努力使数据保持一致。数据中有一些缺失值。因此,主要目的是识别和合并可能相似的多个条目。同时,我们也不想在table中引入错误的数据。此处显示的数据仅包含整个 table 的几列。还有其他属性使元组独一无二。

使用子选择创建 table 的 "clone" 并更新中间名,加入名字和姓氏。

UPDATE names JOIN 
  (SELECT fname, mname, lname FROM names WHERE mname IS NOT NULL
     GROUP BY fname,mname,lname
     HAVING COUNT(*) = 1) AS clone 
ON clone.fname = names.fname AND clone.lname=names.lname
SET names.mname = clone.mname;

这是第一个可能的答案。

UPDATE table t JOIN
  ( SELECT fname, mname, lname, count(*) as qty
    FROM table
    GROUP BY fname, lname
    HAVING qty > 1
) sub
ON t.fname = sub.fname AND t.lname = sub.lname
SET t.mname = sub.mname
WHERE t.mname = '' and sub.qty = 2
;

更新

不应使用 CASE WHEN,应使用 IF 语句。 它处理 RONALD VALE 记录。

UPDATE table t JOIN
  ( SELECT fname, mname, min(mname) minname, max(mname) mxname, lname, count(*) as qty
    FROM table 
    GROUP BY fname, lname
    HAVING qty > 1
) sub
ON t.fname = sub.fname AND t.lname = sub.lname
SET t.mname = IF(sub.qty = 2, sub.mname, IF(sub.qty > 2, sub.mxname, NULL))
WHERE t.mname is NULL OR LEFT(t.mname,1) = LEFT(sub.mxname, 1)
AND t.mname <> sub.mxname
;

更新 2

# Update 1    
UPDATE table t JOIN
         ( SELECT fname, mname, min(mname) minname, max(mname) mxname, lname, count(*) as qty
           FROM table
           GROUP BY fname, lname
           HAVING qty > 1    ) sub    ON t.fname = sub.fname AND t.lname = sub.lname    SET t.mname = IF(sub.qty = 2, sub.mxname, IF(sub.qty > 2 AND minname = mxname, sub.mxname,  NULL))    WHERE t.mname is NULL #OR LEFT(t.mname,1) = LEFT(sub.mxname, 1);
# Update 2    
UPDATE table t JOIN
         ( SELECT fname, mname, min(mname) minname, max(mname) mxname, lname, count(*) as qty
           FROM table
           GROUP BY fname, lname
           HAVING qty > 1    ) sub    ON t.fname = sub.fname AND t.lname = sub.lname    SET t.mname = IF(sub.qty = 2, sub.mxname, IF(sub.qty > 2, sub.mxname,  NULL))    WHERE LEFT(t.mname,1) = LEFT(sub.mxname, 1)    AND t.mname <> sub.mxname # reduce unnecessary tasks;

之前

         DANIEL J   ABADI
         DANIEL     ABADI
         DANIEL     ABADI
         DANIEL     ABADI
         ROBERT     ABADI
         ROBERT K   ABADI
         AMEY   S   BAILEY
         AMEY   SCHENCK BAILEY
         KARL   K   KWON
         KARL       KWON
         DINESH     MAJETI
         ADAM   M   SMITH
         ADAM   B   SMITH
         ADAM   C   SMITH
         ADAM       SMITH
         ADAM       SMITH
         JACK   A   SMITH
         JACK   B   SMITH
         JACK       SMITH
         RONALD A   VALE
         RONALD D   VALE
         RONALD DAVID   VALE
         RONALD     VALE

之后

         DANIEL J   ABADI
         DANIEL J   ABADI
         DANIEL J   ABADI
         DANIEL J   ABADI
         DANIEL J   ABADI
         ROBERT K   ABADI
         ROBERT K   ABADI
         AMEY   SCHENCK BAILEY
         AMEY   SCHENCK BAILEY
         KARL   K   KWON
         KARL   K   KWON
         DINESH     MAJETI
         ADAM   M   SMITH
         ADAM   B   SMITH
         ADAM   C   SMITH
         ADAM       SMITH
         ADAM       SMITH
         JACK   A   SMITH
         JACK   B   SMITH
         JACK       SMITH
         RONALD A   VALE
         RONALD DAVID   VALE
         RONALD DAVID   VALE
         RONALD     VALE
/* create the table */
CREATE TABLE if not exists  `duplicated_names` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `first_name` varchar(50) DEFAULT NULL,
  `middle_name` varchar(50) DEFAULT NULL,
  `last_name` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `first_name` (`first_name`),
  KEY `middle_name` (`middle_name`),
  KEY `last_name` (`last_name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

/* drop old values if any */
truncate duplicated_names ;

/* set up up data for example */
insert into duplicated_names ( first_name, middle_name, last_name ) values ( 'Ronald', 'D', 'Vale') ;
insert into duplicated_names ( first_name, middle_name, last_name ) values ( 'Ronald', 'D', 'Vale') ;
insert into duplicated_names ( first_name, middle_name, last_name ) values ( 'Ronald', '', 'Vale') ;
insert into duplicated_names ( first_name, middle_name, last_name ) values ( 'Jack', 'A', 'Smith') ;
insert into duplicated_names ( first_name, middle_name, last_name ) values ( 'Jack', 'B', 'Smith') ;
insert into duplicated_names ( first_name, middle_name, last_name ) values ( 'Jack', '', 'Smith') ;

update duplicated_names
join (
    /* find the middle names */
    select duplicated_names.id
    , duplicated_names.first_name
    , duplicated_names.middle_name
    , duplicated_names.last_name 
    from duplicated_names
    inner join (
        /* find first_name and last_name that have only one middle name */
        select count(*) sum, first_name, last_name from (
            /* find candidate middle name donors who have middle names */
            select count(*) sum, first_name, middle_name, last_name
            from duplicated_names
            where middle_name <> ''
            group by first_name, middle_name, last_name
        ) candidate_middle_name_donors
        group by first_name, last_name
        having count(*) = 1
    ) names_with_one_middle_name
    on names_with_one_middle_name.first_name = duplicated_names.first_name
    and names_with_one_middle_name.last_name = duplicated_names.last_name
    and duplicated_names.middle_name <> ''
) middle_names
on duplicated_names.first_name = middle_names.first_name
and duplicated_names.last_name = middle_names.last_name
set duplicated_names.middle_name = middle_names.middle_name ;

select * from duplicated_names ;

/* 

results 

id  first_name  middle_name last_name
1   Ronald  D   Vale
2   Ronald  D   Vale
3   Ronald  D   Vale
4   Jack    A   Smith
5   Jack    B   Smith
6   Jack        Smith

*/