2008-09-14 30 views
28

Tengo una tabla de URL y no quiero ninguna URL duplicada. ¿Cómo verifico si una URL dada ya está en la tabla usando PHP/MySQL?¿Cómo verificar si ya existe un valor para evitar duplicados?

+2

Muchas respuestas sugieren agregar una restricción 'ÚNICA' a una columna' url \ '. Una cosa a tener en cuenta es que MySQL limita el tamaño de las teclas. Dependiendo de la cantidad máxima de bytes que permitirá en una URL, esto podría ser un problema. [El manual de referencia 5.6 indica] (http://dev.mysql.com/doc/refman/5.6/en/create-index.html): "El prefijo [A] puede tener hasta 1000 bytes de longitud para las tablas MyISAM, y 767 bytes para tablas InnoDB ". –

Respuesta

39

Si no quiere tener duplicados que puede hacer lo siguiente:

Si varios usuarios pueden inserte datos en DB, método sugerido por @Jeremy Ruten, puede dar lugar a un error: después de realizar un control, alguien puede insertar datos similares en la tabla.

+0

Si está insertando un duplicado, entonces 'INSERT IGNORE' debe ser más rápido que 'REPLACE'. Como una ventaja adicional, puede saber si es nuevo ya que MySQL devuelve el número de filas afectadas (con 'ROW_COUNT()' o la API). También funciona con una inserción de varias filas. –

-1

que podría hacer esta consulta:

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

A continuación, compruebe si mysql_num_rows() == 1 para ver si existe.

+0

¿Cómo se evita que otra conexión entre en una fila con ese valor entremedio cuando se marca desde cuándo se inserta? –

+1

envuélvalo en TRANSACTION –

2

no conozco la sintaxis de MySQL, pero todo lo que necesita hacer es ajustar su INSERT con la instrucción IF que consultará la tabla y verá si el registro con la URL dada EXISTE, si existe - no inserte un nuevo registro.

si MSSQL se puede hacer esto:

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

Puede que no siempre funcione en un entorno concurrente. –

0

Si solo quiere una respuesta afirmativa o negativa, esta sintaxis debería ofrecerle el mejor rendimiento.

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

Si lo que desea es asegurarse de que no hay duplicados a continuación, agregar un índice único en el campo URL, de esa manera no hay necesidad de comprobar explícitamente si el URL existe, basta con insertar de forma normal, y si ya está allí, la inserción fallará con un error de clave duplicado.

13

Para garantizar la exclusividad, debe agregar una restricción única. Asumiendo que su nombre de tabla es "URL" y el nombre de la columna es "URL", se puede añadir la restricción único con este comando alter table:

alter table urls add constraint unique_url unique (url); 

La modificación de tabla probablemente se producirá un error (que realmente sabe con MySQL) si Ya tienes URLs duplicadas en tu mesa.

14

¿Le preocupan exclusivamente las URL que son exactamente la misma cadena? En caso afirmativo, hay muchos consejos útiles en otras respuestas. ¿O también tienes que preocuparte por la canonización?

Por ejemplo: http://google.com y http://go%4fgle.com son exactamente la misma URL, pero se permiten como duplicados por cualquiera de las técnicas de solo base de datos. Si esto es un problema, debe preprocesar las URL para resolver y las secuencias de escape de caracteres.

Dependiendo de dónde provengan las URL, también tendrá que preocuparse por los parámetros y si son significativos en su aplicación.

1

Si desea insertar urls en la tabla, pero solo aquellos que no existen ya puede agregar un CONTRATO UNICO en la columna y en su consulta INSERT agregar IGNORE para que no obtenga un error.

Ejemplo: INSERT IGNORE EN urls url = SET 'url-a-insert'

0

La respuesta depende de si desea saber si se ha hecho un intento de introducir un registro con un campo duplicado. Si no te importa, utiliza la sintaxis "INSERTAR ... EN LA CLAVE DUPLICADA", ya que esto hará que tu intento tenga éxito silenciosamente sin crear un duplicado.

Si, por otro lado, desea saber cuándo ocurre un evento de ese tipo y evitarlo, debe usar una restricción de clave única que hará que el intento de inserción/actualización falle con un error significativo.

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 
} 
6

