php - 为什么数据库返回旧信息?
问题描述
(编辑:我在下面添加了示例。我还按照评论中的建议在我的表中添加了一个 public_ip)
(只是免责声明,我对 Web 开发还是很陌生。我对 MySQL 和 PhP 的了解尤其少。)
我有一个包含数字记录的数据库表。对于此示例,它包含 DVD 记录。我的 php 页面之一收到要删除的 DVD 的 ID,然后继续这样做。($chosenTable 是一个变量,因为我也有不同的 CD、Vinyls 和其他表格 - 它们都有相同的列)
$sql = "DELETE FROM $chosenTable WHERE id=$removeID";
$conn->exec($sql);
然而我注意到最近它会删除不存在的记录。例如,我的数据库中有这个:
ID - 标题 - 评论 - 文件位置
1 - Mr Bones - 非常新 - Folder/Bones.jpg
2 - Avengers - Secondhand - Folder/Avengers.jpg
第一次使用删除查询。如果我发送“2”,它会删除记录二。所以正如预期的那样,我还剩下一条记录:
ID - 标题 - 评论 - 文件位置
1 - Mr Bones - 非常新 - 文件夹/Bones.jpg
然后我会添加一条新记录。从 phpMyAdmin 的数据库中,我绝对确定它已被添加:
ID - 标题 - 评论 - 文件位置
1 - Mr Bones - 非常新 - Folder/Bones.jpg
2 - Intersteller - Broken - Folder/Intersteller.jpg
但这里是我没有任何解释的地方。如果我再次删除第二条记录(这次是Intersteller),那么什么也不会发生。当我检查它应该删除的记录的标题时,它返回旧的第二条记录的标题。
获取我要删除的记录的信息:
$temp = $conn->query("SELECT Title from $chosenTable WHERE ID='$removeID')->fetch()["Title"];
echo $temp;
当我这样做时,它返回Avengers。考虑到上面的示例,返回的标题应该是Intersteller,而不是 Avengers。复仇者联盟不再存在于表中。我对此非常肯定。所以现在我知道它不会删除第二条记录(Intersteller),因为它正在删除旧的第二条记录,复仇者联盟。当我删除第三条或第四条记录时也是如此(它会删除不存在的旧的第三条和第四条记录)。
由于某种原因,Chrome 或服务器似乎将自己的伪表存储在它使用的某个地方,而不是我的实际表。
我已经看了一整天,我找不到解决方案。我再次为任何愚蠢的错误道歉。我真的希望我能明白我到底是如何度过这个难关的。
我注意到当我使用另一个浏览器时,比如 Firefox,甚至是我智能手机的浏览器,它再次允许我删除一次记录。如果我上传并删除另一个项目,我会遇到完全相同的问题。它发生在不同的浏览器上让我认为问题出在我身上。
我正在使用数据库 MySQL 客户端版本:5.6.43
有什么帮助吗?
编辑:我制作了两个 php 页面来模拟问题。同样的事情也会发生。我不知道如何连接到 DB fiddle,所以我希望代码本身就足够了。upload.php 提交信息并将其添加到数据库表中。remove.php 删除表中的记录之一:
上传.php
<?php
if ($_SERVER["REQUEST_METHOD"] == "POST"){
//Get info from POST
$uTitle = $_POST["iTitle"];
$uComment = $_POST["tComment"];
$uPrice = $_POST["iPrice"];
echo "<script>console.log('$uTitel - $uComment - $uPrice);</script>";
//Set a file location for the uploaded picture
$fileName = basename($_FILES["iFile"]["name"]);
echo "<script>console.log('filenaam: $fileName');</script>";
$saveLocation = "uploads/".$fileName;
echo "<script>console.log('Saved locationk: $saveLocation');</script>";
//Upload file
if (move_uploaded_file($_FILES["iFile"]["tmp_name"], $saveLocation))
{
//If file is successfully uploaded
echo "<script>console.log('File uploaded');</script>";
//For connecting to database table - these four variables obviously just for illustration:
$servername = "localhost";
$connectusername = "alyosha";
$connectpassword = "alyoshaPassword";
$dbname = "alyoshaDB";
try { //If connected to database
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $connectusername, $connectpassword);
echo "<script>console.log('Connected to Database');</script>";
//I first see how many records are already in the table so that I can add the appropriate value for my 'public_ip' column
$m = $conn->prepare("SELECT * from testTable");
$m->execute();
$amountRows = $m->rowCount();
echo "<script>console.log('Amount in table before adding record: $amountRows');</script>";
//newIP is the current amount+1 so that it is added below my previous records (if there are 3 records, the new one should obviously be record public_ip 4)
$newIP = $amountRows +1;
//Now for adding a row to the table with the submitted informaton:
$sql = "INSERT INTO testTable (public_ip, Title, Comment, Price, File_Location) VALUES ($newIP, '$uTitle', '$uComment', $uPrice, '$saveLocation')";
$conn->exec($sql);
echo "<script>console.log('New row added');</script>";
//Count again to make sure there is one more record than before
$m = $conn->prepare("SELECT * from testTable");
$m->execute();
$amountRows = $m->rowCount();
echo "<script>console.log('Amount in table after adding record: $amountRows');</script>";
}
catch (PDOException $e){ //if database did not connect
echo "<script>console.log('Failed to connect to database');</script>";
}
}//if move
}//if post
?>
<!DOCTYPE html>
<html>
<head>
<title>Add item</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
</head>
<body>
<header>
<h1>Upload</h1>
</header>
<form action='upload.php' method='post' enctype='multipart/form-data'>
<input type='file' name='iFile' required>
<br/>
<br/>
<label for='iTitle'>Title:</label>
<input type='text' name='iTitle' required>
<br/>
<br/>
<label for='iPrice'>Price:</label>
<input id='iPriceUpload' type='number' name='iPrice' min='1' required>
<br/>
<br/>
<label for='tComment'>Comments (optional):</label>
<br/>
<textarea type='text' name='tComment'></textarea>
<br/>
<br/>
<input type='reset' name='iReset'>
<input type='submit' name='iSubmit'>
<br/>
<a href='remove.php?num=1'>Remove record 1</a>
<!--In practise the user would not be able to choose this by altering the link, but for my example this is easier to change this here -->
</body>
</html>
删除.php:
<?php
if ($_SERVER["REQUEST_METHOD"] == "GET"){
$removeIP = $_GET["num"]; //The public_ip record to be deleted
//Connect to table
servername = "localhost";
$connectusername = "alyosha";
$connectpassword = "alyoshaPassword";
$dbname = "alyoshaDB";
try { //If connected to database
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $connectusername, $connectpassword);
echo "<script>console.log('Connected to Database in Remove.php');</script>";
//All the remaining records should have their public_ips shifted upwards (5->4, 4->3, 3->2, 2->1)
//It is necessary to again count the amount of rows left to achieve this
$m = $conn->prepare("SELECT * from testTable");
$m->execute();
$amountRows = $m->rowCount();
echo "<script>console.log('Amount of rows before deleting: $amountRows');</script>";
//For debugging purposes get the title of removeIP:
$gotTitle = $conn->query("SELECT Title from testTable WHERE public_ip='$removeIP'")->fetch()["Title"];
echo "<script>console.log('Title of record $removeIP to be deleted: $gotTitle');</script>";
//
$sql = "DELETE FROM testTable WHERE public_ip=$removeIP"; //public_ip=1
$conn->exec($sql);
echo "<script>console.log('Record $removeIP deleted');</script>";
//count again to make sure one record is removed:
$m = $conn->prepare("SELECT * from testTable");
$m->execute();
$amountRows = $m->rowCount();
echo "<script>console.log('Amount of rows affter deleting: $amountRows');</script>";
if ($removeIP < ($amountRows+1)){ //in case the removed record is the only one in the table it won't be necessary to change the public_ips of the rest
for ($x = 1; $x < ($amountRows+2); $x++)
{
echo "<script>console.log('Changing record: $x');</script>";
$lowerIP = $x-1; //The record with public_ip 2 should become public_ip 1, and 3->2, etc
$sql = "UPDATE testTable SET public_ip=$lowerIP WHERE public_ip=$x";
$stmt = $conn->prepare($sql);
$stmt->execute();
}
echo "<script>console.log('Loop finished');</script>";
}
}
catch (PDOException $e){
echo "<script>console.log('Failed to connect to database in Remove.php');</script>";
}
}//if GET
//After everything go back to upload page
echo "<script>window.location = 'upload.php';</script>";
?>
更新:从广泛的评论看来,PHP 和浏览器之间的某处正在缓存页面。@Martin 提出了一个解决方法,在我解决这个问题之前它似乎很有帮助。这是在页面 url 中添加日期字符串,强制浏览器读取新页面而不是缓存页面(如果我理解正确的话)。
但是,如果有人仍然可以首先解释为什么这是一个问题,我将不胜感激。我可以理解浏览器或使用旧版本页面的任何内容,但我无法理解这会在某处保留过时的表格。
解决方案
恕我直言,在删除行时重新编号是一个非常糟糕的主意。
为什么?
一方面,这样做的成本会随着您拥有的行数越多而增加。SQL 的全部意义在于允许您有效地处理大量表。
另一方面,当您有多个用户这样做时,您会感到困惑。如果一个用户正在查找内容,而另一个用户正在删除它们,则第一个用户将在她下面更改行号。您可以通过明智地使用数据库事务来管理它,但它们只会阻止第二个用户,直到第一个用户完成。这种情况通常被称为“竞争条件”。
您提到您是 MySql 的新手。再次尊重,我建议您坚持使用经过验证的方式处理您的 ID 号码:自动递增。当您从自动递增表中删除一行时,插入的下一行不会重新使用已删除的 ID。
每个人都使用自动递增的 ID 值——从学生到拥有难以想象的巨大桌子的信用卡公司。我建议你也这样做,至少在你对这项技术更加熟练之前。
编辑如果要按插入的顺序显示行,只需执行SELECT whatever FROM tbl ORDER BY id
where id
is the autoincrementing ID column。由于删除的行导致的 ID 序列中的间隙不是问题。
如果你有一个特定的 ID 值并且你想要下一行,按照你插入它们的顺序,你可以做
SELECT whatever
FROM tbl
WHERE id > <<<the value you have>>
ORDER BY id
LIMIT 1;
id > <<<the value you have>>
即使 ID 值有间隙,过滤器也能正常工作。
你说只有一个用户。但是,如果该用户碰巧同时打开了两个在数据库上工作的网页,那么您的重新编号设置将失败。壮观。当您拥有 Web 界面时,假设您将具有并发访问权限始终是最安全的。
推荐阅读
- angular - 如何将带有图像的 HTML 转换为 PDF
- python - python中的Numba jit警告解释
- liquibase - Liquibase 多个 includeAll 语句
- pandas - 如何用现有列值替换 DataFrame 列的 NaN 值?
- cmd - 如何将文件中的一些文本保存到cmd中的变量?
- javascript - 在 FlaskSocketIO 聊天中设置动态聊天室
- apache-spark - 从 cosmos 到 ADLS 的数据归档
- javascript - 如何根据人员数组长度的可分值准备数组?
- java - java - 如何在Java中使用HTTPDELETE作为RequestBody发送列表参数
- visual-studio-code - 如何选择 TM_DIRECTORY 变量的一部分?