<p>I've got a table of URLs and I don't want any duplicate URLs. How do I check to see if a given URL is already in the table using PHP/MySQL?</p>

[{'answer_id': 61035, 'author': 'Paige Ruten', 'author_id': 813, 'author_profile': 'https://Stackoverflow.com/users/813', 'pm_score': -1, 'selected': False, 'text': '<p>You could do this query:</p>\n\n<pre><code>SELECT url FROM urls WHERE url = \'http://asdf.com\' LIMIT 1\n</code></pre>\n\n<p>Then check if <a href="http://php.net/mysql_num_rows" rel="nofollow noreferrer">mysql_num_rows()</a> == 1 to see if it exists.</p>\n'}, {'answer_id': 61036, 'author': 'roman m', 'author_id': 3661, 'author_profile': 'https://Stackoverflow.com/users/3661', 'pm_score': 2, 'selected': False, 'text': "<p>i don't know the syntax for MySQL, but all you need to do is wrap your INSERT with IF statement that will query the table and see if the record with given url EXISTS, if it exists - don't insert a new record.</p>\n\n<p>if MSSQL you can do this:</p>\n\n<pre><code>IF NOT EXISTS (SELECT 1 FROM YOURTABLE WHERE URL = 'URL')\nINSERT INTO YOURTABLE (...) VALUES (...)\n</code></pre>\n"}, {'answer_id': 61039, 'author': 'aku', 'author_id': 1196, 'author_profile': 'https://Stackoverflow.com/users/1196', 'pm_score': 6, 'selected': True, 'text': '<p>If you don\'t want to have duplicates you can do following:</p>\n\n<ul>\n<li>add uniqueness constraint</li>\n<li>use "<a href="http://dev.mysql.com/doc/refman/5.0/en/replace.html" rel="noreferrer">REPLACE</a>" or "<a href="http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html" rel="noreferrer">INSERT ... ON DUPLICATE KEY UPDATE</a>" syntax</li>\n</ul>\n\n<p>If multiple users can insert data to DB, method suggested by @Jeremy Ruten, can <strong>lead to an error</strong>: after you performed a check someone can insert similar data to the table.</p>\n'}, {'answer_id': 61040, 'author': 'Brian Matthews', 'author_id': 1969, 'author_profile': 'https://Stackoverflow.com/users/1969', 'pm_score': 0, 'selected': False, 'text': "<p>If you just want a yes or no answer this syntax should give you the best performance.</p>\n\n<pre>select if(exists (select url from urls where url = 'http://asdf.com'), 1, 0) from dual</pre>\n"}, {'answer_id': 61043, 'author': 'Nathan', 'author_id': 6062, 'author_profile': 'https://Stackoverflow.com/users/6062', 'pm_score': 0, 'selected': False, 'text': '<p>If you just want to make sure there are no duplicates then add an unique index to the url field, that way there is no need to explicitly check if the url exists, just insert as normal, and if it is already there then the insert will fail with a duplicate key error.</p>\n'}, {'answer_id': 61045, 'author': 'Joe Mahoney', 'author_id': 575, 'author_profile': 'https://Stackoverflow.com/users/575', 'pm_score': 4, 'selected': False, 'text': '<p>To guarantee uniqueness you need to add a unique constraint. Assuming your table name is "urls" and the column name is "url", you can add the unique constraint with this alter table command: </p>\n\n<pre><code>alter table urls add constraint unique_url unique (url);\n</code></pre>\n\n<p>The alter table will probably fail (who really knows with MySQL) if you\'ve already got duplicate urls in your table already. </p>\n'}, {'answer_id': 61049, 'author': 'Rob Walker', 'author_id': 3631, 'author_profile': 'https://Stackoverflow.com/users/3631', 'pm_score': 4, 'selected': False, 'text': '<p>Are you concerned purely about URLs that are the exact same string .. if so there is a lot of good advice in other answers.  Or do you also have to worry about canonization?</p>\n\n<p>For example: <a href="http://google.com" rel="nofollow noreferrer">http://google.com</a> and <a href="http://go%4fgle.com" rel="nofollow noreferrer">http://go%4fgle.com</a> are the exact same URL, but would be allowed as duplicates by any of the database only techniques.  If this is an issue you  should preprocess the URLs to resolve and character escape sequences.</p>\n\n<p>Depending where the URLs are coming from you will also have to worry about parameters and whether they are significant in your application.</p>\n'}, {'answer_id': 62307, 'author': 'Jean Paul Galea', 'author_id': 6618, 'author_profile': 'https://Stackoverflow.com/users/6618', 'pm_score': 1, 'selected': False, 'text': "<p>If you want to insert urls into the table, but only those that don't exist already you can add a UNIQUE contraint on the column and in your INSERT query add IGNORE so that you don't get an error.</p>\n\n<p>Example: INSERT IGNORE INTO <code>urls</code> SET url = 'url-to-insert'</p>\n"}, {'answer_id': 62891, 'author': 'Community', 'author_id': -1, 'author_profile': 'https://Stackoverflow.com/users/-1', 'pm_score': 0, 'selected': False, 'text': '<p>The answer depends on whether you want to know when an attempt is made to enter a record with a duplicate field.   If you don\'t care then use the "INSERT... ON DUPLICATE KEY" syntax as this will make your attempt quietly succeed without creating a duplicate.</p>\n\n<p>If on the other hand you want to know when such an event happens and prevent it, then you should use a unique key constraint which will cause the attempted insert/update to fail with a meaningful error.</p>\n'}, {'answer_id': 7085163, 'author': 'Pedro Lobito', 'author_id': 797495, 'author_profile': 'https://Stackoverflow.com/users/797495', 'pm_score': 0, 'selected': False, 'text': '<pre><code>$url = "http://www.scroogle.com";\n\n$query  = "SELECT `id` FROM `urls` WHERE  `url` = \'$url\' ";\n$resultdb = mysql_query($query) or die(mysql_error());   \nlist($idtemp) = mysql_fetch_array($resultdb) ;\n\nif(empty($idtemp)) // if $idtemp is empty the url doesn\'t exist and we go ahead and insert it into the db.\n{ \n   mysql_query("INSERT INTO urls (`url` ) VALUES(\'$url\') ") or die (mysql_error());\n}else{\n   //do something else if the url already exists in the DB\n}\n</code></pre>\n'}, {'answer_id': 7087626, 'author': 'Steve Buzonas', 'author_id': 816584, 'author_profile': 'https://Stackoverflow.com/users/816584', 'pm_score': 3, 'selected': False, 'text': "<p>The simple SQL solutions require a unique field; the logic solutions do not.</p>\n\n<p>You should normalize your urls to ensure there is no duplication.  Functions in PHP such as <em>strtolower()</em> and <em>urldecode()</em> or <em>rawurldecode()</em>.</p>\n\n<p>Assumptions: Your table name is 'websites', the column name for your url is 'url', and the arbitrary data to be associated with the url is in the column 'data'.</p>\n\n<p><strong>Logic Solutions</strong></p>\n\n<pre><code>SELECT COUNT(*) AS UrlResults FROM websites WHERE url='http://www.domain.com'\n</code></pre>\n\n<p>Test the previous query with if statements in SQL or PHP to ensure that it is 0 before you continue with an INSERT statement.</p>\n\n<p><strong>Simple SQL Statements</strong></p>\n\n<p><strong>Scenario 1:</strong> Your db is a first come first serve table and you have no desire to have duplicate entries in the future.</p>\n\n<pre><code>ALTER TABLE websites ADD UNIQUE (url)\n</code></pre>\n\n<p>This will prevent any entries from being able to be entered in to the database if the url value already exists in that column.</p>\n\n<p><strong>Scenario 2:</strong> You want the most up to date information for each url and don't want to duplicate content.  There are two solutions for this scenario.  (These solutions also require 'url' to be unique so the solution in <em>Scenario 1</em> will also need to be carried out.)</p>\n\n<pre><code>REPLACE INTO websites (url, data) VALUES ('http://www.domain.com', 'random data')\n</code></pre>\n\n<p>This will trigger a DELETE action if a row exists followed by an INSERT in all cases, so be careful with ON DELETE declarations.</p>\n\n<pre><code>INSERT INTO websites (url, data) VALUES ('http://www.domain.com', 'random data')\nON DUPLICATE KEY UPDATE data='random data'\n</code></pre>\n\n<p>This will trigger an UPDATE action if a row exists and an INSERT if it does not.</p>\n"}, {'answer_id': 7093198, 'author': "Mike Sherrill 'Cat Recall'", 'author_id': 562459, 'author_profile': 'https://Stackoverflow.com/users/562459', 'pm_score': 4, 'selected': False, 'text': '<p><strong>First, prepare the database</strong>. </p>\n\n<ul>\n<li>Domain names aren\'t case-sensitive, but you have to assume the rest of a URL is. (Not all web servers respect case in URLs, but most do, and you can\'t easily tell by looking.)  </li>\n<li>Assuming you need to store more than a domain name, use a case-sensitive collation.</li>\n<li>If you decide to store the URL in two columns--one for the domain name and one for the resource locator--consider using a case-insensitive collation for the domain name, and a case-sensitive collation for the resource locator.  If I were you, I\'d test both ways (URL in one column vs. URL in two columns).</li>\n<li>Put a UNIQUE constraint on the URL column. Or on the pair of columns, if you store the domain name and resource locator in separate columns, as <code>UNIQUE (url, resource_locator)</code>.</li>\n<li>Use a CHECK() constraint to keep encoded URLs out of the database. This CHECK() constraint is essential to keep bad data from coming in through a bulk copy or through the SQL shell. </li>\n</ul>\n\n<p><strong>Second, prepare the URL</strong>. </p>\n\n<ul>\n<li>Domain names aren\'t case-sensitive. If you store the full URL in one column, lowercase the domain name on all URLs. But be aware that some languages have uppercase letters that have no lowercase equivalent.</li>\n<li><p>Think about trimming trailing characters. For example, these two URLs from amazon.com point to the same product. You probably want to store the second version, not the first.</p>\n\n<p><a href="https://rads.stackoverflow.com/amzn/click/com/070450331X" rel="noreferrer" rel="nofollow noreferrer">http://www.amazon.com/Systemantics-Systems-Work-Especially-They/dp/070450331X/ref=sr_1_1?ie=UTF8&amp;qid=1313583998&amp;sr=8-1</a></p>\n\n<p><a href="https://rads.stackoverflow.com/amzn/click/com/070450331X" rel="noreferrer" rel="nofollow noreferrer">http://www.amazon.com/Systemantics-Systems-Work-Especially-They/dp/070450331X</a></p></li>\n<li><p>Decode encoded URLs. (See <a href="http://php.net/manual/en/function.urldecode.php" rel="noreferrer">php\'s urldecode() function</a>. Note carefully its shortcomings, as described in that page\'s comments.) Personally, I\'d rather handle these kinds of transformations in the database rather than in client code. That would involve revoking permissions on the tables and views, and allowing inserts and updates only through stored procedures; the stored procedures handle all the string operations that put the URL into a canonical form. But keep an eye on performance when you try that. CHECK() constraints (see above) are your safety net.</p></li>\n</ul>\n\n<p><strong>Third</strong>, if you\'re inserting only the URL, <strong>don\'t test for its existence first</strong>. Instead, try to insert and trap the error that you\'ll get if the value already exists. Testing and inserting hits the database twice for every new URL. Insert-and-trap just hits the database once. Note carefully that insert-and-trap isn\'t the same thing as insert-and-ignore-errors. Only one particular error means you violated the unique constraint; other errors mean there are other problems.</p>\n\n<p>On the other hand, if you\'re inserting the URL along with some other data in the same row, you need to decide ahead of time whether you\'ll handle duplicate urls by</p>\n\n<ul>\n<li>deleting the old row and inserting a new one (See MySQL\'s <a href="http://dev.mysql.com/doc/refman/5.5/en/replace.html" rel="noreferrer">REPLACE extension to SQL</a>)</li>\n<li>updating existing values (See <a href="http://dev.mysql.com/doc/refman/5.5/en/insert-on-duplicate.html" rel="noreferrer">ON DUPLICATE KEY UPDATE</a>)</li>\n<li>ignoring the issue</li>\n<li>requiring the user to take further action</li>\n</ul>\n\n<p>REPLACE eliminates the need to trap duplicate key errors, but it might have unfortunate side effects if there are foreign key references.</p>\n'}, {'answer_id': 7122583, 'author': 'Matt', 'author_id': 61256, 'author_profile': 'https://Stackoverflow.com/users/61256', 'pm_score': 0, 'selected': False, 'text': '<p>Make the column the <code>primary key</code></p>\n'}, {'answer_id': 7128282, 'author': 'Mez', 'author_id': 20010, 'author_profile': 'https://Stackoverflow.com/users/20010', 'pm_score': 5, 'selected': False, 'text': '<p>To answer your initial question, the easiest way to check whether there is a duplicate is to run an SQL query against what you\'re trying to add!</p>\n\n<p>For example, were you to want to check for the url <code>http://www.example.com/</code> in the table <code>links</code>, then your query would look something like</p>\n\n<pre><code>SELECT * FROM links WHERE url = \'http://www.example.com/\';\n</code></pre>\n\n<p>Your PHP code would look something like</p>\n\n<pre><code>$conn = mysql_connect(\'localhost\', \'username\', \'password\');\nif (!$conn)\n{\n    die(\'Could not connect to database\');\n}\nif(!mysql_select_db(\'mydb\', $conn))\n{\n    die(\'Could not select database mydb\');\n}\n\n$result = mysql_query("SELECT * FROM links WHERE url = \'http://www.example.com/\'", $conn);\n\nif (!$result)\n{\n    die(\'There was a problem executing the query\');\n}\n\n$number_of_rows = mysql_num_rows($result);\n\nif ($number_of_rows &gt; 0)\n{\n    die(\'This URL already exists in the database\');\n}\n</code></pre>\n\n<p>I\'ve written this out longhand here, with all the connecting to the database, etc.  It\'s likely that you\'ll already have a connection to a database, so you should use that rather than starting a new connection (replace <code>$conn</code> in the <code>mysql_query</code> command and remove the stuff to do with <code>mysql_connect</code> and <code>mysql_select_db</code>)</p>\n\n<p>Of course, there are other ways of connecting to the database, like PDO, or using an ORM, or similar, so if you\'re already using those, this answer may not be relevant (and it\'s probably a bit beyond the scope to give answers related to this here!)</p>\n\n<p>However, MySQL provides many ways to prevent this from happening in the first place.</p>\n\n<p>Firstly, you can mark a field as "unique".</p>\n\n<p>Lets say I have a table where I want to just store all the URLs that are linked to from my site, and the last time they were visited.</p>\n\n<p>My definition might look something like this:-</p>\n\n<pre><code>CREATE TABLE links\n(\n    url VARCHAR(255) NOT NULL,\n    last_visited TIMESTAMP\n)\n</code></pre>\n\n<p>This would allow me to add the same URL over and over again, unless I wrote some PHP code similar to the above to stop this happening.</p>\n\n<p>However, were my definition to change to</p>\n\n<pre><code>CREATE TABLE links\n(\n  url VARCHAR(255)  NOT NULL,\n  last_visited TIMESTAMP,\n  PRIMARY KEY (url)\n)\n</code></pre>\n\n<p>Then this would make mysql throw an error when I tried to insert the same value twice.</p>\n\n<p>An example in PHP would be</p>\n\n<pre><code>$result = mysql_query("INSERT INTO links (url, last_visited) VALUES (\'http://www.example.com/\', NOW()", $conn);\n\nif (!$result)\n{\n    die(\'Could not Insert Row 1\');\n}\n\n$result2 = mysql_query("INSERT INTO links (url, last_visited) VALUES (\'http://www.example.com/\', NOW()", $conn);\n\nif (!$result2)\n{\n    die(\'Could not Insert Row 2\');\n}\n</code></pre>\n\n<p>If you ran this, you\'d find that on the first attempt, the script would die with the comment <code>Could not Insert Row 2</code>.  However, on subsequent runs, it\'d die with <code>Could not Insert Row 1</code>.</p>\n\n<p>This is because MySQL knows that the url is the <em>Primary</em> Key of the table.  A Primary key is a unique identifier for that row.  Most of the time, it\'s useful to set the unique identifier for a row to be a number.  This is because MySQL is quicker at looking up numbers than it is looking up text.  Within MySQL, keys (and espescially Primary Keys) are used to define relationships between two tables.  For example, if we had a table for users, we could define it as</p>\n\n<pre><code>CREATE TABLE users (\n  username VARCHAR(255)  NOT NULL,\n  password VARCHAR(40) NOT NULL,\n  PRIMARY KEY (username)\n)\n</code></pre>\n\n<p>However, when we wanted to store information about a post the user had made, we\'d have to store the username with that post to identify that the post belonged to that user.</p>\n\n<p>I\'ve already mentioned that MySQL is faster at looking up numbers than strings, so this would mean we\'d be spending time looking up strings when we didn\'t have to.</p>\n\n<p>To solve this, we can add an extra column, user_id, and make that the primary key (so when looking up the user record based on a post, we can find it quicker)</p>\n\n<pre><code>CREATE TABLE users (\n  user_id INT(10)  NOT NULL AUTO_INCREMENT,\n  username VARCHAR(255)  NOT NULL,\n  password VARCHAR(40)  NOT NULL,\n  PRIMARY KEY (`user_id`)\n)\n</code></pre>\n\n<p>You\'ll notice that I\'ve also added something new here - AUTO_INCREMENT.  This basically allows us to let that field look after itself.  Each time a new row is inserted, it adds 1 to the previous number, and stores that, so we don\'t have to worry about numbering, and can just let it do this itself.</p>\n\n<p>So, with the above table, we can do something like</p>\n\n<pre><code>INSERT INTO users (username, password) VALUES(\'Mez\', \'d3571ce95af4dc281f142add33384abc5e574671\');\n</code></pre>\n\n<p>and then </p>\n\n<pre><code>INSERT INTO users (username, password) VALUES(\'User\', \'988881adc9fc3655077dc2d4d757d480b5ea0e11\');\n</code></pre>\n\n<p>When we select the records from the database, we get the following:-</p>\n\n<pre><code>mysql&gt; SELECT * FROM users;\n+---------+----------+------------------------------------------+\n| user_id | username | password                                 |\n+---------+----------+------------------------------------------+\n|       1 | Mez      | d3571ce95af4dc281f142add33384abc5e574671 |\n|       2 | User     | 988881adc9fc3655077dc2d4d757d480b5ea0e11 |\n+---------+----------+------------------------------------------+\n2 rows in set (0.00 sec)\n</code></pre>\n\n<p>However, here - we have a problem - we can still add another user with the same username!  Obviously, this is something we don\'t want to do!</p>\n\n<pre><code>mysql&gt; SELECT * FROM users;\n+---------+----------+------------------------------------------+\n| user_id | username | password                                 |\n+---------+----------+------------------------------------------+\n|       1 | Mez      | d3571ce95af4dc281f142add33384abc5e574671 |\n|       2 | User     | 988881adc9fc3655077dc2d4d757d480b5ea0e11 |\n|       3 | Mez      | d3571ce95af4dc281f142add33384abc5e574671 |\n+---------+----------+------------------------------------------+\n3 rows in set (0.00 sec)\n</code></pre>\n\n<p>Lets change our table definition!</p>\n\n<pre><code>CREATE TABLE users (\n  user_id INT(10)  NOT NULL AUTO_INCREMENT,\n  username VARCHAR(255)  NOT NULL,\n  password VARCHAR(40)  NOT NULL,\n  PRIMARY KEY (user_id),\n  UNIQUE KEY (username)\n)\n</code></pre>\n\n<p>Lets see what happens when we now try and insert the same user twice.</p>\n\n<pre><code>mysql&gt; INSERT INTO users (username, password) VALUES(\'Mez\', \'d3571ce95af4dc281f142add33384abc5e574671\');\nQuery OK, 1 row affected (0.00 sec)\n\nmysql&gt; INSERT INTO users (username, password) VALUES(\'Mez\', \'d3571ce95af4dc281f142add33384abc5e574671\');\nERROR 1062 (23000): Duplicate entry \'Mez\' for key \'username\'\n</code></pre>\n\n<p>Huzzah!! We now get an error when we try and insert the username for the second time.  Using something like the above, we can detect this in PHP.</p>\n\n<p>Now, lets go back to our links table, but with a new definition.</p>\n\n<pre><code>CREATE TABLE links\n(\n    link_id INT(10)  NOT NULL AUTO_INCREMENT,\n    url VARCHAR(255)  NOT NULL,\n    last_visited TIMESTAMP,\n    PRIMARY KEY (link_id),\n    UNIQUE KEY (url)\n)\n</code></pre>\n\n<p>and let\'s insert "http://www.example.com" into the database.</p>\n\n<pre><code>INSERT INTO links (url, last_visited) VALUES (\'http://www.example.com/\', NOW());\n</code></pre>\n\n<p>If we try and insert it again....</p>\n\n<pre><code>ERROR 1062 (23000): Duplicate entry \'http://www.example.com/\' for key \'url\'\n</code></pre>\n\n<p>But what happens if we want to update the time it was last visited?</p>\n\n<p>Well, we could do something complex with PHP, like so:-</p>\n\n<pre><code>$result = mysql_query("SELECT * FROM links WHERE url = \'http://www.example.com/\'", $conn);\n\nif (!$result)\n{\n    die(\'There was a problem executing the query\');\n}\n\n$number_of_rows = mysql_num_rows($result);\n\nif ($number_of_rows &gt; 0)\n{\n    $result = mysql_query("UPDATE links SET last_visited = NOW() WHERE url = \'http://www.example.com/\'", $conn);\n\n    if (!$result)\n    {\n        die(\'There was a problem updating the links table\');\n    }\n}\n</code></pre>\n\n<p>Or, even grab the id of the row in the database and use that to update it.</p>\n\n<p>$result = mysql_query("SELECT * FROM links WHERE url = \'http://www.example.com/\'", $conn);</p>\n\n<pre><code>if (!$result)\n{\n    die(\'There was a problem executing the query\');\n}\n\n$number_of_rows = mysql_num_rows($result);\n\nif ($number_of_rows &gt; 0)\n{\n    $row = mysql_fetch_assoc($result);\n\n    $result = mysql_query(\'UPDATE links SET last_visited = NOW() WHERE link_id = \' . intval($row[\'link_id\'], $conn);\n\n    if (!$result)\n    {\n        die(\'There was a problem updating the links table\');\n    }\n}\n</code></pre>\n\n<p>But, MySQL has a nice built in feature called <code>REPLACE INTO</code></p>\n\n<p>Let\'s see how it works.</p>\n\n<pre><code>mysql&gt; SELECT * FROM links;\n+---------+-------------------------+---------------------+\n| link_id | url                     | last_visited        |\n+---------+-------------------------+---------------------+\n|       1 | http://www.example.com/ | 2011-08-19 23:48:03 |\n+---------+-------------------------+---------------------+\n1 row in set (0.00 sec)\n\nmysql&gt; INSERT INTO links (url, last_visited) VALUES (\'http://www.example.com/\', NOW());\nERROR 1062 (23000): Duplicate entry \'http://www.example.com/\' for key \'url\'\nmysql&gt; REPLACE INTO links (url, last_visited) VALUES (\'http://www.example.com/\', NOW());\nQuery OK, 2 rows affected (0.00 sec)\n\nmysql&gt; SELECT * FROM links;\n+---------+-------------------------+---------------------+\n| link_id | url                     | last_visited        |\n+---------+-------------------------+---------------------+\n|       2 | http://www.example.com/ | 2011-08-19 23:55:55 |\n+---------+-------------------------+---------------------+\n1 row in set (0.00 sec)\n</code></pre>\n\n<p>Notice that when using <code>REPLACE INTO</code>, it\'s updated the last_visited time, and not thrown an error!</p>\n\n<p>This is because MySQL detects that you\'re attempting to replace a row.  It knows the row that you want, as you\'ve set url to be unique.  MySQL figures out the row to replace by using the bit that you passed in that should be unique (in this case, the url) and updating for that row the other values. It\'s also updated the link_id - which is a bit unexpected! (In fact, I didn\'t realise this would happen until I just saw it happen!)</p>\n\n<p>But what if you wanted to add a new URL?  Well, <code>REPLACE INTO</code> will happily insert a new row if it can\'t find a matching unique row!</p>\n\n<pre><code>mysql&gt; REPLACE INTO links (url, last_visited) VALUES (\'http://www.stackoverflow.com/\', NOW());\nQuery OK, 1 row affected (0.00 sec)\n\nmysql&gt; SELECT * FROM links;\n+---------+-------------------------------+---------------------+\n| link_id | url                           | last_visited        |\n+---------+-------------------------------+---------------------+\n|       2 | http://www.example.com/       | 2011-08-20 00:00:07 |\n|       3 | http://www.stackoverflow.com/ | 2011-08-20 00:01:22 |\n+---------+-------------------------------+---------------------+\n2 rows in set (0.00 sec)\n</code></pre>\n\n<p>I hope this answers your question, and gives you a bit more information about how MySQL works!</p>\n'}, {'answer_id': 7128408, 'author': 'Matthew', 'author_id': 369775, 'author_profile': 'https://Stackoverflow.com/users/369775', 'pm_score': 0, 'selected': False, 'text': '<p>You can locate (and remove) using a self-join. Your table has some URL and also some PK (We know that the PK <em>is not</em> the URL because otherwise you would not be allowed to have duplicates)</p>\n\n<pre><code>SELECT\n    *\nFROM\n    yourTable a\nJOIN\n    yourTable b -- Join the same table\n        ON b.[URL] = a.[URL] -- where the URL\'s match\n        AND b.[PK] &lt;&gt; b.[PK] -- but the PK\'s are different\n</code></pre>\n\n<p>This will return all rows which have duplicated URLs.</p>\n\n<p>Say, though, that you wanted to only select the <strong>duplicates</strong> and exclude the original.... Well you would need to decide what constitutes the original. For the purpose of this answer let\'s assume that the lowest PK is the "original"</p>\n\n<p>All you need to do is add the following clause to the above query:</p>\n\n<pre><code>WHERE\n    a.[PK] NOT IN (\n        SELECT \n            TOP 1 c.[PK] -- Only grabbing the original!\n        FROM\n            yourTable c\n        WHERE\n            c.[URL] = a.[URL] -- has the same URL\n        ORDER BY\n            c.[PK] ASC) -- sort it by whatever your criterion is for "original"\n</code></pre>\n\n<p>Now you have a set of all non-original duplicated rows. You could easily execute a <code>DELETE</code> or whatever you like from this result set.</p>\n\n<p>Note that this approach may be inefficient, in part because mySQL doesn\'t always handle <code>IN</code> well but I understand from the OP that this is sort of "clean up" on the table, not always a check.</p>\n\n<p>If you want to check at <code>INSERT</code> time whether or not a value already exists you can run something like this</p>\n\n<pre><code>SELECT \n    1\nWHERE\n    EXISTS (SELECT * FROM yourTable WHERE [URL] = \'testValue\')\n</code></pre>\n\n<p>If you get a result then you can conclude the value already exists in your DB at least once.</p>\n'}, {'answer_id': 7128781, 'author': 'Kibbee', 'author_id': 1862, 'author_profile': 'https://Stackoverflow.com/users/1862', 'pm_score': 1, 'selected': False, 'text': "<p>First things first. If you haven't already created the table, or you created a table but do not have data in in then you need to add a unique constriant, or a unique index.  More information about choosing between index or constraints follows at the end of the post.  But they both accomplish the same thing, enforcing that the column only contains unique values.</p>\n\n<p>To create a table with a unique index on this column, you can use.</p>\n\n<pre><code>CREATE TABLE MyURLTable(\nID INTEGER NOT NULL AUTO_INCREMENT\n,URL VARCHAR(512)\n,PRIMARY KEY(ID)\n,UNIQUE INDEX IDX_URL(URL)\n);\n</code></pre>\n\n<p>If you just want a unique constraint, and no index on that table, you can use</p>\n\n<pre><code>CREATE TABLE MyURLTable(\nID INTEGER NOT NULL AUTO_INCREMENT\n,URL VARCHAR(512)\n,PRIMARY KEY(ID)\n,CONSTRAINT UNIQUE UNIQUE_URL(URL)\n);\n</code></pre>\n\n<p>Now, if you already have a table, and there is no data in it, then you can add the index or constraint to the table with one of the following pieces of code.</p>\n\n<pre><code>ALTER TABLE MyURLTable\nADD UNIQUE INDEX IDX_URL(URL);\n\nALTER TABLE MyURLTable\nADD CONSTRAINT UNIQUE UNIQUE_URL(URL);\n</code></pre>\n\n<p>Now, you may already have a table with some data in it. In that case, you may already have some duplicate data in it. You can try creating the constriant or index shown above, and it will fail if you already have duplicate data.  If you don't have duplicate data, great, if you do, you'll have to remove the duplicates. You can see a lit of urls with duplicates using the following query.</p>\n\n<pre><code>SELECT URL,COUNT(*),MIN(ID) \nFROM MyURLTable\nGROUP BY URL\nHAVING COUNT(*) &gt; 1;\n</code></pre>\n\n<p>To delete rows that are duplicates, and keep one, do the following:</p>\n\n<pre><code>DELETE RemoveRecords\nFROM MyURLTable As RemoveRecords\nLEFT JOIN \n(\nSELECT MIN(ID) AS ID\nFROM MyURLTable\nGROUP BY URL\nHAVING COUNT(*) &gt; 1\nUNION\nSELECT ID\nFROM MyURLTable\nGROUP BY URL\nHAVING COUNT(*) = 1\n) AS KeepRecords\nON RemoveRecords.ID = KeepRecords.ID\nWHERE KeepRecords.ID IS NULL;\n</code></pre>\n\n<p>Now that you have deleted all the records, you can go ahead and create you index or constraint.  Now, if you want to insert a value into your database, you should use something like.</p>\n\n<pre><code>INSERT IGNORE INTO MyURLTable(URL)\nVALUES('http://www.example.com');\n</code></pre>\n\n<p>That will attempt to do the insert, and if it finds a duplicate, nothing will happen. Now, lets say you have other columns, you can do something like this.</p>\n\n<pre><code>INSERT INTO MyURLTable(URL,Visits) \nVALUES('http://www.example.com',1)\nON DUPLICATE KEY UPDATE Visits=Visits+1;\n</code></pre>\n\n<p>That will look try to insert the value, and if it finds the URL, then it will update the record by incrementing the visits counter.  Of course, you can always do a plain old insert, and handle the resulting error in your PHP Code. Now, as for whether or not you should use constraints or indexes, that depends on a lot of factors. Indexes make for faster lookups, so your performance will be better as the table gets bigger, but storing the index will take up extra space.  Indexes also usually make inserts and updates take longer as well, because it has to update the index. However, since the value will have to be looked up either way, to enforce the uniqueness, in this case, It may be quicker to just have the index anyway.  As for anything performance related, the answer is try both options and profile the results to see which works best for your situation. </p>\n"}, {'answer_id': 7138051, 'author': 'Daniel Trebbien', 'author_id': 196844, 'author_profile': 'https://Stackoverflow.com/users/196844', 'pm_score': 2, 'selected': False, 'text': '<p>In considering a solution to this problem, you need to first define what a "duplicate URL" means for your project. This will determine how to <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Canonicalization" rel="nofollow">canonicalize</a> the URLs before adding them to the database.</p>\n\n<p>There are at least two definitions:</p>\n\n<ol>\n<li>Two URLs are considered duplicates if they represent the same resource knowing nothing about the corresponding web service that generates the corresponding content. Some considerations include:\n<ul>\n<li>The scheme and domain name portion of the URLs are case-insensitive, so <a href="http://HTTP://WWW.STACKOVERFLOW.COM/" rel="nofollow">HTTP://WWW.STACKOVERFLOW.COM/</a> is the same as <a href="http://www.stackoverflow.com/">http://www.stackoverflow.com/</a>.</li>\n<li>If one URL specifies a port, but it is the conventional port for the scheme and they are otherwise equivalent, then they are the same ( <a href="http://www.stackoverflow.com/">http://www.stackoverflow.com/</a> and <a href="http://www.stackoverflow.com:80/">http://www.stackoverflow.com:80/</a>).</li>\n<li>If the parameters in the query string are simple rearrangements and the parameter names are all different, then they are the same; e.g. <a href="http://authority/?a=test&amp;b=test" rel="nofollow">http://authority/?a=test&amp;b=test</a> and <a href="http://authority/?b=test&amp;a=test" rel="nofollow">http://authority/?b=test&amp;a=test</a>. Note that <a href="http://authority/?a%5B%5D=test1&amp;a%5B%5D=test2" rel="nofollow">http://authority/?a%5B%5D=test1&amp;a%5B%5D=test2</a> is not the same, by this first definition of sameness, as <a href="http://authority/?a%5B%5D=test2&amp;a%5B%5D=test1" rel="nofollow">http://authority/?a%5B%5D=test2&amp;a%5B%5D=test1</a>.</li>\n<li>If the scheme is HTTP or HTTPS, then the hash portions of the URLs can be removed, as this portion of the URL is not sent to the web server.</li>\n<li>A shortened IPv6 address can be expanded.</li>\n<li>Append a trailing forward slash <em>to the authority only</em> if it is missing.</li>\n<li>Unicode canonicalization changes the referenced resource; e.g. you can\'t conclude that <a href="http://google.com/?q=%C3%84" rel="nofollow">http://google.com/?q=%C3%84</a> (<code>%C3%84</code> represents \'Ä\' in UTF-8) is the same as <a href="http://google.com/?q=A%CC%88" rel="nofollow">http://google.com/?q=A%CC%88</a> (<code>%CC%88</code> represents U+0308, COMBINING DIAERESIS).</li>\n<li>If the scheme is HTTP or HTTPS, \'<code>www.</code>\' in one URL\'s authority can not simply be removed if the two URLs are otherwise equivalent, as the text of the domain name is sent as the value of the <code>Host</code> HTTP header, and some web servers use virtual hosts to send back different content based on this header. More generally, even if the domain names resolve to the same IP address, you can not conclude that the referenced resources are the same.</li>\n</ul></li>\n<li>Apply basic URL canonicalization (e.g. lower case the scheme and domain name, supply the default port, stable sort query parameters by parameter name, remove the hash portion in the case of HTTP and HTTPS, ...), <em>and</em> take into account knowledge of the web service. Maybe you will assume that all web services are smart enough to canonicalize Unicode input (Wikipedia is, for example), so you can apply <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Unicode_equivalence#Normal_forms" rel="nofollow">Unicode Normalization Form</a> Canonical Composition (NFC). You would strip \'<code>www.</code>\' from all Stack Overflow URLs. You could use PostRank\'s <a href="https://github.com/postrank-labs/postrank-uri" rel="nofollow">postrank-uri</a> code, ported to PHP, to remove all sorts of pieces of the URLs that are unnecessary (e.g. <code>&amp;utm_source=...</code>).</li>\n</ol>\n\n<p>Definition 1 leads to a stable solution (i.e. there is no further canonicalization that can be performed and the canonicalization of a URL will not change). Definition 2, which I think is what a human considers the definition of URL canonicalization, leads to a canonicalization routine that can yield different results at different moments in time.</p>\n\n<p>Whichever definition you choose, I suggest that you use separate columns for the scheme, login, host, port, and path portions. This will allow you to use indexes intelligently. The columns for scheme and host can use a character collation (all character collations are case-insensitive in MySQL), but the columns for the login and path need to use a binary, case-insensitive collation. Also, if you use Definition 2, you need to preserve the original scheme, authority, and path portions, as certain canonicalization rules might be added or removed from time to time.</p>\n\n<p><strong>EDIT:</strong> Here are example table definitions:</p>\n\n<pre><code>CREATE TABLE `urls1` (\n    `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,\n    `scheme` VARCHAR(20) NOT NULL,\n    `canonical_login` VARCHAR(100) DEFAULT NULL COLLATE \'utf8mb4_bin\',\n    `canonical_host` VARCHAR(100) NOT NULL COLLATE \'utf8mb4_unicode_ci\', /* the "ci" stands for case-insensitive. Also, we want \'utf8mb4_unicode_ci\'\nrather than \'utf8mb4_general_ci\' because \'utf8mb4_general_ci\' treats accented characters as equivalent. */\n    `port` INT UNSIGNED,\n    `canonical_path` VARCHAR(4096) NOT NULL COLLATE \'utf8mb4_bin\',\n\n    PRIMARY KEY (`id`),\n    INDEX (`canonical_host`(10), `scheme`)\n) ENGINE = \'InnoDB\';\n\n\nCREATE TABLE `urls2` (\n    `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,\n    `canonical_scheme` VARCHAR(20) NOT NULL,\n    `canonical_login` VARCHAR(100) DEFAULT NULL COLLATE \'utf8mb4_bin\',\n    `canonical_host` VARCHAR(100) NOT NULL COLLATE \'utf8mb4_unicode_ci\',\n    `port` INT UNSIGNED,\n    `canonical_path` VARCHAR(4096) NOT NULL COLLATE \'utf8mb4_bin\',\n\n    `orig_scheme` VARCHAR(20) NOT NULL, \n    `orig_login` VARCHAR(100) DEFAULT NULL COLLATE \'utf8mb4_bin\',\n    `orig_host` VARCHAR(100) NOT NULL COLLATE \'utf8mb4_unicode_ci\',\n    `orig_path` VARCHAR(4096) NOT NULL COLLATE \'utf8mb4_bin\',\n\n    PRIMARY KEY (`id`),\n    INDEX (`canonical_host`(10), `canonical_scheme`),\n    INDEX (`orig_host`(10), `orig_scheme`)\n) ENGINE = \'InnoDB\';\n</code></pre>\n\n<p>Table `urls1` is for storing canonical URLs according to definition 1. Table `urls2` is for storing canonical URLs according to definition 2.</p>\n\n<p>Unfortunately you will not be able to specify a <code>UNIQUE</code> constraint on the tuple (`scheme`/`canonical_scheme`, `canonical_login`, `canonical_host`, `port`, `canonical_path`) as MySQL limits the length of InnoDB keys to 767 bytes.</p>\n'}]
['https://Stackoverflow.com/questions/61033', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/6305/']