Las soluciones SQL simples requieren un campo único; las soluciones lógicas no.

Debe normalizar sus direcciones URL para asegurarse de que no haya duplicaciones. Funciones en PHP como strtolower() y urldecode() o rawurldecode().

Suposiciones: El nombre de su tabla es 'sitios web', el nombre de columna de su url es 'url' y los datos arbitrarios que se asociarán con la url se encuentran en la columna 'datos'.

Soluciones Logic

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

probar la consulta previa con las instrucciones if en SQL o PHP para asegurarse de que es 0 antes de continuar con una instrucción INSERT.

sentencias SQL simples

Escenario 1: su base de datos es un primer llegado primer servido mesa y no tiene ningún deseo de tener entradas duplicadas en el futuro.

ALTER TABLE websites ADD UNIQUE (url) 

Esto evitará que cualquier entrada se pueda ingresar a la base de datos si el valor de la URL ya existe en esa columna.

Escenario 2: Desea la información más actualizada para cada url y no desea duplicar el contenido. Hay dos soluciones para este escenario. (Estas soluciones también requieren 'url' a ser única para que la solución en Escenario 1 también tendrá que llevarse a cabo.)

REPLACE INTO websites (url, data) VALUES ('http://www.domain.com', 'random data') 

Esto activará una acción de eliminación si existe una fila seguido por un inserto en todos los casos, así que ten cuidado con las declaraciones ON DELETE.

INSERT INTO websites (url, data) VALUES ('http://www.domain.com', 'random data') 
ON DUPLICATE KEY UPDATE data='random data' 

Esto disparará una acción ACTUALIZAR si existe una fila y un INSERTAR si no existe.

14

Primero, prepare la base de datos.

  • Los nombres de dominio no distinguen entre mayúsculas y minúsculas, pero debe suponer que el resto de una URL sí lo es. (No todos los servidores web respetan el caso en las URL, pero la mayoría lo hace, y no se puede ver fácilmente al mirar).)
  • Suponiendo que necesita almacenar más que un nombre de dominio, use una intercalación que distinga entre mayúsculas y minúsculas.
  • Si decide almacenar la URL en dos columnas, una para el nombre de dominio y otra para el localizador de recursos, considere usar una intercalación de mayúsculas y minúsculas para el nombre de dominio y una intercalación de mayúsculas y minúsculas para el localizador de recursos . Si yo fuera tú, probaría en ambos sentidos (URL en una columna vs. URL en dos columnas).
  • Ponga una restricción ÚNICA en la columna URL. O en el par de columnas, si almacena el nombre de dominio y el localizador de recursos en columnas separadas, como UNIQUE (url, resource_locator).
  • Use una restricción CHECK() para mantener las URL codificadas fuera de la base de datos. Esta restricción CHECK() es esencial para evitar que ingresen datos incorrectos a través de una copia masiva o mediante el shell de SQL.

