如何检查一个值是不是已经存在以避免重复?

Posted

技术标签:

【中文标题】如何检查一个值是不是已经存在以避免重复?【英文标题】:How to check if a value already exists to avoid duplicates?如何检查一个值是否已经存在以避免重复? 【发布时间】:2010-09-08 19:54:36 【问题描述】:

我有一个 URL 表,我不想要任何重复的 URL。如何使用 php/mysql 检查给定的 URL 是否已经在表中?

【问题讨论】:

许多答案建议将UNIQUE 约束添加到“url”列。要记住的一件事是 MySQL 限制了键的大小。根据您在 URL 中允许的最大字节数,这可能是个问题。 The 5.6 reference manual states: "[A] 前缀对于 MyISAM 表最长可达 1000 字节,对于 InnoDB 表最长可达 767 字节。" 【参考方案1】:

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

添加唯一性约束 使用“REPLACE”或“INSERT ... ON DUPLICATE KEY UPDATE”语法

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

【讨论】:

如果您要插入副本,那么INSERT IGNORE 应该比REPLACE 快​​。作为额外的好处,您可以知道它是否是新的,因为 MySQL 返回受影响的行数(使用ROW_COUNT() 或 API)。它也适用于多行插入。【参考方案2】:

要回答您最初的问题,检查是否存在重复的最简单方法是针对您要添加的内容运行 SQL 查询!

例如,如果您想在表 links 中检查 url 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');

我已经在这里写了这个,所有连接到数据库等等。很可能你已经有一个到数据库的连接,所以你应该使用它而不是开始一个新的连接(替换@ mysql_query 命令中的 987654325@ 并删除与 mysql_connectmysql_select_db 相关的内容)

当然,还有其他方法可以连接到数据库,例如 PDO,或使用 ORM 或类似方法,所以如果您已经在使用这些方法,那么这个答案可能不相关(而且可能有点超出范围在这里给出与此相关的答案!)

然而,MySQL 提供了许多方法来防止这种情况发生。

首先,您可以将字段标记为“唯一”。

假设我有一个表,我想在其中存储从我的网站链接到的所有 URL,以及上次访问它们的时间。

我的定义可能是这样的:-

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 是表的 Primary Key。主键是该行的唯一标识符。大多数情况下,将行的唯一标识符设置为数字很有用。这是因为 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'

嘘!!当我们第二次尝试插入用户名时,我们现在得到一个错误。使用类似上面的方法,我们可以在 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.***.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.***.com/ | 2011-08-20 00:01:22 |
+---------+-------------------------------+---------------------+
2 rows in set (0.00 sec)

我希望这能回答您的问题,并为您提供有关 MySQL 工作原理的更多信息!

【讨论】:

Eep - 我没有意识到这篇文章有多长! 我认为它实际上是从上一个问题开始的。 REPLACE INTO 更新 link_id 的原因是因为它实际上执行了 DELETEINSERT,而不是 UPDATE - 这很可怕。考虑改用INSERT ON DUPLICATE KEY UPDATE 这个答案值得拥有自己的博文。 必须是 SO 上最长的答案之一。 :o【参考方案3】:

您是否只关心完全相同的字符串的 URL .. 如果是这样,其他答案中有很多好的建议。还是您还需要担心封圣?

例如:http://google.com 和 http://go%4fgle.com 是完全相同的 URL,但任何仅数据库技术都允许重复。如果这是一个问题,您应该预处理要解析的 URL 和字符转义序列。

根据 URL 的来源,您还必须担心参数以及它们在您的应用程序中是否重要。

【讨论】:

【参考方案4】:

首先,准备数据库

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

其次,准备网址

域名不区分大小写。如果您将完整的 URL 存储在一列中,请将所有 URL 上的域名小写。但请注意,有些语言的大写字母没有对应的小写字母。

考虑修剪尾随字符。例如,来自 amazon.com 的这两个 URL 指向同一个产品。您可能想要存储第二个版本,而不是第一个。

http://www.amazon.com/Systemantics-Systems-Work-Especially-They/dp/070450331X/ref=sr_1_1?ie=UTF8&qid=1313583998&sr=8-1

http://www.amazon.com/Systemantics-Systems-Work-Especially-They/dp/070450331X

