





Вы можете сделать такой запрос:
SELECT url FROM urls WHERE url = 'http://asdf.com' LIMIT 1
Затем проверьте, существует ли mysql_num_rows () == 1, чтобы узнать, существует ли он.
Как сделать так, чтобы другое соединение не вводило строку с этим значением между ними, когда вы проверяете, когда вставляете?
Я не знаю синтаксиса MySQL, но все, что вам нужно сделать, это обернуть ваш INSERT оператором IF, который будет запрашивать таблицу и видеть, СУЩЕСТВУЕТ ли запись с заданным URL-адресом, если она существует - не вставляйте новую запись.
если MSSQL вы можете сделать это:
IF NOT EXISTS (SELECT 1 FROM YOURTABLE WHERE URL = 'URL')
INSERT INTO YOURTABLE (...) VALUES (...)
Если вы не хотите иметь дубликаты, вы можете сделать следующее:
Если несколько пользователей могут вставлять данные в БД, метод, предложенный @Jeremy Ruten, может привести к ошибке: после того, как вы выполнили проверку, кто-то может вставить похожие данные в таблицу.
Если вы вставляете дубликат, то INSERT IGNORE должен быть быстрее, чем REPLACE. В качестве дополнительного бонуса вы можете узнать, был ли он новым, поскольку MySQL возвращает количество затронутых строк (с ROW_COUNT() или API). Он также работает со вставкой нескольких строк.
Если вам просто нужен ответ «да» или «нет», этот синтаксис должен дать вам наилучшую производительность.
select if (exists (select url from urls where url = 'http://asdf.com'), 1, 0) from dual
Если вы просто хотите убедиться, что нет дубликатов, добавьте уникальный индекс в поле URL-адреса, таким образом, нет необходимости явно проверять, существует ли URL-адрес, просто вставьте как обычно, и если он уже существует, тогда вставка будет сбой с ошибкой дублирования ключа.
Чтобы гарантировать уникальность, вам нужно добавить уникальное ограничение. Предполагая, что имя вашей таблицы - «urls», а имя столбца - «url», вы можете добавить уникальное ограничение с помощью этой команды изменения таблицы:
alter table urls add constraint unique_url unique (url);
Таблица alter, вероятно, выйдет из строя (кто действительно знает, с MySQL), если у вас уже есть повторяющиеся URL-адреса в вашей таблице.
Вас беспокоят только URL-адреса, которые представляют собой одну и ту же строку ... если это так, в других ответах есть много хороших советов. Или вам тоже нужно беспокоиться о канонизации?
Например: http://google.com и http: //go%4fgle.com - это один и тот же URL-адрес, но их можно использовать как дубликаты любым методом, использующим только базу данных. Если это проблема, вам следует предварительно обработать URL-адреса для разрешения и escape-последовательности символов.
В зависимости от того, откуда берутся URL-адреса, вам также придется беспокоиться о параметрах и о том, важны ли они в вашем приложении.
Если вы хотите вставить в таблицу URL-адреса, но только те, которые еще не существуют, вы можете добавить УНИКАЛЬНОЕ ограничение в столбце, а в запросе INSERT добавить IGNORE, чтобы не получить ошибку.
Пример: INSERT IGNORE INTO urls SET url = 'url-to-insert'
Ответ зависит от того, хотите ли вы знать, когда была предпринята попытка ввести запись с повторяющимся полем. Если вам все равно, используйте синтаксис «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 требуют уникального поля; логических решений нет.
Вам следует нормализовать свои URL-адреса, чтобы избежать дублирования. Функции в PHP, такие как strtolower () и urldecode () или rawurldecode ().
Предположения: имя вашей таблицы - «веб-сайты», имя столбца для вашего URL-адреса - «URL-адрес», а произвольные данные, которые должны быть связаны с URL-адресом, находятся в столбце «данные».
Логические решения
SELECT COUNT(*) AS UrlResults FROM websites WHERE url='http://www.domain.com'
Прежде чем продолжить выполнение инструкции INSERT, проверьте предыдущий запрос с помощью операторов if в SQL или PHP, чтобы убедиться, что он равен 0.
Простые операторы SQL
Сценарий 1: Ваш db - это таблица, работающая в порядке очереди, и у вас нет желания иметь повторяющиеся записи в будущем.
ALTER TABLE websites ADD UNIQUE (url)
Это предотвратит ввод любых записей в базу данных, если значение url уже существует в этом столбце.
Сценарий 2: Вам нужна самая свежая информация для каждого URL-адреса и вы не хотите дублировать контент. Для этого сценария есть два решения. (Эти решения также требуют, чтобы 'url' был уникальным, поэтому решение в Сценарий 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, если его нет.
Сначала подготовим базу данных.
UNIQUE (url, resource_locator).Во-вторых, подготовьте URL.
Подумайте об обрезке конечных символов. Например, эти два URL-адреса с amazon.com указывают на один и тот же продукт. Вероятно, вы захотите сохранить вторую версию, а не первую.
http://www.amazon.com/Systemantics-Systems-Work-Especial-They/dp/070450331X
Расшифровать закодированные URL-адреса. (См. функция php urldecode (). Внимательно обратите внимание на его недостатки, как описано в комментариях к этой странице.) Лично я предпочитаю обрабатывать такие преобразования в базе данных, а не в клиентском коде. Это потребует отмены разрешений для таблиц и представлений и разрешения вставок и обновлений только через хранимые процедуры; хранимые процедуры обрабатывают все строковые операции, которые переводят URL-адрес в каноническую форму. Но следите за производительностью, когда попробуете это. Ограничения CHECK () (см. Выше) - ваша подстраховка.
В третьих, если вы вставляете только URL, не проверяйте его существование в первую очередь. Вместо этого попробуйте вставить и перехватить ошибку, которую вы получите, если значение уже существует. Тестирование и вставка обращаются к базе данных дважды для каждого нового URL-адреса. Insert-and-trap попадает в базу данных только один раз. Обратите внимание, что вставка и ловушка - это не то же самое, что ошибки вставки и игнорирования. Только одна конкретная ошибка означает, что вы нарушили уникальное ограничение; другие ошибки означают, что есть другие проблемы.
С другой стороны, если вы вставляете URL-адрес вместе с некоторыми другими данными в той же строке, вам нужно заранее решить, будете ли вы обрабатывать повторяющиеся URL-адреса с помощью
REPLACE избавляет от необходимости перехватывать повторяющиеся ключевые ошибки, но может иметь нежелательные побочные эффекты, если есть ссылки на внешние ключи.
Как насчет добавления urldecode () к URL-адресу, чтобы обойти проблему, поднятую в ответе Роба Уокера? Или, по крайней мере, в части доменного имени.
PHP находится за пределами dbms, а это означает, что любое другое приложение, которое может вставлять URL-адрес, должно помнить, что нужно либо пройти через ваше приложение PHP, либо разработать код с таким же поведением. Но использование urldecode () вне ограничений db и CHECK () внутри db - это оправданный подход, зависящий от приложения.
OP действительно сказал PHP / MySQL, однако это также можно было сделать с помощью хранимой процедуры (например, snippets.dzone.com/posts/show/7746)
Да, именно это я имел в виду, когда сказал: «Я лучше буду обрабатывать подобные изменения в базе данных, а не в клиентском коде».
Сделайте колонку primary key
Чтобы ответить на ваш первоначальный вопрос, самый простой способ проверить, есть ли дубликат, - это запустить SQL-запрос к тому, что вы пытаетесь добавить!
Например, если вы хотите проверить URL-адрес http://www.example.com/ в таблице links, ваш запрос будет выглядеть примерно так:
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 предоставляет множество способов предотвратить это в первую очередь.
Во-первых, вы можете пометить поле как «уникальное».
Допустим, у меня есть таблица, в которой я хочу просто сохранить все 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-адрес является ключом Начальный таблицы. Первичный ключ - это уникальный идентификатор для этой строки. В большинстве случаев в качестве уникального идентификатора строки полезно использовать число. Это потому, что 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');
}
}
Или даже возьмите идентификатор строки в базе данных и используйте его для ее обновления.
$ result = mysql_query ("ВЫБРАТЬ * ИЗ ссылок 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!
Ээп - я не представлял, насколько ужасно длинным был этот пост!
Я думаю, что это действительно начинается с предыдущего вопроса.
Причина, по которой REPLACE INTO обновил link_id, заключается в том, что он фактически выполняет DELETE и INSERT, а не UPDATE - это пугает. Вместо этого рассмотрите возможность использования INSERT ON DUPLICATE KEY UPDATE.
Этот ответ заслуживает отдельной публикации в блоге.
Должен быть один из самых длинных ответов на SO. : o
Вы можете найти (и удалить), используя самостоятельное соединение. В вашей таблице есть 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')
Если вы получили результат, вы можете сделать вывод, что значение уже существует в вашей БД хотя бы один раз.
Перво-наперво. Если вы еще не создали таблицу или создали таблицу, но в нее нет данных, вам необходимо добавить уникальную константу или уникальный индекс. Более подробная информация о выборе между индексом или ограничениями приведена в конце сообщения. Но они оба выполняют одно и то же, обеспечивая, чтобы столбец содержал только уникальные значения.
Чтобы создать таблицу с уникальным индексом в этом столбце, вы можете использовать.
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);
Возможно, у вас уже есть таблица с некоторыми данными. В этом случае у вас уже могут быть дубликаты данных. Вы можете попробовать создать константу или индекс, показанные выше, и это не удастся, если у вас уже есть повторяющиеся данные. Если у вас нет повторяющихся данных, отлично, если у вас есть, вам придется удалить дубликаты. Вы можете увидеть список 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-коде. Теперь, что касается того, следует ли вам использовать ограничения или индексы, это зависит от множества факторов. Индексы ускоряют поиск, поэтому ваша производительность будет выше по мере увеличения таблицы, но для хранения индекса потребуется дополнительное место. Индексы также обычно заставляют вставки и обновления занимать больше времени, потому что они должны обновлять индекс. Однако, поскольку значение нужно будет искать в любом случае, чтобы обеспечить уникальность, в этом случае может быть быстрее просто получить индекс. Что касается всего, что связано с производительностью, ответ - попробовать оба варианта и профилировать результаты, чтобы увидеть, какой из них лучше всего подходит для вашей ситуации.
При рассмотрении решения этой проблемы вам необходимо сначала определить, что означает «повторяющийся URL-адрес» для вашего проекта. Это определит, как канонизировать URL-адреса перед добавлением их в базу данных.
Есть как минимум два определения:
%C3%84 представляет 'Ä' в UTF-8) то же самое, что http://google.com/?q=A%CC%88 (%CC%88 представляет U + 0308, ОБЪЕДИНЯЯ ДИАРЕЗ).www.» в центре одного URL-адреса не может быть просто удален, если два URL-адреса в остальном эквивалентны, поскольку текст имени домена отправляется как значение HTTP-заголовка Host, а некоторые веб-серверы использовать виртуальные хосты для отправки различного контента на основе этого заголовка. В более общем смысле, даже если имена доменов соответствуют одному и тому же IP-адресу, вы не можете сделать вывод, что ресурсы, на которые есть ссылки, одинаковы.www. из всех URL-адресов Stack Overflow. Вы можете использовать код 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 предназначена для хранения канонических URL в соответствии с определением 1. Таблица urls2 предназначена для хранения канонических URL в соответствии с определением 2.
К сожалению, вы не сможете указать ограничение UNIQUE для кортежа (`scheme` /` canonical_scheme`, `canonical_login`,` canonical_host`, `port`,` canonical_path`), поскольку MySQL ограничивает длину ключей InnoDB до 767 байтов. .
Во многих ответах предлагается добавить ограничение
UNIQUEв столбец url. Следует иметь в виду, что MySQL ограничивает размер ключей. В зависимости от максимального количества байтов, которое вы разрешаете в URL-адресе, это может быть проблемой. В справочном руководстве 5.6 говорится:: «Префикс [A] может иметь длину до 1000 байт для таблиц MyISAM и 767 байт для таблиц InnoDB».