En segundo lugar, prepare la dirección URL.

  • Los nombres de dominio no distinguen mayúsculas de minúsculas. Si almacena la URL completa en una columna, ponga el nombre de dominio en minúscula en todas las URL. Pero tenga en cuenta que algunos idiomas tienen letras mayúsculas que no tienen un equivalente en minúsculas.
  • Piense en recortar los caracteres finales. Por ejemplo, estas dos URL de amazon.com apuntan al mismo producto. Probablemente desee almacenar la segunda versión, no la primera.

    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

  • Decodificar URLs codificados. (Consulte php's urldecode() function. Observe cuidadosamente sus deficiencias, tal como se describe en los comentarios de esa página.) Personalmente, preferiría manejar este tipo de transformaciones en la base de datos en lugar de en el código del cliente. Eso implicaría revocar permisos en las tablas y vistas, y permitir inserciones y actualizaciones solo a través de procedimientos almacenados; los procedimientos almacenados manejan todas las operaciones de cadena que ponen la URL en una forma canónica. Pero fíjate en el rendimiento cuando lo intentes. Las restricciones CHECK() (ver arriba) son su red de seguridad.

Tercer, si está introduciendo la URL única, NO probar primero su existencia. En su lugar, intente insertar y atrapar el error que obtendrá si el valor ya existe. Las pruebas y las inserciones golpean la base de datos dos veces por cada nueva URL. Insertar y atrapar solo golpea la base de datos una vez. Observe cuidadosamente que insert-and-trap no es lo mismo que insert-and-ignore-errors. Solo un error en particular significa que violó la restricción única; otros errores significan que hay otros problemas.

Por otro lado, si vas a insertar la URL junto con algunos otros datos en la misma fila, debe decidir de antemano si va a manejar URLs duplicadas por

  • borrar el viejo fila y la inserción de una nueva (Ver REPLACE extension to SQL de MySQL)
  • la actualización de los valores existentes (Ver ON DUPLICATE KEY UPDATE)
  • ignorar el problema
  • que requiere que el usuario tome medidas adicionales

REEMPLAZAR elimina la necesidad de atrapar errores clave duplicados, pero podría tener efectos secundarios desafortunados si hay referencias de claves externas.

+1

¿Qué tal agregar un urldecode() a la URL para evitar el problema planteado en la respuesta de Rob Walker? O al menos a la parte del nombre de dominio – Mike

+1

PHP está fuera de dbms, lo que significa que cualquier otra aplicación que pueda insertar una URL tiene que recordar pasar por su aplicación PHP o desarrollar código que tenga el mismo comportamiento. Pero usar urldecode() fuera de las restricciones db y CHECK() dentro de db es un enfoque defendible, dependiente de la aplicación. –

+1

El OP dijo PHP/MySQL, sin embargo, esto también podría hacerse usando un procedimiento almacenado (por ejemplo, http://snippets.dzone.com/posts/show/7746) – Mike

0

Haga la columna de primary key

23

Para responder a su pregunta inicial, la forma más fácil de comprobar si existe un duplicado es ejecutar una consulta SQL en lo que estás tratando de añadir!

por ejemplo, se le desea comprobar si la URL http://www.example.com/ en la tabla links, a continuación, la consulta sería algo como

SELECT * FROM links WHERE url = 'http://www.example.com/'; 

su código PHP sería algo como

$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'); 
} 

Lo he escrito aquí a mano, con todas las conexiones a la base de datos, etc. Es probable que ya tenga una conexión a una base de datos, por lo que debe usar eso en lugar de comenzar una nueva conexión (reemplace.210 en el comando mysql_query y retirar el material que ver con mysql_connect y mysql_select_db)

Por supuesto, hay otras formas de conectarse a la base de datos, como DOP, o el uso de un ORM, o similares, por lo que si usted ya está al usarlos, esta respuesta puede no ser relevante (¡y probablemente esté un poco fuera del alcance dar respuestas relacionadas con esto aquí!)

Sin embargo, MySQL ofrece muchas maneras de evitar que esto suceda en primer lugar.

En primer lugar, puede marcar un campo como "único".

Digamos que tengo una tabla donde quiero simplemente almacenar todas las URL que están vinculadas desde mi sitio, y la última vez que fueron visitadas.

Mi definición podría ser algo como esto: -

CREATE TABLE links 
(
    url VARCHAR(255) NOT NULL, 
    last_visited TIMESTAMP 
) 

Esto me permitiría añadir a la misma URL una y otra vez, a menos que escribí algo de código PHP similar a la anterior para evitar que esto ocurra.

Sin embargo, eran mi definición de cambiar a

CREATE TABLE links 
(
    url VARCHAR(255) NOT NULL, 
    last_visited TIMESTAMP, 
    PRIMARY KEY (url) 
) 

Entonces esto haría MySQL generará un error cuando trataba de insertar el mismo valor dos veces.

Un ejemplo en PHP sería

$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'); 
} 

