Unique constrain with condition MYSQL - mysql

I have User table in my DB.
A user has the fields name, company_id and status: boolean, 1- live, 0- deleted.
When a user is deleted, his status is set to 0.
The combination of a live user name in a company should be unique. After a user is deleted, I don't mind that a user should be created with the same name for the company.
My question is how do I define a uniuqe constrain for the fields name, company_id and status=1 (It's not a uniuqe constrain on those three field becuase I don't mind that the combination of name-company_id-0 will appear a few times in the table).
Thanks,
Dvora

Use NULL value for deleted users.
Unique key allows unlimited number of NULL values.
Update: Don't touch user name, NULL in status field is enough.

Which programming language you are using?
your logic shoule be as follows
select * from Table_name where name='' AND company_id = '' AND status = 1
if this return any rows give uniqueness error to the user else create it.

I would create another column to store a deleted user's previous name and set their real name to NULL when they're deleted (as well as setting the status to 0).
Then have a unique constraint on name and company. NULLs will not affect the uniqueness (since NULL != NULL) and you can still recover the user's original name if desired.
So the delete operation is something like:
update users
set prev_name = name,
name = null,
status = 0
where name = 'paxdiablo' and company = 'SmallGreen';

Would it be easier if you split "live" and "deleted" so they have their own tinyint/boolean columns ?

I would replace status field with deleted_at (datetime). When the user is active its value would be NULL, but when deleted, it would be set to current datetime.
Next, i would add unique index on username & deleted_at fields, which will allow me to delete more than one user with the same username (in at least 1 second interval).

Related

How to reset fields to DEFAULT value in a MYSQL table when update a record with some value?

i've a MYSQL table called "customers" with the following fields:
id (primary key, int, autoincrement)
name (varchar(20), default value='')
surname (varchar(20), default value='')
age (int, default value=NULL)
city (varchar(20), default value='')
The question is simple:
If i had to update "name" and "surname" i would like all the other fields of that record to be returned to their starting values without expressly declaring them.
Is there any instruction that automatically does this?
Thank you all! :)
Is there any instruction that automatically does this?
No. MySQL supports ON UPDATE clauses in columns definitions for datetime and timestamp only (and only to set the column to the current date). Other databases do not even support ON UPDATE
So you will need to explicitly assign the columns you want to reset in your update statement. You can simplify the logic a little by using DEFAULT as a target value, so you don’t need to remember each and every default value.
update customers
set
name = 'foo',
surname = 'bar',
age = default,
city = default
where id = 1
Unrelated note: it is not a good practice to store an age, since this information changes over time. Instead you can store the date of birth, and compute the age on the fly when needed (or use a view).

mySql – How to search for available IDs

I have a mySQL table, which stores data of a user list and has an ID in unsigned tiny-int format (0 to 255 entries possible) as primary key. I enabled auto-increment in order to automatically set the key, which works fine so far.
When users log off I call ALTER TABLE sj_userlist AUTO_INCREMENT=1 which executes without errors.
However, the next logged in user still receives the ID+1 of the currently highest ID.
Example:
10 Users are online with the IDs 1,2,3,4,5,6,7,8,9,10
Users 1 and 2 to 9 log out - only ID 1 and 10 are still logged on
a new user logs in and receives ID 11 but I want him/her to get the 2
In case this behavior is correct, how can I achieve an alternative solution, which always starts at 1 incrementing untl the next available ID is found ?
Thanks in advance,
best
Alex
Possible solution.
Create a table for users registration. Define username column as unique.
Insert max amount of records into it according the datatype (0-255, for example).
When the user logs in and needs to register you use
UPDATE table
SET name = 'Current user name'
WHERE name IS NULL
ORDER BY id
LIMIT 1;
SELECT id
FROM table
WHERE name = 'Current user name';
This guarantee that user will get the least available number, and no interference/concurrence problems will occur.
When user log out then you use
UPDATE table
SET name = NULL
WHERE name = 'Current user name'
id is now free for re-use by the next user logged in.
If some problems occures on the client side, and user have not logged out correctly then his name will stay registered. But when he logged in the next time he will receive the same id - UPDATE will fail but SELECT will return non-freed id.
Nevertheless you must clear such old records periodically.

Mysql UNIQUE Constraint that only applies if one field has specfic values

I hope this works somehow:
I have a Mysql Table with 3 column:
id = int
state = enum('visible', 'moderated', 'deleted')
user_id = int
No user should have more than 1 entry, that is 'visible' or 'moderated', but he can have endless 'deleted' entries.
So, I need a UNIQUE Key on user_id, that only applies if the state is 'visible' or 'moderated'
There are basically two options, first one is fairly simple but would require change in your table structure and application logic.
If you use NULL value instead of 'deleted', you can have as many "deleted" rows as you want for given user_id (having unique constaint on (user_id, state)). Your table structure would be something like this:
id = int
state = enum('visible', 'moderated') NULL
user_id = int
The other option would involve checking in post update/post insert triggers whether you are not breaking your contraint and throwing "sql exception" vide https://dev.mysql.com/doc/refman/5.5/en/signal.html

