Как проверить, существует ли уже значение, чтобы избежать дублирования? - PullRequest
30 голосов
/ 14 сентября 2008

У меня есть таблица URL-адресов, и я не хочу дублировать URL-адреса. Как проверить, присутствует ли данный URL в таблице с использованием PHP / MySQL?

Ответы [ 17 ]

39 голосов
/ 14 сентября 2008

Если вы не хотите иметь дубликаты, вы можете сделать следующее:

Если несколько пользователей могут вставить данные в БД, метод, предложенный @Jeremy Ruten, может привести к ошибке : после выполнения проверки кто-то может вставить аналогичные данные в таблицу.

23 голосов
/ 20 августа 2011

Чтобы ответить на ваш первоначальный вопрос, самый простой способ проверить, есть ли дубликат, - выполнить 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 ("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 он обновляет время последнего посещения и не выдает ошибку!

Это потому, что 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!

14 голосов
/ 17 августа 2011

Сначала подготовьте базу данных .

  • Доменные имена не чувствительны к регистру, но вы должны предположить, что остальные URL-адреса. (Не все веб-серверы учитывают регистр в URL-адресах, но большинство это делают, и вы не можете легко определить, посмотрев.)
  • Предполагая, что вам нужно хранить не только доменное имя, используйте сортировку с учетом регистра.
  • Если вы решили сохранить URL-адрес в двух столбцах - один для имени домена и один для локатора ресурсов - рассмотрите возможность использования сортировки без учета регистра для имени домена и сортировки с учетом регистра для локатора ресурса , На вашем месте я бы протестировал оба способа (URL в одном столбце или URL в двух столбцах).
  • Установите уникальное ограничение для столбца URL. Или для пары столбцов, если вы храните имя домена и указатель ресурса в отдельных столбцах, например UNIQUE (url, resource_locator).
  • Используйте ограничение CHECK (), чтобы сохранить закодированные URL-адреса вне базы данных. Это ограничение CHECK () необходимо для предотвращения поступления плохих данных через массовую копию или через оболочку SQL.

Во-вторых, подготовьте URL .

  • Доменные имена не чувствительны к регистру. Если вы храните полный URL-адрес в одном столбце, строчные имена доменов на всех URL-адресах. Но учтите, что в некоторых языках есть заглавные буквы, которые не имеют строчных эквивалентов.
  • Подумайте об обрезке конечных символов. Например, эти два URL-адреса с amazon.com указывают на один и тот же продукт. Возможно, вы хотите сохранить вторую версию, а не первую.

    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 urldecode () . Внимательно обратите внимание на ее недостатки, как описано в комментариях к этой странице.) Лично я предпочел бы обрабатывать подобные преобразования в базе данных, а не в коде клиента. Это будет включать отзыв разрешений для таблиц и представлений и разрешение на вставку и обновление только через хранимые процедуры; хранимые процедуры обрабатывают все строковые операции, которые помещают URL в каноническую форму. Но следите за производительностью, когда вы попробуете это. Ограничения CHECK () (см. Выше) - это ваша сеть безопасности.

Третий , если вы вставляете только URL, сначала не проверяйте его существование . Вместо этого попытайтесь вставить и перехватить ошибку, которую вы получите, если значение уже существует. Тестирование и вставка попадает в базу данных дважды для каждого нового URL. Insert-and-trap просто попадает в базу данных один раз. Обратите внимание, что вставка и прерывание - это не то же самое, что вставка и игнорирование ошибок. Только одна конкретная ошибка означает, что вы нарушили уникальное ограничение; другие ошибки означают, что есть другие проблемы.

С другой стороны, если вы вставляете URL-адрес вместе с некоторыми другими данными в той же строке, вам необходимо заранее решить, будете ли вы обрабатывать дублирующиеся URL-адреса с помощью

  • удаление старой строки и вставка новой (см. Расширение MySQL REPLACE для SQL )
  • обновление существующих значений (см. ON DUPLICATE KEY UPDATE )
  • игнорируя проблему
  • требуя от пользователя дальнейших действий

REPLACE устраняет необходимость отлавливать повторяющиеся ошибки ключей, но может иметь нежелательные побочные эффекты при наличии ссылок на внешние ключи.

14 голосов
/ 14 сентября 2008

Вас беспокоят только те URL, которые являются точно такой же строкой? Если да, то в других ответах есть много полезных советов. Или вам также нужно беспокоиться о канонизации?

Например: http://google.com и http://go%4fgle.com являются точно такими же URL-адресами, но допускаются в качестве дубликатов любым из методов только для базы данных. Если это проблема, вы должны предварительно обработать URL-адреса для разрешения и символьные escape-последовательности.

В зависимости от того, откуда приходят URL-адреса, вам также придется беспокоиться о параметрах и о том, имеют ли они значение в вашем приложении.

13 голосов
/ 14 сентября 2008

Чтобы гарантировать уникальность, вам нужно добавить уникальное ограничение. Предполагая, что имя вашей таблицы - "urls", а имя столбца - "url", вы можете добавить уникальное ограничение с помощью этой команды alter table:

alter table urls add constraint unique_url unique (url);