Si ejecutó esto, que iba a encontrar que en el primer intento, el guión iba a morir con el comentario Could not Insert Row 2. Sin embargo, en ejecuciones posteriores, moriría con Could not Insert Row 1.

Esto se debe a que MySQL sabe que la url es Principal Clave de la tabla. Una clave principal es un identificador único para esa fila. La mayoría de las veces, es útil establecer el identificador único para una fila como un número. Esto es porque MySQL es más rápido buscando números que buscando texto. Dentro de MySQL, las claves (y especialmente las teclas principales) se utilizan para definir relaciones entre dos tablas. Por ejemplo, si tuviéramos una mesa para los usuarios, podríamos definirlo como

CREATE TABLE users (
    username VARCHAR(255) NOT NULL, 
    password VARCHAR(40) NOT NULL, 
    PRIMARY KEY (username) 
) 

Sin embargo, cuando queríamos para almacenar información sobre un puesto había hecho el usuario, tendríamos que almacenar el nombre de usuario con ese puesto para identificar que la publicación perteneció a ese usuario.

Ya he mencionado que MySQL es más rápido en la búsqueda de números que de cadenas, así que esto significaría que pasaríamos el tiempo buscando cadenas cuando no fuera necesario.

Para solucionar esto, podemos añadir una columna adicional, user_id, y hacer que la clave primaria (así que cuando buscando el registro de usuario basada en un poste, lo podemos encontrar más rápido)

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

Usted Notaré que también he agregado algo nuevo aquí - AUTO_INCREMENT. Esto básicamente nos permite dejar que ese campo se cuide a sí mismo. Cada vez que se inserta una nueva fila, agrega 1 al número anterior y la almacena, para que no tengamos que preocuparnos por la numeración, y podemos dejar que lo haga solo.

lo tanto, con la tabla anterior, podemos hacer algo como

INSERT INTO users (username, password) VALUES('Mez', 'd3571ce95af4dc281f142add33384abc5e574671'); 

y luego

INSERT INTO users (username, password) VALUES('User', '988881adc9fc3655077dc2d4d757d480b5ea0e11'); 

Cuando seleccionamos los registros de la base de datos, obtenemos lo siguiente: -

mysql> SELECT * FROM users; 
+---------+----------+------------------------------------------+ 
| user_id | username | password         | 
+---------+----------+------------------------------------------+ 
|  1 | Mez  | d3571ce95af4dc281f142add33384abc5e574671 | 
|  2 | User  | 988881adc9fc3655077dc2d4d757d480b5ea0e11 | 
+---------+----------+------------------------------------------+ 
2 rows in set (0.00 sec) 

Sin embargo, aquí tenemos un problema: ¡aún podemos agregar otro usuario con el mismo nombre de usuario! ¡Obviamente, esto es algo que no queremos hacer!

mysql> SELECT * FROM users; 
+---------+----------+------------------------------------------+ 
| user_id | username | password         | 
+---------+----------+------------------------------------------+ 
|  1 | Mez  | d3571ce95af4dc281f142add33384abc5e574671 | 
|  2 | User  | 988881adc9fc3655077dc2d4d757d480b5ea0e11 | 
|  3 | Mez  | d3571ce95af4dc281f142add33384abc5e574671 | 
+---------+----------+------------------------------------------+ 
3 rows in set (0.00 sec) 

¡Cambiemos la definición de nuestra tabla!

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

Veamos qué sucede cuando intentamos insertar el mismo usuario dos veces.

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' 

Huzzah !! Ahora obtenemos un error cuando intentamos insertar el nombre de usuario por segunda vez. Usando algo como el anterior, podemos detectar esto en PHP.

Ahora, regresemos a nuestra tabla de enlaces, pero con una nueva definición.

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

