#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;
Пример данных:
| 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();
}
}