解码编码的 URL。 (请参阅php's urldecode() function。仔细注意它的缺点,如该页的 cmets 中所述。)就个人而言,我宁愿在数据库中而不是在客户端代码中处理这些类型的转换。这将涉及撤销对表和视图的权限,并只允许通过存储过程进行插入和更新;存储过程处理将 URL 转换为规范形式的所有字符串操作。但是,当您尝试这样做时,请注意性能。 CHECK() 约束(见上文)是您的安全网。

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

另一方面,如果您要在同一行中插入 URL 以及其他一些数据,则需要提前决定是否处理重复的 url

删除旧行并插入新行(参见 MySQL 的 REPLACE extension to SQL) 更新现有值(参见ON DUPLICATE KEY UPDATE) 忽略问题 要求用户采取进一步措施

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

【讨论】:

如何将 urldecode() 添加到 URL 以解决 Rob Walker 回答中提出的问题?或者至少是它的域名部分 PHP 在 dbms 之外,这意味着可能插入 URL 的每个其他应用程序都必须记住要通过您的 PHP 应用程序或开发具有相同行为的代码。但是在 db 外部使用 urldecode() 并在 db 内部使用 CHECK() 约束是一种可防御的、依赖于应用程序的方法。 OP 确实说 PHP/MySQL,但是,这也可以使用存储过程来完成(例如 snippets.dzone.com/posts/show/7746) 是的,这就是我所说的“我宁愿在数据库中而不是在客户端代码中处理这些类型的更改”时的意思。【参考方案5】:

为了保证唯一性,您需要添加唯一性约束。假设您的表名是“urls”并且列名是“url”,您可以使用这个 alter table 命令添加唯一约束:

alter table urls add constraint unique_url unique (url);

如果您的表中已经有重复的 url,alter 表可能会失败(谁知道 MySQL)。

【讨论】:

【参考方案6】:

简单的 SQL 解决方案需要一个唯一的字段;逻辑解决方案没有。

您应该规范化您的网址以确保没有重复。 PHP 中的函数,例如 strtolower()urldecode()rawurldecode()

假设:你的表名是'websites',你的url的列名是'url',和url关联的任意数据在'data'列中。

逻辑解决方案

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。

【讨论】:

【参考方案7】:

在考虑解决此问题时,您需要首先定义“重复 URL”对您的项目意味着什么。这将确定在将 URL 添加到数据库之前如何canonicalize。

至少有两种定义:

    如果两个 URL 代表相同的资源,对生成相应内容的相应 Web 服务一无所知,则认为它们是重复的。一些考虑因素包括: URL 的方案和域名部分不区分大小写,因此HTTP://WWW.***.COM/ 与http://www.***.com/ 相同。 如果一个 URL 指定了一个端口,但它是该方案的常规端口,并且它们在其他方面是等效的,那么它们是相同的(http://www.***.com/ 和 http://www.***.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 规范化改变了引用的资源;例如你不能断定http://google.com/?q=%C3%84(%C3%84代表UTF-8中的'Ä')与http://google.com/?q=A%CC%88相同(%CC%88代表U+0308,COMBINING DIAERESIS)。 如果方案是 HTTP 或 HTTPS,如果两个 URL 相同,则不能简单地删除一个 URL 权限中的“www.”,因为域名文本作为Host 的值发送HTTP 标头,一些 Web 服务器使用虚拟主机根据此标头发回不同的内容。更一般地说,即使域名解析到相同的 IP 地址,也不能断定引用的资源相同。 应用基本 URL 规范化(例如小写方案和域名,提供默认端口,按参数名称稳定排序查询参数,在 HTTP 和 HTTPS 的情况下删除哈希部分,...),并且考虑到网络服务的知识。也许您会假设所有 Web 服务都足够智能以规范化 Unicode 输入(例如 Wikipedia),因此您可以申请 Unicode Normalization Form Canonical Composition (NFC)。您将从所有 Stack Overflow URL 中删除“www.”。您可以使用 PostRank 的 postrank-uri 代码(移植到 PHP)来删除各种不必要的 URL(例如 &utm_source=...)。

定义 1 导致了一个稳定的解决方案(即没有可以执行的进一步规范化,并且 URL 的规范化不会改变)。定义 2,我认为是人类对 URL 规范化的定义,导致规范化例程可以在不同的时间产生不同的结果。

无论您选择哪种定义,我建议您为方案、登录名、主机、端口和路径部分使用单独的列。这将允许您智能地使用索引。 scheme 和 host 的列可以使用字符排序规则(所有字符排序规则在 MySQL 中都是不区分大小写的),但是 login 和 path 的列需要使用二进制的、不区分大小写的排序规则。此外,如果您使用定义 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 的规范 URL。表 `urls2` 用于存储根据定义 2 的规范 URL。

不幸的是,您将无法在元组(`scheme`/`canonical_scheme`、`canonical_login`、`canonical_host`、`port`、`canonical_path`)上指定 UNIQUE 约束,因为 MySQL 限制了InnoDB 键为 767 字节。

【讨论】:

【参考方案8】:

我不知道 MySQL 的语法,但您需要做的就是用 IF 语句包装您的 INSERT,该语句将查询表并查看具有给定 url 的记录是否存在,如果它存在 - 不要插入新纪录。

如果是 MSSQL,你可以这样做:

IF NOT EXISTS (SELECT 1 FROM YOURTABLE WHERE URL = 'URL')
INSERT INTO YOURTABLE (...) VALUES (...)

【讨论】:

【参考方案9】:

如果您想将 url 插入表中,但只有那些不存在的,您可以在列上添加 UNIQUE 约束,并在 INSERT 查询中添加 IGNORE,这样您就不会收到错误。

示例:INSERT IGNORE INTO urls SET url = 'url-to-insert'

【讨论】:

【参考方案10】:

首先要做的事情。如果您还没有创建表,或者您创建了一个表但其中没有数据,那么您需要添加一个唯一约束或唯一索引。有关在索引或约束之间进行选择的更多信息,请参见文章末尾。但它们都完成了同样的事情,强制列只包含唯一值。

要在此列上创建具有唯一索引的表,您可以使用。

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);