y vamos a insertar "http://www.example.com" en la base de datos.

INSERT INTO links (url, last_visited) VALUES ('http://www.example.com/', NOW()); 

Si tratamos e insertarla de nuevo ....

ERROR 1062 (23000): Duplicate entry 'http://www.example.com/' for key 'url' 

¿Pero qué sucede si queremos actualizar la hora que era visitado por última vez?

Bueno, podríamos hacer algo complejo con PHP, así: -

$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'); 
    } 
} 

O, incluso agarrar el id de la fila en la base de datos y el uso que para actualizarlo.

$ result = mysql_query ("SELECCIONAR * FROM enlaces DONDE 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'); 
    } 
} 

Pero, MySQL tiene una buena función incorporada de llama REPLACE INTO

Vamos a ver cómo funciona.

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) 

Tenga en cuenta que cuando se utiliza REPLACE INTO, se pone al día el tiempo last_visited, y sin torcer un error!

Esto se debe a que MySQL detecta que está intentando reemplazar una fila. Conoce la fila que desea, ya que ha establecido que la URL sea única. MySQL calcula la fila para reemplazar utilizando el bit que pasó en que debe ser único (en este caso, la url) y actualizar para esa fila los otros valores. También se actualizó el link_id - ¡que es un poco inesperado! (De hecho, ¡no me di cuenta de que esto sucedería hasta que lo viera suceder!)

Pero, ¿y si quisieras agregar una nueva URL? ¡Bien, REPLACE INTO felizmente insertará una nueva fila si no puede encontrar una fila única coincidente!

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) 

Espero que esto responda a su pregunta y le proporcione un poco más de información sobre cómo funciona MySQL.

+12

Eep - ¡No me di cuenta de lo horrendomente larga que fue esta publicación! – Mez

+3

Creo que en realidad comienza con la pregunta anterior. – JNK

+2

El motivo 'REEMPLAZAR EN' actualizado el link_id es porque realmente hace un 'DELETE' y 'INSERT', en lugar de un 'UPDATE' - esto da miedo. Considere usar 'INSERT ON DUPLICATE KEY UPDATE' en su lugar. –

0

Puede ubicar (y eliminar) usando una autocombinación. La tabla tiene alguna URL y también algo de PK (Sabemos que la PK no es la URL porque de lo contrario no se le permitiría tener duplicados)

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 

Esto devolverá todas las filas que se han duplicado URL.

Digamos, sin embargo, que solo quería seleccionar duplicados y excluir el original .... Bueno, tendría que decidir qué constituye el original. A los efectos de esta respuesta vamos a suponer que el PK más bajo es el "original"

Todo lo que necesita hacer es añadir la siguiente cláusula a la consulta anterior:

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" 

Ahora usted tienen un conjunto de todos filas duplicadas no originales. Puede ejecutar fácilmente un DELETE o lo que quiera de este conjunto de resultados.

Tenga en cuenta que este enfoque puede ser ineficiente, en parte porque mySQL no siempre maneja bien IN pero entiendo por OP que esto es una especie de "limpieza" en la tabla, no siempre una verificación.

Si desea comprobar en INSERT tiempo si es o no un valor ya existe puede ejecutar algo como esto

SELECT 
    1 
WHERE 
    EXISTS (SELECT * FROM yourTable WHERE [URL] = 'testValue') 

Si se obtiene un resultado entonces se puede concluir el valor ya existe en su base de datos, al menos, una vez.

+0

@downvoter cuidado de explicar? – Matthew

1

Lo primero es lo primero. Si aún no ha creado la tabla, o si creó una tabla pero no tiene datos, entonces necesita agregar un constriant único o un índice único. Más información sobre elegir entre índice o restricciones sigue al final de la publicación. Pero ambos logran lo mismo, imponiendo que la columna solo contenga valores únicos.

Para crear una tabla con un índice único en esta columna, puede usar.