Таблица alter, вероятно, потерпит неудачу (кто действительно знает, что такое MySQL), если у вас уже есть дублирующиеся URL в вашей таблице.

6 голосов
/ 17 августа 2011

Простые SQL-решения требуют уникального поля; логических решений нет.

Вам следует нормализовать свои URL, чтобы избежать дублирования. Функции в PHP, такие как strtolower () и urldecode () или rawurldecode () .

Допущения: имя вашей таблицы - "веб-сайты", имя столбца для вашего URL-адреса - "url", а произвольные данные, связанные с URL-адресом, - в столбце "данные".

Логические решения

SELECT COUNT(*) AS UrlResults FROM websites WHERE url='http://www.domain.com'

Протестируйте предыдущий запрос с инструкциями if в SQL или PHP, чтобы убедиться, что он равен 0, прежде чем продолжить работу с оператором INSERT.

Простые операторы SQL

Сценарий 1: Ваша база данных является таблицей "первым пришел - первым обслужен", и у вас нет желания иметь повторяющиеся записи в будущем.

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, если ее нет.

4 голосов
/ 21 августа 2011

При рассмотрении решения этой проблемы вам необходимо сначала определить, что означает «дублирующий URL» для вашего проекта. Это определит, как канонизировать URL-адреса перед их добавлением в базу данных.

Существует как минимум два определения:

  1. Два 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-адреса не отправляется на веб-сервер.
    • Сокращенный адрес IPv6 может быть расширен.
    • Добавьте косую черту только к полномочию , если оно отсутствует.
    • Канонизация Unicode изменяет указанный ресурс; например Вы не можете сделать вывод, что http://google.com/?q=%C3%84 (%C3%84 представляет 'Ä' в UTF-8) - это то же самое, что http://google.com/?q=A%CC%88 (%CC%88 представляет U + 0308, ДИАРЕЗ СОЧЕТАНИЯ).
    • Если используется схема HTTP или HTTPS, «www.» в полномочиях одного URL-адреса нельзя просто удалить, если два URL-адреса в противном случае эквивалентны, поскольку текст имени домена отправляется как значение Host Заголовок HTTP, а некоторые веб-серверы используют виртуальные хосты для отправки различного контента на основе этого заголовка. В более общем смысле, даже если доменные имена разрешаются на один и тот же IP-адрес, вы не можете сделать вывод, что указанные ресурсы совпадают.
  2. Применить базовую канонизацию URL (например, строчные буквы схемы и имени домена, указать порт по умолчанию, параметры запроса стабильной сортировки по имени параметра, удалить часть хеша в случае HTTP и HTTPS, ...), и принять во внимание знание веб-службы. Возможно, вы предположите, что все веб-службы достаточно умны, чтобы канонизировать ввод Unicode (например, Wikipedia), поэтому вы можете применить Форма нормализации Unicode Каноническая композиция (NFC). Вы бы удалили 'www.' со всех URL переполнения стека. Вы можете использовать код 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 для кортежа (`схема` /` canonical_scheme`, `canonical_login`,` canonical_host`, `port`,` canonical_path`), поскольку MySQL ограничивает длину InnoDB ключи до 767 байт.

2 голосов
/ 14 сентября 2008

Я не знаю синтаксис для MySQL, но все, что вам нужно сделать, - это обернуть вашу INSERT инструкцией IF, которая запросит таблицу и проверит, существует ли запись с данным URL, если она существует, - не вставляйте Новая запись.

если MSSQL, вы можете сделать это:

IF NOT EXISTS (SELECT 1 FROM YOURTABLE WHERE URL = 'URL')
INSERT INTO YOURTABLE (...) VALUES (...)
1 голос
/ 15 сентября 2008

Если вы хотите вставить URL-адреса в таблицу, но только те, которые уже не существуют, вы можете добавить УНИКАЛЬНОЕ ограничение в столбце, а в запросе INSERT добавить IGNORE, чтобы не получить ошибку. *

Пример: INSERT IGNORE INTO urls SET url = 'url-to-insert'

1 голос
/ 20 августа 2011

обо всем по порядку. Если вы еще не создали таблицу или создали таблицу, но в ней нет данных, вам необходимо добавить уникальный констант или уникальный индекс. Более подробная информация о выборе между индексом или ограничениями приведена в конце публикации. Но оба они выполняют одно и то же, обеспечивая, чтобы столбец содержал только уникальные значения.

Чтобы создать таблицу с уникальным индексом для этого столбца, вы можете использовать.

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-коде. Теперь о том, следует ли использовать ограничения или индексы, зависит от множества факторов. Индексы ускоряют поиск, поэтому ваша производительность будет лучше по мере увеличения таблицы, но хранение индекса займет дополнительное место. Индексы также обычно делают вставки, и обновления также занимают больше времени, потому что это должно обновить индекс. Однако, поскольку значение нужно искать в любом случае, для обеспечения уникальности, в этом случае, в любом случае, может быть быстрее просто иметь индекс. Что касается всего, что связано с производительностью, ответ - попробуйте оба варианта и профилируйте результаты, чтобы увидеть, что лучше всего подходит для вашей ситуации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...