MySQL unique index doesnt apply when values are NULL - mysql

I have a table for detecting views of articles - it should have one unique row for each article_id&&NULL&&IP when noone is logged in and unique row for each article_id&&loggedInUser&&IP. So I thought that when noone is logged in I will just add a NULL instead of user_id. But MySQL suprised me - when I've added UNIQUE KEY like article_id&&user_id&&IP it worked fine for logged in users, but if no user logged in it started to add rows like (e.g.):
article_id | user_id | IP
5 NULL 192.168.3.50
5 NULL 192.168.3.50
5 NULL 192.168.3.50
5 NULL 192.168.3.50
This doesnt seem much unique - I know it is caused by NULL but how to solve this? Should I just rely on the fact that no user will have user_id "0"?
Thanks.

This is intentional and is documented:-
http://dev.mysql.com/doc/refman/5.0/en/create-index.html
A UNIQUE index creates a constraint such that all values in the index must be distinct. An error occurs if you try to add a new row with a key value that matches an existing row. This constraint does not apply to NULL values except for the BDB storage engine. For other engines, a UNIQUE index permits multiple NULL values for columns that can contain NULL. If you specify a prefix value for a column in a UNIQUE index, the column values must be unique within the prefix.
While you could use a user id of 0 I would be concerned that you might have 0 used elsewhere when you do not want a record found. For example I often just convert any input id field to an integer and if someone has tried to hack around and enter a string this might well be converted to 0. In such a case I wouldn't really want the zero to be meaningful.
I would possibly be tempted to set up a 'none' userid to use in this situation.

Your current solution is going to grow huge very quickly and provide very little benefit. If it were me I would just rely on analytics to handle this sort of data. If you really want this it can be done very easily by adding one more field to your table for a count. When you are about to add a row to this look for one which already exists. If one does then instead of adding a new record just update the current record and increment the count instead. This will provide the exact same information in much less space.

Related

MySQL: Why would an `ID` column be defined with `NOT NULL` if using `AUTO_INCREMENT`?

I was reading somewhere that adding the AUTO_INCREMENT will allow the id column to automatically generate sequential numbers starting from 1. However, in that case, it seems that there is no need to define NOT NULL and UNIQUE. Why is it that I still see many examples online using NOT NULL with AUTO_INREMENT, when creating a table?
Per the documentation, if you assign null values to an auto_id which is defined as not-null, it will replace null with the next sequential value. As ID is likely your primary key (or part of your key), it shouldn't ever take null values.
Also, per this example, if you don't specify that it should be non-null, MySQL will supply this for you.
Okay, after re-reading documentation it seems that if NOT NULL is defined... When inserting values, one may use the value NULL to automatically assign the next sequential number. If NOT NULL is not defined, then one may only use the value 0 to assign the next sequential number. Also, it should be noted that UNIQUE doesn't need to be defined because ID's are usually used in conjunction with PRIMARY KEY which achieves the same.

How to select the max primary key auto Increment in Mysql?

If I delete the max IDs (for example 9, 10,11) from the table, the sql will not begin from the max existing ID ! it begins with 12!
is it possible to let the sql add new id according to existing max id?
how to get the max primary key autoincrement in the table (the deleted one too)?
This code finds the existing only and not the deleted ids:
select max(id) from table1
If I delete the max IDs (for example 9, 10,11) from the table, the sql will not begin from the max existing ID ! it begins with 12 !
That is correct. There is nothing surprising or exclamation-worthy about this. That's how RDBMSes work.
1- is it possible to let the sql add new id according to existing max id ?
Not easily at all. As a matter of fact, it is so difficult, and any attempt to do it would be so tied to the particular RDBMS that you are using, (that is, so not portable,) that you are advised to not even try. That's because RDBMSes are built for highly concurrent use, so they have to be able to prevent the possibility of different clients inserting inconsistent primary key values.
2- how to get the max primary key autoincrement in the table
There really is no such thing as a "max primary key autoincrement in the table", because it may be changing at a very fast rate as someone is inserting rows into the table, so by the time your select max(id) from table1 would return a value to you, the actual max value in the database may already be different.

Why mysql autoincrement increments the last used id rather then the last existing id

I am using mysql, and am looking at a strange behavior.
Scenario :
I have a table having table_id as primary key, which is set to auto-increment.
table_id more_columns
1 some value
2 others
Now if i delete row 2, and insert one more row, the table_id becomes 3 (Expected is 2)
table_id more_columns
1 some value
3 recent
Why is it so? Here I am loosing some ids (I know they are not important). Please put some lights on this behavior
In auto-increment field If a row is deleted, the auto_increment column of that row will not be re-assigned.
Please see here for more information.
For reasons why auto-increment doesn't use deleted values you can refer here(mentioned in comments by #AaronBlenkush).
The auto_increment value is a counter stored internally for each table. The counter is only increased, never decreased.
Every syntactically correct INSERT statement fired against the table increments this counter, even when it is rolled back and also when you define an insert value for the primary key.
A MySQL auto_increment column maintains a number internally, and will always increment it, even after deletions. If you need to fill in an empty space, you have to handle it yourself in PHP, rather than use the auto_increment keyword in the table definition.
Rolling back to fill in empty row ids can cause all sorts of difficulty if you have foreign key relationships to maintain, and it really isn't advised.
The auto_increment can be reset using a SQL statement, but this is not advised because it will cause duplicate key errors.
-- Doing this will cause problems!
ALTER table AUTO_INCREMENT=12345;
EDIT
To enforce your foreign key relationships as described in the comments, you should add to your table definition:
FOREIGN KEY (friendid) REFERENCES registration_table (id) ON DELETE SET NULL;
Fill in the correct table and column names. Now, when a user is deleted from the registration, their friend association is nulled. If you need to reassociate with a different user, that has to be handled with PHP. mysql_insert_id() is no longer helpful.
If you need to find the highest numbered id still in the database after deletion to associate with friends, use the following.
SELECT MAX(id) FROM registration_table;
After delete write this query
ALTER TABLE tablename AUTO_INCREMENT = 1

