обновите столбец, чтобы дублированные значения стали уникальными

#php #mysql #sql

#php #mysql #sql

Вопрос:

Столбец title содержит много дублирующихся значений, более одного раза.

Мне нужно обновить столбец, чтобы, например, если 'gold' он дублируется — он стал 'gold 1' , 'gold 2' и т.д.

Что-то вроде этого:

 $st = $db->query("select id, title from arts order by title asc");
$st->execute();
$x = 0;
while($row = $st->fetch()){
    $title = $row['title'];
    //if($title.is duplicated){
        $x  ;
        $title .= ' ' . $x;
        $stb = $db->query("update arts set title = '" . $title . "' where id = " . $row['id']);
        $stb->execute();
    }
}
  

Любая помощь?

Комментарии:

1. Что такое $db ? query Функция для драйверов, которую я знаю, не использует execute .. это также выглядит открытым для SQL-инъекций. Вы делаете это только для уже существующих данных или новых данных? Может быть, просто добавьте идентификатор столбца к заголовку? Я думаю, вам не нужно select и вы можете просто сделать update arts set title = concat(title, ' ', id) where title = 'gold'

2. @user3783243, интересная идея, но было бы лучше использовать алфавиты, только если заголовок не дублируется.

3. О, я думал, это только для gold . В этом случае, возможно, что-то вроде update arts set title = concat(title, ' ', id) where title in (select title, count(*) as count from arts group by title having count >= 2) ( having синтаксис может быть не с той group стороны, я его перепутал, потому что использую его редко)

4. @user3783243, ошибка — Operand should contain 1 column(s)

5. О, да, count(*) as count это неверно. Не подумал об этом, хм, для этого нужен какой-то синтаксис без захвата

Ответ №1:

Было бы эффективнее сделать это на чистом SQL, а не с помощью PHP. Вот подход, который использует оконные функции, доступные в MySQL 8.0.

Вы можете использовать подзапрос, чтобы подсчитать, сколько title дубликатов существует для каждой записи, и присвоить ранг каждой записи в группах записей, имеющих одинаковое значение title . Затем вы можете JOIN обновить подзапрос с таблицей. Если существует более одной записи, вы можете добавить номер строки к каждой записи в группе.

Запрос:

 UPDATE arts a
INNER JOIN (
    SELECT 
        id, 
        title, 
        COUNT(*) OVER(PARTITION BY title) cnt,
        ROW_NUMBER() OVER(PARTITION BY title ORDER BY id) rn
    FROM arts
) b ON a.id = b.id
SET a.title = CONCAT(a.title, b.rn)
WHERE cnt > 1;
  

Демонстрация на скрипте DB

Пример данных:

 | id  | title  |
| --- | ------ |
| 10  | silver |
| 20  | gold   |
| 30  | gold   |
| 40  | bronze |
| 50  | gold   |
| 60  | bronze |
  

Результаты после выполнения запроса на обновление:

 | id  | title   |
| --- | ------- |
| 10  | silver  |
| 20  | gold1   |
| 30  | gold2   |
| 40  | bronze1 |
| 50  | gold3   |
| 60  | bronze2 |
  

Комментарии:

1. получаю ошибку на моем локальном хостинге (xampp, Windows, phpMyAdmin) — ... MariaDB server version for the right syntax to use near '(PARTITION BY title) cnt, ROW_NUMBER() OVER(PARTITION BY title ORDER BY' at line 6

2. @qadenza: какую версию MySQL / MariaDB вы используете?

3. Что я вижу в phpMyAdmin (версии 4.8.5 ) — Тип сервера: MariaDB , Версия сервера: 10.1.37-MariaDB - mariadb.org binary distribution , версия протокола: 10

4. @qadenza Я думаю, он / она имеет в виду select @@version

5. @пользователь3783243, используя select @@version результат — 10.1.37-MariaDB

Ответ №2:

Пожалуйста, смотрите ниже код, который работает у меня

 // Create connection
$conn = new mysqli($servername, $username, $password,$dbname);
// get all row  
$sql = "select id, title from arts order by title asc";
$result = $conn->query($sql);

while ($row=$result->fetch_assoc()) {   
    $title=$row['title'];
    // select where title is same
    $sql = "select * from arts where title='".$title."'";
    $result2 = $conn->query($sql);
    // if number of row is greater then one 
    if ($result2->num_rows > 1){
        $x=0;
        while ($row2=$result2->fetch_assoc()) {
            $id=$row2['id'];
            // skip first row 
            if($x>0){
                $newTitle=$title.' '.$x;
                $uquery = "update arts set title='".$newTitle."' where title='".$title."' and id=$id";                  
                $update = $conn->query($uquery);
            }
            $x  ;
        }
    }   
}
  

перед запросом

и после выполнения запроса

изображение ниже

Ответ №3:

Это работает в MySQL 5.7:

 update arts a inner join (
  select * from (
    select t.id,
      (
        select count(*)   1 from arts 
        where id < t.id and title = t.title 
      ) counter
    from arts t
  ) t 
) t on t.id = a.id
set a.title = concat(a.title, ' ', t.counter)
where a.title in (
  select h.title from (
    select title from arts
    group by title
    having count(*) > 1    
  ) h 
);
  

Смотрите демонстрацию.
Для данных:

 | id  | title    |
| --- | -------- |
| 1   | silver   |
| 2   | gold     |
| 3   | diamond  |
| 4   | bronze   |
| 5   | gold     |
| 6   | bronze   |
| 7   | gold     |
  

результатом является

 | id  | title    |
| --- | -------- |
| 1   | silver   |
| 2   | gold 1   |
| 3   | diamond  |
| 4   | bronze 1 |
| 5   | gold 2   |
| 6   | bronze 2 |
| 7   | gold 3   |
  

Ответ №4:

Я думаю, что было бы эффективнее сделать это и в SQL, но вы можете создать функцию для проверки дубликата, что-то вроде этого:

 function isDuplicated( $title, $db ){   
    $dp = $db->query("SELECT * FROM arts WHERE title = $title");        
    if ( $dp->num_rows > 1)
        return true;

    return false;            
}


$st = $db->query("select id, title from arts order by title asc");
$st->execute();
$x = 0;
while($row = $st->fetch()){
    $title = $row['title'];
    if( isDuplicated( $title, $db ) ){
        $x  ;
        $title .= ' ' . $x;
        $stb = $db->query("update arts set title = '" . $title . "' where id = " . $row['id']);
        $stb->execute();
    }
}