我有两张桌子;
id_image foo bar
1 3 5
2 8 1
3 17 88
7 14 23
8 12 9
id_image bar foo
1 2 3
1 5 6
2 18 11
2 10 12
3 8 21
3 17 81
7 29 50
7 1 14
8 10 26
8 27 34
第一个表格中的自动增量id_image
存在差距。在第二个表格中,id_image
引用第一个表格中的id_image
,其中每个ID都有两个。
注意:此表是理论上的。我不知道哪里差距正是如此,或者是否存在多个间隙。我所知道的是,第一个值是1
,最后一个值高于总行数。
现在,我想解决这个问题。
在你说差距无关紧要之前,如果他们这样做,这是糟糕的数据库设计,让我告诉你; 我同意你的观点。
然而,我正在处理的是一个(绝望的后端倒退)第三方开源系统,我需要将大量现有数据导入到多个表中,这些数据没有交叉引用的ID。我可以确保相同的数据在整个系统的每个表中获得匹配ID的唯一方法是按顺序输入,这意味着我不能有间隙。
所以我现在需要做的是;
id_image
列的间隙,以便最后一个值与行数匹配。id_image
列,使其值对应于同一行,与缺口修复前对应。我将如何开始这样做?我知道这可能超出了MySQL查询语言的功能,因此PHP答案也是可以接受的。谢谢! :)
答案 0 :(得分:3)
ALTER TABLE table2
ADD FOREIGN KEY FK_IMAGE (id_image)
REFERENCES table1 (id_image)
ON DELETE CASCADE
ON UPDATE CASCADE;
SET @currentRow = 0;
UPDATE table1 INNER JOIN (
SELECT @currentRow := @currentRow + 1 AS id_image_new, id_image AS id_image_old
FROM table1
ORDER BY id_image ASC) t on t.id_image_old = table1.id_image
SET table1.id_image = t.id_image_new;
ALTER TABLE table1 AUTO_INCREMENT = 1;
FK会相应地自动更新第二张桌子的ID。
我根本不确定,但在某些旧版本的mysql中,更新一个在更新子查询中引用的表可能会崩溃。如果是这样,只需创建第二个表并填充(插入),然后删除旧表并重命名新表。
答案 1 :(得分:1)
痛苦的。
创建一个像第一个表,Id_Image上没有标识的表和一个名为rownumber的额外int列
使用伪row_number技巧填充它,有些像
Insert into NewTable
Select id_image,foo,bar,@RowNumber := @RowNumber + 1 order by id_image.
如果您有第二个表的外键删除它,那么它是一个带连接的简单更新。删除旧的table1,重命名新的table1,添加标识并重新设置,如果有的话,将外键放回去。
你意识到你将不得不继续做这个废话吗?
如果你有级联更新,可能有一种有趣的方法可以一次完成所有这些,但要注意执行计划。只有在按Id_Image顺序完成操作时,RowNumber技巧才有效。如果Mysql决定采用更有效的方式进行查询......
答案 2 :(得分:1)
这里的基本思想是首先找出所有的差距,以确定减少每个id需要多少。然后,您必须遍历两个表并应用减量。 (您需要添加:host,db,user,pass和实际的表名)
try {
$pdo = new PDO('mysql:host=HOST;dbname=DB', 'user', 'pass');
$pdo->beginTransaction();
// Iterate through all id's in the first table
$stmt = $pdo->exec('SELECT image_id FROM TableOne ORDER BY image_id ASC');
$stmt->bindColumn('image_id', $id);
if(!$stmt->fetch(PDO::FETCH_BOUND)) {
throw Exception('No rows in table');
}
$lastId = $id;
$gaps = array();
// Find all the gaps
while($stmt->fetch(PDO::FETCH_BOUND)) {
if($id != ($lastId + 1)) {
$gaps[] = $id;
}
$lastId = $id;
}
if(!isset($gaps[0])) {
throw new Exception('No gaps found');
}
// For each gap, update the range from the last gap to that gap by subtracting
// the number of gaps there has been from the id
$lastGap = $gaps[0];
for($i = 1; $i < count($gaps); $i++) {
$stmt = $pdo->prepare('UPDATE TableOne SET image_id = image_id - :i WHERE image_id BETWEEN :lastGap AND :gap');
$stmt->execute(array(
':i' => $i,
':lastGap' => $lastGap,
':gap' => $gaps[$i]
));
$stmt = $pdo->prepare('UPDATE TableTwo SET image_id = image_id - :i WHERE image_id BETWEEN :lastGap AND :gap');
$stmt->execute(array(
':i' => $i,
':lastGap' => $lastGap,
':gap' => $gaps[$i]
));
$lastGap = $gaps[$i];
}
// Finally, fix the gap between the last found gap and the end of the table
$stmt = $pdo->prepare('UPDATE TableOne SET image_id = image_id - :i WHERE image_id BETWEEN :lastGap AND :gap');
$stmt->execute(array(
':i' => $i,
':lastGap' => $lastGap,
':gap' => $gaps[$i]
));
$stmt = $pdo->prepare('UPDATE TableTwo SET image_id = image_id - :i WHERE image_id BETWEEN :lastGap AND :lastId');
$stmt->execute(array(
':i' => $i,
':lastGap' => $lastGap,
':lastId' => $lastId
));
// Verify everything is correct
$stmt = $pdo->exec('SELECT image_id FROM TableOne ORDER BY image_id ASC');
$stmt->bindColumn('image_id', $id);
if(!$stmt->fetch(PDO::FETCH_BOUND)) {
throw new Exception('No rows'); // Should never be thrown
}
$lastId = $id;
while($stmt->fetch(PDO::FETCH_BOUND)) {
if($id != ($lastId + 1)) {
throw new Exception('There was an error between ids ' . $lastId . ' and '. $id);
}
$lastId = $id;
}
$stmt = $pdo->exec('SELECT image_id FROM TableTwo ORDER BY image_id ASC');
$stmt->bindColumn('image_id', $id);
if(!$stmt->fetch(PDO::FETCH_BOUND)) {
throw new Exception('No rows in table two'); // Shouldn't hit this
}
$lastId = $id;
$ids = array($id);
while($stmt->fetch(PDO::FETCH_BOUND)) {
$ids[] = $id;
if(count($ids) == 2) {
if($ids[0] !== $ids[1]) {
throw new Exception('Table two error on ids ');
}
if($ids[0] !== $lastId) {
throw new Exception('Table two error on id gapfix');
}
$lastId = $ids[0];
$ids = array();
}
}
$pdo->commit();
} catch(Exception $e) {
$pdo->rollBack();
var_dump($e);
}
重要提示: 您可能希望将其丢弃到文件中并通过CLI运行: php -f gapfix.php
并在$pdo->commit()
之前包含一个返回列表的查询所有ID,以便您可以验证操作是否按预期工作。如果没有,您可以将其回滚,就像没有发生任何事情一样。 如果第一个表的顺序正确,代码现在会检查自己。但是它还没有检查第二个表格。 所有检查都已实施!