MySQL: adding a string with a comma to an existing field

In my Users table I have a row called "products_in_sale" that contains the product ids of the products the user sells.
For one user it looks like this. "33" <-- therefore this very user sells only one product with the id 33.
Now, since users can sell more than one product, I have to join another number (preferably with a "," in between) to this field when a user creates a new product.
How does the necessary UPDATE statement have to look?
maybe 'UPDATE Users SET products_in_sale = products_in_sale + ", '.$id.'"'; ?
thanks for your help!
Use the CONCAT_WS function:
UPDATE Users SET products_in_sale = CONCAT_WS(',', products_in_sale, newProductId) where userId = ?
This query should work also for the first insert, if your products_in_sale column defaults to NULL.
Anyway, as suggested by juergen, using another table would be a better option.
Never never store multiple values in a single cell, this is a violation of first normal form (1NF). Read more: http://www.ntu.edu.sg/home/ehchua/programming/sql/Relational_Database_Design.html
Every value (e.g. product_id) should have its cell in a relational database table. In your case, there should be a table say "user_product" with at least 2 fields - "user_id" and "product_id", and both are composite primary key.
Of course, there should be a "user" table storing user details which would have a "user_id" field linking to the "user_id" field of the "user_product" table. Likewise, there should be a "product" table storing product details which would have a "product_id" field linking to the "product_id" field of the "user_product" table.

Can I conditionally enforce a uniqueness constraint?

My database contains a table of users. Every active user has a unique username. I'd like to be able to deactivate a user and free up the username they're using, but keep them in the same table.
Is there a way to only conditionally enforce the uniqueness constraint?
Add another column called something like isactive. The create a unique constraint on (username, isactive).
Then you can have both an active and inactive user name at the same time. You will not be able to have two active user names.
If you want multiple inactive names, use NULL for the value of isactive. NULL values can be repeated in a unique index.
No, a UNIQUE constraint can't be "conditional".
One option is to set the username column to NULL. The UNIQUE constraint will allow multiple rows with NULL value.
You could translate that to any string you wanted for display. either in the application, or in the SQL
SELECT IFNULL(t.username,'USER DELETED') AS username
FROM mytable t
If you are retaining these rows for historical/archive purposes, you probably do NOT want to update the username column. (If you change the value of the username column, then a subsequent statement will be allowed to insert a row with the same value as the previous username.)
You could instead add an additional column to your table, to represent the "user deleted" condition. For example:
user_deleted TINYINT(1) UNSIGNED DEFAULT 0 COMMENT 'boolean'
You could check this column and return the 'USER DELETED' constant in place of the username column whenever the user_deleted boolean is set:
SELECT IF(u.user_deleted,'USER DELETED',u.username) AS username
(Use a value of 1 to indicated a logical "user deleted" condition.)
The big advantage to this approach is that the username column does NOT have to be modified, the username value, and the UNIQUE constraint will prevent a new row with a duplicate username from being inserted.
Different way to achieve the same result. May not be really required for the question asked. But just for information.
Create a trigger on insert / update
Check if there is duplicate records found with current (NEW) records values.
a. This can be checked by counting dupicates or checking of OTHER records exists with the same values, but different primary key
If found raise a Signal to throw an error
This is best suited if your condition is complex to decide uniqueness. Also consider the performance cost.
Sample
DELIMITER $$
CREATE TRIGGER `my_trigger` BEFORE INSERT/UPDATE
ON `usertable`
FOR EACH ROW BEGIN
IF EXISTS (SELECT 1 FROM usertable WHERE userid <> NEW.userid AND username = NEW.username AND isactive = 1) THEN
SELECT CONCAT(NEW.username, ' exists !') INTO #error_text;
SIGNAL SQLSTATE '45000' SET message_text = #error_text;
END IF;
END$$
DELIMITER ;
I would just create another (non-unique) field called FORMER_NAME and move the original name to that field when a user goes inactive. No need for a special uniqueness constraint that's not possible.
Nope, if there is a unique index (hence the name) you can not have duplicates. Either add a extra column to make each record unique. Or change the value so its unique.
Not recommended but for example you could add a timestamp "USER DELETED 2013/08/17:233805"
This is my solution when I met a similar problem:
add a column inactive, so the unique key as: (username,inactive)
for inactive, inactive = 0 means a user is active, inactive > 0 means a user is active
when deactivate a user, just set inactive = user_id, not 1 as we usually did!
now it allows duplicated usernames for inactive users, but only unique usernames for active users.
I expanded on #gordon-linoff answer by adding a generated column which provides the nullable functionality. I would rather have a true not null active column that has a definitive true and false value I can use to read and write that is not confusing and won't get messed up by accidentally forgetting about this null behavior later on when writing code. So I compute a column with a specialized name and then use that value in the constraint, so I get the nullable unique active behavior but can use the active column as I wish.
isactive BOOL NOT NULL,
_isactive_constraint_key_ BOOL AS (CASE WHEN isactive IS true THEN true END),
CONSTRAINT active_user UNIQUE(username, _isactive_constraint_key)