现在,您可能已经有了一个包含一些数据的表。在这种情况下,您可能已经有一些重复的数据。您可以尝试创建上面显示的约束或索引,如果您已经有重复数据,它将失败。如果您没有重复数据,很好,如果有,您将不得不删除重复数据。您可以使用以下查询查看大量带有重复的网址。

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 代码中处理由此产生的错误。现在,至于是否应该使用约束或索引,这取决于很多因素。索引可以加快查找速度,因此随着表变大,您的性能会更好,但是存储索引会占用额外的空间。索引通常也会使插入和更新花费更长的时间,因为它必须更新索引。但是,由于必须以任何一种方式查找该值,以强制执行唯一性,在这种情况下,无论如何只有索引可能会更快。至于与性能相关的任何事情,答案是尝试这两个选项并分析结果,看看哪个最适合您的情况。

【讨论】:

【参考方案11】:

如果您只想回答“是”或“否”,则此语法应该可以为您提供最佳性能。

select if(exists (select url from urls where url = 'http://asdf.com'), 1, 0) from dual

【讨论】:

【参考方案12】:

如果你只是想确保没有重复,那么在 url 字段中添加一个唯一索引,这样就不需要显式检查 url 是否存在,只需正常插入,如果它已经存在则插入将因重复键错误而失败。

【讨论】:

【参考方案13】:

答案取决于您是否想知道何时尝试输入具有重复字段的记录。如果您不在乎,请使用“INSERT... ON DUPLICATE KEY”语法,因为这将使您的尝试悄悄成功,而不会创建重复。

另一方面,如果您想知道此类事件何时发生并阻止它,那么您应该使用唯一键约束,这将导致尝试的插入/更新失败并出现有意义的错误。

【讨论】:

【参考方案14】:
$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

【讨论】:

【参考方案15】:

将列设为primary key

【讨论】:

【参考方案16】:

您可以使用自联接来定位(和删除)。您的表格有一些 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')

如果你得到一个结果,那么你可以断定该值已经存在于你的数据库中至少一次。

【讨论】:

【参考方案17】:

你可以这样查询:

SELECT url FROM urls WHERE url = 'http://asdf.com' LIMIT 1

然后检查mysql_num_rows() == 1是否存在。

【讨论】:

从插入时开始检查时,如何防止另一个连接进入具有该值的行?

以上是关于如何检查一个值是不是已经存在以避免重复?的主要内容,如果未能解决你的问题,请参考以下文章

Python检查字典中是不是存在值[重复]

如何避免在 SQL Server 中重复插入动态值

如何避免嵌套的空检查[重复]

如何编写一个触发器来检查一个值是不是已经在表中?

Wordpress:如何以编程方式创建帖子并在之前检查重复项

检查python列表中是不是已经存在一个数字