我有一张网址表,我不想要任何重复的网址。如何使用PHP/MySQL检查一个给定的URL是否已经在表中?如何检查一个值是否已经存在以避免重复?
回答
如果你不希望有重复你可以做以下操作:
- 添加唯一性约束
- 使用 “REPLACE” 或“ INSERT ... ON DUPLICATE KEY UPDATE“语法
如果多个用户可以将数据插入数据库,@Jeremy Ruten建议的方法,可以导致错误:执行检查后,某人可以向表中插入类似的数据。
如果你插入了一个副本,那么`INSERT IGNORE`应该比`REPLACE`更快。作为额外的好处,你可以知道它是否是新的,因为MySQL返回受影响的行数(使用`ROW_COUNT()`或API)。它也适用于多行插入。 – 2011-08-20 02:08:31
你能做到这一点查询:
SELECT url FROM urls WHERE url = 'http://asdf.com' LIMIT 1
然后检查mysql_num_rows() == 1,看它是否存在。
当您插入时检查时,如何防止另一个连接输入具有该值的行? – 2008-09-14 01:17:47
将其包装到TRANSACTION中 – 2008-09-14 01:18:49
我不知道MySQL的语法,但所有你需要做的就是用IF语句包装你的INSERT语句,它将查询表并查看给定的URL EXISTS是否存在 - 不插入一个新的纪录。
如果MSSQL你可以这样做:
IF NOT EXISTS (SELECT 1 FROM YOURTABLE WHERE URL = 'URL')
INSERT INTO YOURTABLE (...) VALUES (...)
可能不总是在并行环境中工作。 – 2011-08-18 16:48:23
如果你只是想要一个是或否的答案,这个语法应该给你最好的性能。
select if(exists (select url from urls where url = 'http://asdf.com'), 1, 0) from dual
如果你只是想确保有没有重复,然后添加一个唯一索引URL字段,这样就没有必要明确检查URL存在,只需要插入正常,如果已经存在,那么插入将失败并出现重复键错误。
为了保证唯一性,您需要添加一个唯一的约束。假设你的表的名称是“网址”和列名是“URL”,你可以用这个alter table命令添加唯一约束:
alter table urls add constraint unique_url unique (url);
的ALTER TABLE可能会失败,如果(谁真正与MySQL知道)您的表格中已经有了重复的网址。
你是否只关心完全相同的字符串的网址..如果有的话,其他答案中有很多好的建议。还是你也不得不担心封圣?
例如:http://google.com和http://go%4fgle.com是完全相同的URL,但可以通过任何仅限数据库的技术进行复制。如果这是一个问题,您应该预处理URL以解析和字符转义序列。
根据URL来自哪里,您也必须担心参数以及它们在您的应用程序中是否显着。
如果你想在表中插入urls,但只有那些不存在的,你可以在列上添加一个UNIQUE约束,并在你的INSERT查询中添加IGNORE,这样你就不会收到错误。
例子:INSERT INTO忽略SET urls
URL =“网址到插入”
的答案取决于你是否想知道什么时候进行的尝试输入一个记录,一个重复字段。如果你不在乎,那么使用“INSERT ... ON DUPLICATE KEY”语法,因为这将使你的尝试悄然成功,而不会产生重复。
另一方面,如果您想知道何时发生此类事件并防止它发生,那么您应该使用唯一的键约束,这将导致试图插入/更新失败,并显示有意义的错误。
$url = "http://www.scroogle.com";
$query = "SELECT `id` FROM `urls` WHERE `url` = '$url' ";
$resultdb = mysql_query($query) or die(mysql_error());
list($idtemp) = mysql_fetch_array($resultdb) ;
if(empty($idtemp)) // if $idtemp is empty the url doesn't exist and we go ahead and insert it into the db.
{
mysql_query("INSERT INTO urls (`url`) VALUES('$url') ") or die (mysql_error());
}else{
//do something else if the url already exists in the DB
}
简单的SQL解决方案需要一个唯一的字段;逻辑解决方案没有。
你应该规范你的网站,以确保没有重复。在PHP中的函数如strtolower()和urldecode()或rawurldecode()。
假设:您的表名是'网站',您的网址的列名是'网址',并且与该网址相关联的任意数据位于'data'列中。
逻辑解决方案
SELECT COUNT(*) AS UrlResults FROM websites WHERE url='http://www.domain.com'
测试之前的查询与如果SQL或PHP语句,以确保它是0,你继续使用INSERT语句之前。
简单的SQL语句
方案1:你的数据库是先到先得的表,你有没有希望在未来重复的条目。
ALTER TABLE websites ADD UNIQUE (url)
这将防止任何条目能够被输入到数据库,如果URL值已经存在于该列中。
场景2:您希望获得每个网址的最新信息并且不希望重复内容。这种情况有两种解决方案。 (这些解决方案还需要“网址”是唯一的,这样在方案解决方案1 也需要进行。)
REPLACE INTO websites (url, data) VALUES ('http://www.domain.com', 'random data')
这将触发一个DELETE操作,如果行存在,随后在INSERT所有的情况下,所以要小心ON DELETE声明。
INSERT INTO websites (url, data) VALUES ('http://www.domain.com', 'random data')
ON DUPLICATE KEY UPDATE data='random data'
如果行存在,将触发UPDATE操作,如果不存在则触发INSERT。
首先,准备数据库。
- 域名不区分大小写,但您必须假定URL的其余部分是。 (并不是所有的网络服务器都会尊重URL中的大小写,但大多数都会这样做,而且您无法通过查看来轻松分辨。)
- 假设您需要存储多个域名,请使用区分大小写的排序规则。
- 如果您决定将URL存储在两列中(一个用于域名,另一个用于资源定位器),请考虑对域名使用不区分大小写的排序规则以及资源定位符的区分大小写排序规则。如果我是你,我会测试两种方式(一列中的URL与两列中的URL)。
- 在URL列上放置一个UNIQUE约束。或者在一对列上,如果将域名和资源定位器存储在单独的列中,则为
UNIQUE (url, resource_locator)
。 - 使用CHECK()约束将编码的URL保留在数据库之外。此CHECK()约束对于防止不良数据通过大容量副本或SQL外壳进入不可或缺。
其次,准备URL。
- 域名不区分大小写。如果您将完整的网址存储在一列中,请将所有网址的域名小写。但请注意,有些语言的大写字母没有小写字母。
考虑修剪尾随字符。例如,来自amazon.com的这两个URL指向相同的产品。您可能想要存储第二个版本,而不是第一个。
http://www.amazon.com/Systemantics-Systems-Work-Especially-They/dp/070450331X
解码编码的URL。 (请参阅php's urldecode() function。请小心注意其缺点,如该页面的注释中所述)。个人而言,我宁愿在数据库中处理这些类型的转换,而不处理客户端代码。这将涉及撤销对表和视图的权限,并允许仅通过存储过程进行插入和更新;存储过程处理将URL放入规范形式的所有字符串操作。但是当你尝试的时候要留意性能。 CHECK()约束(见上文)是您的安全网。
三,如果你只插入的URL,没有测试它的存在首先。相反,如果值已经存在,请尝试插入并捕获将得到的错误。测试和插入为每个新URL都点击数据库两次。插入和陷阱只需要访问一次数据库。请注意,插入和陷阱与insert-and-ignore-errors不同。只有一个特定的错误意味着你违反了独特的约束;其他错误意味着还有其他问题。
在另一方面,如果你有在同一行中其他一些数据一起插入URL,你需要决定的时候,你是否会通过
- 处理重复的网址,删除旧的未来行并插入一个新的(见MySQL的REPLACE extension to SQL)
- 更新现有的值(见ON DUPLICATE KEY UPDATE)
- 忽视的问题
- 要求用户采取进一步行动
更换无需陷阱重复键错误,但它可能有不幸的副作用,如果有外键引用。
如何在URL中添加urldecode()以解决Rob Walker在答案中提出的问题?或者至少在其域名部分 – Mike 2011-08-18 15:24:38
PHP在dbms之外,这意味着每个其他可能插入URL的应用程序都必须记住要么通过PHP应用程序,要么开发具有相同行为的代码。但是,在db外使用urldecode(),在db内部使用CHECK()约束是一种可靠的,依赖于应用程序的方法。 – 2011-08-18 16:47:54
OP确实说过PHP/MySQL,但是,这也可以使用存储过程完成(例如http://snippets.dzone.com/posts/show/7746) – Mike 2011-08-18 17:13:16
使列的primary key
要回答你最初的问题,检查是否有重复的最简单的方法是运行针对你想添加什么样的SQL查询!
。例如,如果你想在表links
来检查网址http://www.example.com/
,然后将查询将看起来像
SELECT * FROM links WHERE url = 'http://www.example.com/';
你的PHP代码会看起来像
$conn = mysql_connect('localhost', 'username', 'password');
if (!$conn)
{
die('Could not connect to database');
}
if(!mysql_select_db('mydb', $conn))
{
die('Could not select database mydb');
}
$result = mysql_query("SELECT * FROM links WHERE url = 'http://www.example.com/'", $conn);
if (!$result)
{
die('There was a problem executing the query');
}
$number_of_rows = mysql_num_rows($result);
if ($number_of_rows > 0)
{
die('This URL already exists in the database');
}
我已经写了这一点手写在这里,所有的连接到数据库,等等。这可能是因为你已经有一个数据库的连接,所以你应该使用,而不是开始一个新的连接(更换$conn
在mysql_query
命令,并把该东东删除做mysql_connect
和mysql_select_db
)
当然,也有连接到数据库,像PDO,或使用ORM或类似的其他方式,所以如果你已经使用这些,这个答案可能不相关(而且它可能有点超出范围,在这里给出与此相关的解答!)
然而,MySQL提供了许多方法来防止这种在第一时间发生的事情。
首先,你可以标记一个字段作为“唯一”。
可以说我有一个表,我想只是存储链接到从我的站点中的所有网址,以及他们最后一次进行了走访。
我的定义可能是这个样子: -
CREATE TABLE links
(
url VARCHAR(255) NOT NULL,
last_visited TIMESTAMP
)
这将让我一遍又一遍地添加相同的URL,除非我写类似于上面的一些PHP代码来阻止这种情况发生。
但是,是我的定义更改为
CREATE TABLE links
(
url VARCHAR(255) NOT NULL,
last_visited TIMESTAMP,
PRIMARY KEY (url)
)
那么这将使MySQL抛出一个错误,当我试图插入相同值的两倍。
在PHP的一个例子是
$result = mysql_query("INSERT INTO links (url, last_visited) VALUES ('http://www.example.com/', NOW()", $conn);
if (!$result)
{
die('Could not Insert Row 1');
}
$result2 = mysql_query("INSERT INTO links (url, last_visited) VALUES ('http://www.example.com/', NOW()", $conn);
if (!$result2)
{
die('Could not Insert Row 2');
}
如果你跑了这一点,你会发现,在第一次尝试,该脚本将与评论Could not Insert Row 2
死亡。然而,在随后的运行中,它会死于Could not Insert Row 1
。
这是因为MySQL知道url是主要的表的关键。主键是该行的唯一标识符。大多数情况下,将行的唯一标识符设置为数字很有用。这是因为MySQL查找数字比查找文本更快。在MySQL中,键(特别是主键)用于定义两个表之间的关系。举例来说,如果我们得到广大用户的表,我们可以把它定义为
CREATE TABLE users (
username VARCHAR(255) NOT NULL,
password VARCHAR(40) NOT NULL,
PRIMARY KEY (username)
)
然而,当我们想存储有关用户已经发了一个帖子的信息,我们就必须将用户名存储与该职位以确定该帖子属于该用户。
我已经提到MySQL查找数字比字符串更快,所以这意味着当我们不需要时,我们会花时间查找字符串。
为了解决这个问题,我们可以添加一个额外的列,USER_ID,使主键(因此要查找基于对某个帖子的用户记录时,我们可以发现它更快)
CREATE TABLE users (
user_id INT(10) NOT NULL AUTO_INCREMENT,
username VARCHAR(255) NOT NULL,
password VARCHAR(40) NOT NULL,
PRIMARY KEY (`user_id`)
)
你会注意到我还在这里添加了新的东西 - AUTO_INCREMENT。这基本上允许我们让这个领域照顾自己。每插入一个新行,它都会将前一个数字加1,并存储该数字,所以我们不必担心编号问题,并且可以让它自行完成此操作。
因此,与上表中,我们可以这样做
INSERT INTO users (username, password) VALUES('Mez', 'd3571ce95af4dc281f142add33384abc5e574671');
然后
INSERT INTO users (username, password) VALUES('User', '988881adc9fc3655077dc2d4d757d480b5ea0e11');
的东西。当我们从数据库中选择记录,我们得到如下: -
mysql> SELECT * FROM users;
+---------+----------+------------------------------------------+
| user_id | username | password |
+---------+----------+------------------------------------------+
| 1 | Mez | d3571ce95af4dc281f142add33384abc5e574671 |
| 2 | User | 988881adc9fc3655077dc2d4d757d480b5ea0e11 |
+---------+----------+------------------------------------------+
2 rows in set (0.00 sec)
但是,在这里 - 我们遇到了问题 - 我们仍然可以使用相同的用户名添加其他用户!显然,这是我们不想做的事情!
mysql> SELECT * FROM users;
+---------+----------+------------------------------------------+
| user_id | username | password |
+---------+----------+------------------------------------------+
| 1 | Mez | d3571ce95af4dc281f142add33384abc5e574671 |
| 2 | User | 988881adc9fc3655077dc2d4d757d480b5ea0e11 |
| 3 | Mez | d3571ce95af4dc281f142add33384abc5e574671 |
+---------+----------+------------------------------------------+
3 rows in set (0.00 sec)
让我们改变我们的表格定义!
CREATE TABLE users (
user_id INT(10) NOT NULL AUTO_INCREMENT,
username VARCHAR(255) NOT NULL,
password VARCHAR(40) NOT NULL,
PRIMARY KEY (user_id),
UNIQUE KEY (username)
)
让我们看看当我们现在尝试插入同一个用户两次会发生什么。
mysql> INSERT INTO users (username, password) VALUES('Mez', 'd3571ce95af4dc281f142add33384abc5e574671');
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO users (username, password) VALUES('Mez', 'd3571ce95af4dc281f142add33384abc5e574671');
ERROR 1062 (23000): Duplicate entry 'Mez' for key 'username'
Huzzah !!我们现在尝试第二次插入用户名时会出现错误。使用类似上面的内容,我们可以在PHP中检测到这一点。
现在,让我们回到我们的链接表,但有一个新的定义。
CREATE TABLE links
(
link_id INT(10) NOT NULL AUTO_INCREMENT,
url VARCHAR(255) NOT NULL,
last_visited TIMESTAMP,
PRIMARY KEY (link_id),
UNIQUE KEY (url)
)
让我们在数据库中插入“http://www.example.com”。
INSERT INTO links (url, last_visited) VALUES ('http://www.example.com/', NOW());
如果我们尝试并重新插入....
ERROR 1062 (23000): Duplicate entry 'http://www.example.com/' for key 'url'
但是,如果我们要更新上次访问的时间会发生什么?
好了,我们可以做一些复杂的,使用PHP,像这样: -
$result = mysql_query("SELECT * FROM links WHERE url = 'http://www.example.com/'", $conn);
if (!$result)
{
die('There was a problem executing the query');
}
$number_of_rows = mysql_num_rows($result);
if ($number_of_rows > 0)
{
$result = mysql_query("UPDATE links SET last_visited = NOW() WHERE url = 'http://www.example.com/'", $conn);
if (!$result)
{
die('There was a problem updating the links table');
}
}
或者,甚至抢在数据库中的行的id,并用它来更新它。
$ result = mysql_query(“SELECT * FROM links WHERE url ='http://www.example.com/'”,$ conn);
if (!$result)
{
die('There was a problem executing the query');
}
$number_of_rows = mysql_num_rows($result);
if ($number_of_rows > 0)
{
$row = mysql_fetch_assoc($result);
$result = mysql_query('UPDATE links SET last_visited = NOW() WHERE link_id = ' . intval($row['link_id'], $conn);
if (!$result)
{
die('There was a problem updating the links table');
}
}
但是,MySQL有内置的功能,一个漂亮的叫REPLACE INTO
让我们来看看它是如何工作的。
mysql> SELECT * FROM links;
+---------+-------------------------+---------------------+
| link_id | url | last_visited |
+---------+-------------------------+---------------------+
| 1 | http://www.example.com/ | 2011-08-19 23:48:03 |
+---------+-------------------------+---------------------+
1 row in set (0.00 sec)
mysql> INSERT INTO links (url, last_visited) VALUES ('http://www.example.com/', NOW());
ERROR 1062 (23000): Duplicate entry 'http://www.example.com/' for key 'url'
mysql> REPLACE INTO links (url, last_visited) VALUES ('http://www.example.com/', NOW());
Query OK, 2 rows affected (0.00 sec)
mysql> SELECT * FROM links;
+---------+-------------------------+---------------------+
| link_id | url | last_visited |
+---------+-------------------------+---------------------+
| 2 | http://www.example.com/ | 2011-08-19 23:55:55 |
+---------+-------------------------+---------------------+
1 row in set (0.00 sec)
注意使用REPLACE INTO
时,它的更新的last_visited时间,而不是抛出一个错误!
这是因为MySQL检测到您正试图替换一行。它知道你想要的行,因为你已经将url设置为唯一的。 MySQL通过使用传入的位应该是唯一的(在本例中为url)并更新该行的其他值来计算要替换的行。它也更新了link_id - 这有点意外! (事实上,我没有意识到这会发生,直到我看到它发生!)
但是,如果你想添加一个新的URL?那么,REPLACE INTO
会很高兴地插入一个新的行,如果它找不到匹配的唯一行!
mysql> REPLACE INTO links (url, last_visited) VALUES ('http://www.stackoverflow.com/', NOW());
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM links;
+---------+-------------------------------+---------------------+
| link_id | url | last_visited |
+---------+-------------------------------+---------------------+
| 2 | http://www.example.com/ | 2011-08-20 00:00:07 |
| 3 | http://www.stackoverflow.com/ | 2011-08-20 00:01:22 |
+---------+-------------------------------+---------------------+
2 rows in set (0.00 sec)
我希望这会回答你的问题,并且给你更多关于MySQL如何工作的信息!
您可以使用自联接来定位(和删除)。表中有一些网址,也有一些PK(我们知道,PK 不是的URL,否则你将不会被允许有重复)
SELECT
*
FROM
yourTable a
JOIN
yourTable b -- Join the same table
ON b.[URL] = a.[URL] -- where the URL's match
AND b.[PK] <> b.[PK] -- but the PK's are different
这将返回已复制的URL所有行。
说,但是,你想只选择重复并排除原来的....好吧,你需要决定什么构成原件。对于这个答案的目的,让我们假设最低的PK是“原始”
所有你需要做的是以下条款添加到上面的查询:
WHERE
a.[PK] NOT IN (
SELECT
TOP 1 c.[PK] -- Only grabbing the original!
FROM
yourTable c
WHERE
c.[URL] = a.[URL] -- has the same URL
ORDER BY
c.[PK] ASC) -- sort it by whatever your criterion is for "original"
现在你有一个集中所有的非原始重复行。你可以很容易地执行一个DELETE
或任何你喜欢从这个结果集。
请注意,这种方法可能效率不高,部分原因是mySQL并不总是处理IN
,但我从OP了解到,这在桌面上是“清理”的,而不总是检查。
如果您要检查在INSERT
时间的值是否已经存在,如果你得到一个结果,那么你可以得出结论的价值已经在您的数据库至少存在,你可以运行像这样
SELECT
1
WHERE
EXISTS (SELECT * FROM yourTable WHERE [URL] = 'testValue')
一旦。
第一件事是第一件事。如果你还没有创建表,或者你创建了一个表,但是没有数据,那么你需要添加一个唯一的constriant或唯一的索引。有关在索引或约束之间进行选择的更多信息,请参见文章末尾。但他们都完成同样的事情,强制该列只包含唯一值。
要在此列上创建具有唯一索引的表,您可以使用。
CREATE TABLE MyURLTable(
ID INTEGER NOT NULL AUTO_INCREMENT
,URL VARCHAR(512)
,PRIMARY KEY(ID)
,UNIQUE INDEX IDX_URL(URL)
);
如果你只是想要一个独特的约束,并在该表没有索引,则可以使用
CREATE TABLE MyURLTable(
ID INTEGER NOT NULL AUTO_INCREMENT
,URL VARCHAR(512)
,PRIMARY KEY(ID)
,CONSTRAINT UNIQUE UNIQUE_URL(URL)
);
现在,如果你已经有一个表,并且在它没有数据,然后您可以使用以下某个代码将索引或约束添加到表中。现在
ALTER TABLE MyURLTable
ADD UNIQUE INDEX IDX_URL(URL);
ALTER TABLE MyURLTable
ADD CONSTRAINT UNIQUE UNIQUE_URL(URL);
,你可能已经在它的一些数据的表格。在这种情况下,你可能已经有一些重复的数据。你可以尝试创建上面显示的constriant或index,如果你已经有重复的数据,它会失败。如果你没有重复的数据,那么很好,如果你这样做,你必须删除重复的数据。使用以下查询,您可以看到一串重复的网址。
SELECT URL,COUNT(*),MIN(ID)
FROM MyURLTable
GROUP BY URL
HAVING COUNT(*) > 1;
以删除重复行,并保留一个,请执行以下操作:
DELETE RemoveRecords
FROM MyURLTable As RemoveRecords
LEFT JOIN
(
SELECT MIN(ID) AS ID
FROM MyURLTable
GROUP BY URL
HAVING COUNT(*) > 1
UNION
SELECT ID
FROM MyURLTable
GROUP BY URL
HAVING COUNT(*) = 1
) AS KeepRecords
ON RemoveRecords.ID = KeepRecords.ID
WHERE KeepRecords.ID IS NULL;
现在你已经删除了所有的记录,你可以继续创建您的索引或约束。现在,如果你想在你的数据库中插入一个值,你应该使用类似的东西。
INSERT IGNORE INTO MyURLTable(URL)
VALUES('http://www.example.com');
这将试图做插入,如果它发现重复,什么都不会发生。现在,让我们说你有其他专栏,你可以做这样的事情。
INSERT INTO MyURLTable(URL,Visits)
VALUES('http://www.example.com',1)
ON DUPLICATE KEY UPDATE Visits=Visits+1;
这看起来会尝试插入值,如果找到该URL,那么它会通过递增访问计数器来更新记录。当然,你总是可以做一个普通的旧插入,并处理你的PHP代码中产生的错误。现在,至于你是否应该使用约束或索引,这取决于很多因素。索引可以加快查找速度,所以当表格变大时,性能会更好,但存储索引会占用额外的空间。索引通常也会使插入和更新花费更长的时间,因为它必须更新索引。然而,由于必须以任何方式查看该值,为了强制实现唯一性,在这种情况下,无论如何索引索引可能会更快。至于任何与性能相关的问题,答案都是尝试两种选择并分析结果,以查看哪种方法最适合您的情况。
在考虑解决这个问题时,您需要首先定义“重复URL”对于您的项目意味着什么。这将决定如何在将URL添加到数据库之前canonicalize。
至少有两个定义:
- 两个URL被视为重复,如果它们表示相同的资源一无所知有关生成相应内容对应的网络服务。一些考虑因素包括:
- URL的方案和域名部分区分大小写,因此HTTP://WWW.STACKOVERFLOW.COM/与http://www.stackoverflow.com/相同。
- 如果一个URL指定一个端口,但它是该方案的传统端口,并且它们在其他方面是等效的,那么它们是相同的(http://www.stackoverflow.com/和http://www.stackoverflow.com:80/)。
- 如果查询字符串中的参数是简单重新排列且参数名称都不相同,那么它们是相同的;例如http://authority/?a=test&b=test和http://authority/?b=test&a=test。请注意,http://authority/?a%5B%5D=test1&a%5B%5D=test2与第一个定义的相同性不同,如http://authority/?a%5B%5D=test2&a%5B%5D=test1。
- 如果方案是HTTP或HTTPS,则可以删除URL的散列部分,因为这部分URL不会发送到Web服务器。
- 缩短的IPv6地址可以扩展。
- 如果缺少尾随正斜杠,则仅向授权。
- Unicode canonicalization更改引用的资源;例如你不能得出结论:http://google.com/?q=%C3%84(
%C3%84
代表UTF-8中的'Ä')与http://google.com/?q=A%CC%88(%CC%88
代表U + 0308,组合DIAERESIS)相同。 - 如果该方案是HTTP或HTTPS,则一个URL的权威中的'
www.
'不能简单地被删除,因为这两个URL在其他方面是等同的,因为域名的文本是作为HTTP头的值发送的;一些网络服务器使用虚拟主机根据这个头部发回不同的内容。更一般地说,即使域名解析为相同的IP地址,也不能断定引用的资源是相同的。
- 应用基本的URL规范化(例如小写方案和域名,提供默认端口,通过参数名称提供稳定的排序查询参数,在HTTP和HTTPS的情况下移除哈希部分...) 和考虑了Web服务的知识。也许你会认为所有的Web服务都足够聪明,可以对Unicode输入进行规范化(例如维基百科),所以你可以应用Unicode Normalization Form Canonical Composition(NFC)。您可以从所有堆栈溢出URL中去除“
www.
”。您可以使用PostRank的postrank-uri代码移植到PHP,以移除各种不必要的URL(例如&utm_source=...
)。
定义1导致一个稳定的解决方案(即没有进一步的规范化可以执行,并且URL的规范化不会改变)。定义2,我认为是人们认为URL规范化的定义,导致规范化例程,可以在不同的时刻产生不同的结果。
无论您选择哪种定义,我建议您为方案,登录,主机,端口和路径部分使用单独的列。这将允许您智能地使用索引。方案和主机的列可以使用字符整理(所有字符整理在MySQL中都不区分大小写),但登录和路径的列需要使用二进制,不区分大小写的整理。另外,如果使用定义2,则需要保留原始方案,权限和路径部分,因为某些规范化规则可能会不时添加或删除。
编辑:下面是示例表定义:
CREATE TABLE `urls1` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`scheme` VARCHAR(20) NOT NULL,
`canonical_login` VARCHAR(100) DEFAULT NULL COLLATE 'utf8mb4_bin',
`canonical_host` VARCHAR(100) NOT NULL COLLATE 'utf8mb4_unicode_ci', /* the "ci" stands for case-insensitive. Also, we want 'utf8mb4_unicode_ci'
rather than 'utf8mb4_general_ci' because 'utf8mb4_general_ci' treats accented characters as equivalent. */
`port` INT UNSIGNED,
`canonical_path` VARCHAR(4096) NOT NULL COLLATE 'utf8mb4_bin',
PRIMARY KEY (`id`),
INDEX (`canonical_host`(10), `scheme`)
) ENGINE = 'InnoDB';
CREATE TABLE `urls2` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`canonical_scheme` VARCHAR(20) NOT NULL,
`canonical_login` VARCHAR(100) DEFAULT NULL COLLATE 'utf8mb4_bin',
`canonical_host` VARCHAR(100) NOT NULL COLLATE 'utf8mb4_unicode_ci',
`port` INT UNSIGNED,
`canonical_path` VARCHAR(4096) NOT NULL COLLATE 'utf8mb4_bin',
`orig_scheme` VARCHAR(20) NOT NULL,
`orig_login` VARCHAR(100) DEFAULT NULL COLLATE 'utf8mb4_bin',
`orig_host` VARCHAR(100) NOT NULL COLLATE 'utf8mb4_unicode_ci',
`orig_path` VARCHAR(4096) NOT NULL COLLATE 'utf8mb4_bin',
PRIMARY KEY (`id`),
INDEX (`canonical_host`(10), `canonical_scheme`),
INDEX (`orig_host`(10), `orig_scheme`)
) ENGINE = 'InnoDB';
表`urls1`是用于根据定义1。表`urls2`是用于根据定义2.
存储规范的URL存储规范网址不幸的是,由于MySQL限制了InnoDB密钥的长度,所以你不能在元组('scheme` /`canonical_scheme`,`canonical_login`,`canonical_host`,`port`,`canonical_path`)上指定UNIQUE
约束到767字节。
- 1. 如何检查一个值是否已经存在,以避免重复?如果它存在,然后什么都不做,否则插入记录到数据库
- 2. 检查是否值已经存在
- 3. 如何检查是否存在jquery函数以避免重复func
- 4. 避免重写已经存在
- 5. 如何检查一个关系是否已经存在?
- 6. 如何检查一个IP是否已经存储在mysql中?
- 7. 如何检查一个集合是否已经存在于ArangoDB
- 8. 检查SQLite数据库中是否已经存在一个值
- 9. CodeIgniter - 检查数据库中是否已经存在一个值
- 10. 选项设置文本值重复....是否有一个属性,我可以检查,以避免重复?
- 11. 如何检查插入新值是否已经存在?
- 12. 如何检查是否值已经存在
- 13. 如何检查值是否已经存在?
- 14. 如何检查Android中的数据库中是否已经存在一个值
- 15. Rails,检查url是否已经存在
- 16. MySQL检查表是否已经存在
- 17. 检查SPListItem是否已经存在
- 18. 检查mysql表是否已经存在
- 19. 检查数据是否已经存在
- 20. 检查表单是否已经存在
- 21. 你将如何检查一行是否已经存在jquery
- 22. 如何避免重复值?
- 23. 如何避免重复值
- 24. 如何检查DOM中是否已经存在三个元素
- 25. 如何在检查目录是否存在时避免EXC_BAD_ACCESS?
- 26. 如何避免重复记录而不是否存在
- 27. 如何检查一个实体是否已经存在于永久存储中
- 28. 检查内存中的值是否已经存在
- 29. 检查一个项目是否已经存在于JComboBox中?
- 30. 检查一个项目是否已经存在于listbox1中
很多答案都建议在'url'列中添加`UNIQUE`约束。有一件事要记住,MySQL限制了密钥的大小。根据您在URL中允许的最大字节数,这可能是个问题。 [5.6参考手册陈述](http://dev.mysql.com/doc/refman/5.6/en/create-index.html):“对于MyISAM表,”[A]前缀长度最长可达1000字节,并且InnoDB表的767字节“。 – 2011-08-21 18:51:21