如何检查值是否已存在以避免重复?

时间:2008-09-14 01:02:10

标签: php sql mysql

我有一个网址表,我不想要任何重复的网址。如何使用PHP / MySQL检查表中是否已存在给定的URL?

17 个答案:

答案 0 :(得分:39)

如果您不想要重复,可以执行以下操作:

如果多个用户可以向DB插入数据,@ Jeremy Ruten建议的方法可以导致错误:执行检查后,有人可以向表中插入类似的数据。

答案 1 :(得分:23)

答案 2 :(得分:14)

首先,准备数据库

  • 域名不区分大小写,但您必须假设其余的URL是。 (并非所有的Web服务器都尊重URL中的大小写,但大多数都是这样,并且您无法通过查看来轻松判断。)
  • 假设您需要存储多个域名,请使用区分大小写的排序规则。
  • 如果您决定将URL存储在两列中 - 一列用于域名,另一列用于资源定位器 - 请考虑对域名使用不区分大小写的排序规则,并为资源定位器使用区分大小写的排序规则。如果我是你,我会测试两种方式(一列中的URL与两列中的URL)。
  • 在URL列上放置UNIQUE约束。或者在列对上,如果将域名和资源定位符存储在单独的列中,则为UNIQUE (url, resource_locator)
  • 使用CHECK()约束将编码的URL保留在数据库之外。此CHECK()约束对于防止错误数据通过批量复制或通过SQL shell进入是必不可少的。

其次,准备网址

第三次,如果您只插入网址,不要先测试其存在。相反,尝试插入并捕获如果值已存在您将获得的错误。对于每个新URL,测试和插入会对数据库执行两次命中。插入和陷阱只需命中一次数据库。请注意,insert-and-trap与insert-and-ignore-errors不同。只有一个特定错误意味着您违反了唯一约束;其他错误意味着还有其他问题。

另一方面,如果您将URL与其他一些数据一起插入同一行,则需要提前决定是否通过

处理重复的网址

REPLACE消除了捕获重复键错误的需要,但如果存在外键引用,则可能会产生不幸的副作用。

答案 3 :(得分:14)

您是否完全关注与完全相同的字符串的URL ...如果是这样,其他答案中有很多好的建议。或者你还要担心经典化吗?

例如:http://google.comhttp://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

至少有两个定义:

  1. 如果两个URL表示相同的资源,则它们被视为重复,这些资源对生成相应内容的相应Web服务一无所知。一些考虑包括:
  2. 应用基本URL规范化(例如小写方案和域名,提供默认端口,通过参数名称稳定排序查询参数,在HTTP和HTTPS的情况下删除哈希部分,...),考虑到Web服务的知识。也许您会假设所有Web服务都足够智能化以规范化Unicode输入(例如,维基百科),因此您可以应用Unicode Normalization Form规范组合(NFC)。您将从所有Stack Overflow URL中删除“Host”。您可以使用移植到PHP的PostRank的postrank-uri代码来删除不必要的各种URL(例如www.)。
  3. 定义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是否存在。