我有一个网址表,我不想要任何重复的网址。如何使用PHP / MySQL检查表中是否已存在给定的URL?
答案 0 :(得分:39)
如果您不想要重复,可以执行以下操作:
如果多个用户可以向DB插入数据,@ Jeremy Ruten建议的方法可以导致错误:执行检查后,有人可以向表中插入类似的数据。
答案 1 :(得分:23)
答案 2 :(得分:14)
首先,准备数据库。
UNIQUE (url, resource_locator)
。其次,准备网址。
考虑修剪尾随字符。例如,amazon.com的这两个网址指向同一产品。您可能希望存储第二个版本,而不是第一个版本。
http://www.amazon.com/Systemantics-Systems-Work-Especially-They/dp/070450331X
解码编码的网址。 (参见php's urldecode() function。请仔细注意其缺点,如该页面的评论所述。)就个人而言,我宁愿在数据库而不是客户端代码中处理这些类型的转换。这将涉及撤消对表和视图的权限,并允许仅通过存储过程进行插入和更新;存储过程处理将URL放入规范形式的所有字符串操作。但是,当你尝试时,请注意性能。 CHECK()约束(见上文)是你的安全网。
第三次,如果您只插入网址,不要先测试其存在。相反,尝试插入并捕获如果值已存在您将获得的错误。对于每个新URL,测试和插入会对数据库执行两次命中。插入和陷阱只需命中一次数据库。请注意,insert-and-trap与insert-and-ignore-errors不同。只有一个特定错误意味着您违反了唯一约束;其他错误意味着还有其他问题。
另一方面,如果您将URL与其他一些数据一起插入同一行,则需要提前决定是否通过
处理重复的网址REPLACE消除了捕获重复键错误的需要,但如果存在外键引用,则可能会产生不幸的副作用。
答案 3 :(得分:14)
您是否完全关注与完全相同的字符串的URL ...如果是这样,其他答案中有很多好的建议。或者你还要担心经典化吗?
例如:http://google.com和http://go%4fgle.com是完全相同的网址,但只允许使用任何数据库技术作为重复网址。如果这是一个问题,您应该预处理URL以解析和字符转义序列。
根据网址的来源,您还必须担心参数以及它们在您的应用中是否具有重要意义。
答案 4 :(得分:13)
为了保证唯一性,您需要添加唯一约束。假设您的表名为“urls”且列名为“url”,则可以使用此alter table命令添加唯一约束:
alter table urls add constraint unique_url unique (url);
如果您已经在表中已经有重复的URL,则alter table可能会失败(谁真的知道MySQL)。
答案 5 :(得分:6)
简单的SQL解决方案需要一个唯一的字段;逻辑解决方案没有。
您应该规范化您的网址,以确保没有重复。 PHP中的函数,例如 strtolower()和 urldecode()或 rawurldecode()。
假设:您的表格名称为“网站”,您网址的列名称为“网址”,与网址关联的任意数据位于“数据”列中。
逻辑解决方案
SELECT COUNT(*) AS UrlResults FROM websites WHERE url='http://www.domain.com'
使用SQL或PHP中的if语句测试上一个查询,以确保在继续INSERT语句之前它为0。
简单的SQL语句
场景1:您的数据库是先到先得的表,您不希望将来有重复的条目。
ALTER TABLE websites ADD UNIQUE (url)
如果该列中已存在url值,这将阻止任何条目进入数据库。
场景2:您希望获得每个网址的最新信息,并且不希望复制内容。这种情况有两种解决方案。 (这些解决方案还要求'url'是唯一的,因此还需要执行场景1 中的解决方案。)
REPLACE INTO websites (url, data) VALUES ('http://www.domain.com', 'random data')
如果在所有情况下都存在行后跟INSERT,则会触发DELETE操作,因此请注意ON DELETE声明。
INSERT INTO websites (url, data) VALUES ('http://www.domain.com', 'random data')
ON DUPLICATE KEY UPDATE data='random data'
如果存在行,则会触发UPDATE操作,如果不存在,则触发INSERT。
答案 6 :(得分:4)
在考虑此问题的解决方案时,您需要首先定义“重复的URL”对您的项目意味着什么。这将决定在将URL添加到数据库之前如何canonicalize。
至少有两个定义:
%C3%84
代表'Ä'在UTF-8中)与http://google.com/?q=A%CC%88相同(%CC%88
代表U + 0308,组合DIAERESIS)。 www.
”,因为域名的文本作为{的值发送{1}} HTTP标头,一些Web服务器使用虚拟主机根据此标头发回不同的内容。更一般地说,即使域名解析为相同的IP地址,也不能断定引用的资源是相同的。Host
”。您可以使用移植到PHP的PostRank的postrank-uri代码来删除不必要的各种URL(例如www.
)。定义1导致稳定的解决方案(即,没有可以执行的进一步规范化,并且URL的规范化不会改变)。定义2,我认为是人类认为URL规范化的定义,导致规范化例程,可以在不同的时刻产生不同的结果。
无论您选择哪种定义,我建议您为方案,登录,主机,端口和路径部分使用单独的列。这将允许您智能地使用索引。 scheme和host的列可以使用字符排序规则(所有字符排序规则在MySQL中都不区分大小写),但登录和路径的列需要使用二进制,不区分大小写的排序规则。此外,如果使用定义2,则需要保留原始方案,权限和路径部分,因为可能会不时添加或删除某些规范化规则。
编辑:以下是示例表定义:
&utm_source=...
表`urls1`用于根据定义1存储规范URL。表`urls2`用于根据定义2存储规范URL。
不幸的是,你将无法在元组(`scheme` /`canonical_scheme`,`canonical_login`,`canonical_host`,`port`,`canonical_path`)上指定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';
约束,因为MySQL限制了InnoDB密钥的长度为767字节。
答案 7 :(得分:2)
我不知道MySQL的语法,但是你需要做的就是用你的INSERT包装你的INSERT语句来查询表,看看带有给定url EXISTS的记录是否存在 - 不插入新纪录。
如果是MSSQL,你可以这样做:
IF NOT EXISTS (SELECT 1 FROM YOURTABLE WHERE URL = 'URL')
INSERT INTO YOURTABLE (...) VALUES (...)
答案 8 :(得分:1)
如果要在表中插入URL,但只有那些不存在的URL,可以在列上添加UNIQUE约束,在INSERT查询中添加IGNORE,这样就不会出错。
示例:INSERT IGNORE INTO urls
SET url ='url-to-insert'
答案 9 :(得分:1)
首先要做的事情。如果您尚未创建表,或者您创建了一个表但没有数据,则需要添加唯一的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代码中处理生成的错误。现在,至于你是否应该使用约束或索引,这取决于很多因素。索引可以加快查找速度,因此随着表变大,性能会更好,但存储索引会占用额外的空间。索引通常也会使插入和更新花费更长时间,因为它必须更新索引。但是,由于必须以任一方式查找该值,以强制执行唯一性,在这种情况下,无论如何都可能更快地获得索引。至于任何与性能相关的问题,答案是尝试这两个选项并对结果进行分析,以确定哪种方法最适合您的情况。
答案 10 :(得分:0)
答案取决于您是否想知道何时尝试输入具有重复字段的记录。如果您不在乎,请使用“INSERT ... ON DUPLICATE KEY”语法,因为这样可以使您的尝试安静地成功,而不会产生重复。
另一方面,如果你想知道这样的事件何时发生并阻止它,那么你应该使用一个唯一的密钥约束,这将导致尝试的插入/更新失败并产生有意义的错误。
答案 11 :(得分:0)
$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
}
答案 12 :(得分:0)
如果你只是想确保没有重复项,那么在url字段中添加一个唯一索引,这样就不需要显式检查url是否存在,只是正常插入,如果它已经存在那么插入将失败并出现重复键错误。
答案 13 :(得分:0)
将列设为primary key
答案 14 :(得分:0)
如果你只想要一个是或否答案,这个语法应该会给你最好的表现。
select if(exists (select url from urls where url = 'http://asdf.com'), 1, 0) from dual
答案 15 :(得分:0)
您可以使用自联接找到(并删除)。你的表有一些URL和一些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')
如果您得到了结果,那么您可以至少一次结束数据库中已存在的值。
答案 16 :(得分:-1)
您可以执行以下查询:
SELECT url FROM urls WHERE url = 'http://asdf.com' LIMIT 1
然后检查mysql_num_rows() == 1是否存在。