How to let data "disappear" from database? MySQL

I've got a bit of a stupid question. The thing is my program has to have the function to delete data from my database. Yay, not really the problem. But how can I delete data without the danger that others can see, that there has been something deleted.
User Table:
U_ID U_NAME
1 Chris
2 Peter
OTHER TABLE
ID TIMESTAMP FK_U_D
1 2012-12-01 1
2 2012-12-02 1
Sooooo the ID's are AUTO_INCREMENT, so if I delete one of them there's a gap. Furthermore, the timestamp is also bigger than the row before, so ascending.
I want to let the data with ID 1 disappear from the user's profile (U_ID 1).
If I delete it, there is a gap. If I just change the FK_U_ID to 2 (Peter) it's obvious, because when I insert data, there are 20 or 30 data rows with the same U_ID...so it's obvious that there has been a modification.
If I set the FK_U_ID NULL --> same sh** like when I change it to another U_ID.
Is there any solution to get this work? I know that if nobody but me has access to the database, it's just no problem. But just in case, if somebody controls my program it should not be obvious that there has been modifications.
So here we go.
For the ID gaps issue you can use GUIDs as #SLaks suggests, but then you can't use the native RDBMS auto_increment which means you have to create the GUID and insert it along with the rest of the record data upon creation. Of course, you don't really need the ID to be globally unique, you could just store a random string of 20 characters or something, but then you have to do a DB read to see if that ID is taken and repeat (recursively) that process until you find an unused ID... could be quite taxing.
It's not at all clear why you would want to "hide" evidence that a delete was performed. That sounds like a really bad idea. I'm not a fan of promulgating misinformation.
Two of the characteristics of an ideal primary key are:
- anonymous (be void of any useful information, doesn't matter what it's set to)
- immutable (once assigned, it will never be changed.)
But, if we set that whole discussion aside...
I can answer a slightly different question (an answer you might find helpful to your particular situation)
The only way to eliminate a "gap" in the values in a column with an AUTO_INCREMENT would be to change the column values from their current values to a contiguous sequence of new values. If there are any foreign keys that reference that column, the values in those columns would need to be updated as well, to preserve the relationship. That will likely leave the current auto_increment value of the table higher than the largest value of the id column, so I'd want to reset that as well, to avoid a "gap" on the next insert.
(I have done re-sequencing of auto_increment values in development and test environments, to "cleanup" lookup tables, and to move the id values of some tables to ranges that are distinct from ranges in other tables... that let's me test SQL to make sure the SQL join predicates aren't inadvertently referencing the wrong table, and returning rows that look correct by accident... those are some reasons I've done reassignment if auto_increment values)
Note that the database can "automagically" update foreign key values (for InnnoDB tables) when you change the primary key value, as long as the foreign key constraint is defined with ON UPDATE CASCADE, and FOREIGN_KEY_CHECKS is not disabled.
If there are no foreign keys to deal with, and assuming that all of the current values of id are positive integers, then I've been able to do something like this: (with appropriate backups in place, so I can recover if things don't work right)
UPDATE mytable t
JOIN (
SELECT s.id AS old_id
, #i := #i + 1 AS new_id
FROM mytable s
CROSS
JOIN (SELECT #i := 0) i
ORDER BY s.id
) c
ON t.id = c.old_id
SET t.id = c.new_id
WHERE t.id <> c.new_id
To reset the table AUTO_INCREMENT back down to the largest id value in the table:
ALTER TABLE mytable AUTO_INCREMENT = 1;
Typically, I will create a table and populate it from that query in the inline view (aliased as c) above. I can then use that table to update both foreign key columns and the primary key column, first disabling the FOREIGN_KEY_CHECKS and then re-enabling it. (In a concurrent environment, where other processes might be inserting/updating/deleting rows from one of the tables, I would of course first obtain an exclusive lock on all of the tables to be updated.)
Taking up again, the discussion I set aside earlier... this type of "administrative" function can be useful in a test environment, when setting up test cases. But it is NOT a function that is ever performed in a production environment, with live data.

How To Correct A Unique Key To Not Allow Duplicate Entries

Here is my problem:
The Key "idx_SR_u_Identity_FingerPrintProfile" is meant to constrain the fields "c_r_Fingerprint" and "c_r_Profile" to be unique.
It seems that I have done something wrong because all 4 entries in the table have identical values for those two fields. It is okay if two records have the same Fingerprint OR the same Profile, but not BOTH.
How can I correctly specify this unique key, so that such duplicates are not allowed?
(source: Rigel222.Com)
I think your key is correct, but MySQL does not apply it to NULL values. The MySQL Docs for CREATE TABLE state:
a UNIQUE index allows multiple NULL values for columns that can contain NULL.
While entries like (1,2) can occur only once, entries like (1,NULL) can occur several times, they are not considered to be duplicates because of the NULL.
Dependent on your use case, you may forbid NULL for the two columns to circumvent the problem.