CREATE TABLE MyURLTable(
ID INTEGER NOT NULL AUTO_INCREMENT 
,URL VARCHAR(512) 
,PRIMARY KEY(ID) 
,UNIQUE INDEX IDX_URL(URL) 
); 

Si lo que desea es una restricción única, y ningún índice en esa tabla, puede utilizar

CREATE TABLE MyURLTable(
ID INTEGER NOT NULL AUTO_INCREMENT 
,URL VARCHAR(512) 
,PRIMARY KEY(ID) 
,CONSTRAINT UNIQUE UNIQUE_URL(URL) 
); 

Ahora, si ya dispone de una mesa, y no hay datos en él, a continuación, puede agregar el índice o la restricción a la tabla con uno de los siguientes fragmentos de código.

ALTER TABLE MyURLTable 
ADD UNIQUE INDEX IDX_URL(URL); 

ALTER TABLE MyURLTable 
ADD CONSTRAINT UNIQUE UNIQUE_URL(URL); 

Ahora, es posible que ya tenga una tabla con algunos datos. En ese caso, es posible que ya tenga algunos datos duplicados. Puede intentar crear el compilador o índice que se muestra arriba, y fallará si ya tiene datos duplicados. Si no tiene datos duplicados, genial, si lo hace, tendrá que eliminar los duplicados. Puedes ver un litro de URL con duplicados usando la siguiente consulta.

SELECT URL,COUNT(*),MIN(ID) 
FROM MyURLTable 
GROUP BY URL 
HAVING COUNT(*) > 1; 

Para eliminar las filas que están duplicados, y mantener uno, haga lo siguiente:

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; 

Ahora que ha eliminado todos los registros, puede seguir adelante y crear el índice o restricción. Ahora, si desea insertar un valor en su base de datos, debe usar algo como.

INSERT IGNORE INTO MyURLTable(URL) 
VALUES('http://www.example.com'); 

Eso intentará hacer la inserción, y si encuentra un duplicado, no pasará nada. Ahora, digamos que tiene otras columnas, puede hacer algo como esto.

INSERT INTO MyURLTable(URL,Visits) 
VALUES('http://www.example.com',1) 
ON DUPLICATE KEY UPDATE Visits=Visits+1; 

que buscará tratar de insertar el valor, y si encuentra la URL, a continuación, se actualizará el registro de incrementar el contador de visitas. Por supuesto, siempre puedes hacer un simple inserto antiguo y manejar el error resultante en tu código PHP. Ahora, si debe o no usar restricciones o índices, eso depende de muchos factores. Los índices hacen que las búsquedas sean más rápidas, por lo que su rendimiento será mejor a medida que la tabla aumente, pero almacenar el índice ocupará más espacio. Los índices también suelen hacer que las inserciones y actualizaciones tarden más, porque tienen que actualizar el índice. Sin embargo, dado que el valor tendrá que buscarse de cualquier manera, para hacer cumplir la unicidad, en este caso, puede ser más rápido tener el índice de todos modos. En cuanto a todo lo relacionado con el rendimiento, la respuesta es probar ambas opciones y perfilar los resultados para ver cuál funciona mejor para su situación.

4

Al considerar una solución a este problema, primero necesita definir qué significa una "URL duplicada" para su proyecto. Esto determinará cómo canonicalize las URL antes de agregarlas a la base de datos.

Existen al menos dos definiciones:

  1. dos direcciones URL se consideran duplicados si representan el mismo recurso no saber nada sobre el servicio web correspondiente que genera el contenido correspondiente. Algunas consideraciones incluyen:
    • El esquema y la porción del nombre de dominio de las URL no distinguen entre mayúsculas y minúsculas, por lo que HTTP://WWW.STACKOVERFLOW.COM/ es lo mismo que http://www.stackoverflow.com/.
    • Si una URL especifica un puerto, pero es el puerto convencional para el esquema y son equivalentes, entonces son los mismos (http://www.stackoverflow.com/ y http://www.stackoverflow.com:80/).
    • Si los parámetros en la cadena de consulta son reordenamientos simples y los nombres de los parámetros son todos diferentes, entonces son los mismos; p.ej. http://authority/?a=test&b=test y http://authority/?b=test&a=test. Tenga en cuenta que http://authority/?a%5B%5D=test1&a%5B%5D=test2 no es lo mismo, según esta primera definición de igualdad, como http://authority/?a%5B%5D=test2&a%5B%5D=test1.
    • Si el esquema es HTTP o HTTPS, las partes hash de las URL se pueden eliminar, ya que esta parte de la URL no se envía al servidor web.
    • Se puede ampliar una dirección IPv6 acortada.
    • Agregue una barra inclinada anterior a la autoridad solamente si falta.
    • La canonicalización Unicode cambia el recurso al que se hace referencia; p.ej. no puede concluir que http://google.com/?q=%C3%84 (%C3%84 representa 'Ä' en UTF-8) es lo mismo que http://google.com/?q=A%CC%88 (%CC%88 representa U + 0308, COMBINACIÓN DE DIAESIS).
    • Si el esquema es HTTP o HTTPS, 'www.' en la autoridad de una URL no puede eliminarse simplemente si las dos URL son equivalentes, ya que el texto del nombre de dominio se envía como el Host encabezado HTTP, y algunos servidores web usan hosts virtuales para enviar contenido diferente basado en este encabezado. En términos más generales, incluso si los nombres de dominio se resuelven en la misma dirección IP, no puede concluir que los recursos a los que se hace referencia son los mismos.
  2. Aplicar canonicalización URL básica (por ejemplo, minúsculas esquema y nombre de dominio, suministrar el puerto predeterminado, parámetros de consulta de ordenación estables por nombre de parámetro, eliminar la porción hash en el caso de HTTP y HTTPS, ...), y tienen en cuenta el conocimiento del servicio web. Tal vez suponga que todos los servicios web son lo suficientemente inteligentes como para canonicalizar la entrada Unicode (Wikipedia es, por ejemplo), por lo que puede aplicar Unicode Normalization Form Composición Canonical (NFC). Debería quitar "www." de todas las URL de desbordamiento de pila. Puede usar el código postrank-uri de PostRank, portado a PHP, para eliminar todo tipo de fragmentos de las URL innecesarias (por ejemplo, &utm_source=...).

Definición 1 lleva a una solución estable (es decir, no se puede llevar a cabo ninguna canonización adicional y la canonización de una URL no cambiará). La definición 2, que creo que es lo que un humano considera la definición de canonicalización de URL, conduce a una rutina de canonización que puede producir diferentes resultados en diferentes momentos en el tiempo.

Cualquiera que sea la definición que elija, le sugiero que utilice columnas separadas para las partes de esquema, inicio de sesión, host, puerto y ruta. Esto le permitirá usar índices inteligentemente. Las columnas para el esquema y el host pueden usar una intercalación de caracteres (todas las intercalaciones de caracteres no distinguen entre mayúsculas y minúsculas en MySQL), pero las columnas para el inicio de sesión y la ruta necesitan usar una intercalación binaria que no distinga entre mayúsculas y minúsculas. Además, si utiliza la Definición 2, debe conservar el esquema original, la autoridad y las partes de ruta, ya que ciertas reglas de canonización pueden agregarse o eliminarse de vez en cuando.

EDIT: Éstos son ejemplos de definiciones de tabla:

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

tabla `urls1` es para almacenar URLs canónicas de acuerdo con la definición 1. Tabla` urls2` es para almacenar URLs canónicas de acuerdo con la definición 2.

Desafortunadamente no podrá especificar una restricción UNIQUE en la tupla (`scheme` /` canonical_scheme`, `canonical_login`,` canonical_host`, `port`,` canonical_path`) ya que MySQL limita la longitud de las teclas InnoDB a 767 bytes.

+0

+1 para definir el problema. –

Cuestiones relacionadas