Unique index not based on words order? - mysql

Is it possibile to have a mysql UNIQUE index on a varchar field not based on the words order?
I mean if there is a row with key1 key2, and I try to insert key2 key1 mysql should throw an error.

You could achieve this effect by adding before insert and before update triggers to the table; they could check whether a row with the fields reversed exists or not before inserting/updating if it doesn't, or forcing an error if it does.
See here for more information.

If you set the indexed to UNIQUE, you will not be able to enter the identical data twice - numbers, words or otherwise.
If you wish to achieve something otherwise, you'll have to set a new non-unique field in your database then you'll be able achieve it using external code and queries (PHP, C#).

Create a trigger to do this.
i.e.
CREATE TRIGGER trigger_name BEFORE INSERT ON table_name
FOR EACH ROW
BEGIN
IF ((SELECT COUNT(*) FROM table WHERE col2=NEW.col1 OR col1=NEW.col2) <> 0)
THEN
CALL this_procedure_does_not_exist();
END IF
END

Related

mysql add unique multi column with no check actual data

For an mysql v8.0.18 project with mariaDb 10.4.10
I would like add to my existing table an unique constraint for multi columns
ALTER TABLE 'new_purchasseorder' ADD UNIQUE ('created', 'fk_customer_id', 'fk_removal_id', 'fk_recipient_id')
but would like no check for old datas
something like that:
where id > 3869
i also tried the SET FOREIGN_KEY_CHECKS=0; but nor working in this case.
is it possible ?
My table looks like:
You can't do this with a unique constraint as far as I know, because, as you have already discovered, such a constraint will be applied to the entire table, regardless of id value. One workaround might be to use a before insert trigger, which does the assertion:
DELIMITER //
CREATE TRIGGER contacts_before_insert
BEFORE INSERT ON new_purchasseorder FOR EACH ROW
BEGIN
IF EXISTS (SELECT 1 FROM new_purchasseorder
WHERE created = NEW.created AND
fk_customer_id = NEW.fk_customer_id AND
fk_removal_id = NEW.fk_removal_id AND
fk_recipient_id = NEW.fk_recipient_id)
THEN
signal sqlstate '45000';
END IF;
END; //
DELIMITER ;
This insert trigger would cause any insert incoming with what your unique index defines as duplicate data to fail with an error, effectively blocking that insert.
A better long term (and easier) strategy might be to just fix your old data so that it can pass the requirements of the unique constraint.
Starting version 8.0.13, MySQL supports functional key parts - basically indexes on expression. Assuming that all 4 columns are non-nullable, you can do:
create unique index idx_new_purchaseorder on new_purchaseorder (
(
case when id > 3869
then concat_ws('$$', created, fk_customer_id, fk_removal_id, fk_recipient_id)
end
)
)
The case expression filters on id values, and generates a concatenated string that should be unique for rows that comply to the filter. I used some fancy characters to avoid "fake" duplicates.
Demo on DB Fiddle

MySQL auto increment on insert and update

I have a MySQL database and I need to auto increment a column by 1 every time I do an insert or update. If I had to increment the column only during insert I could have used the built-in autoincrement option (usually used for primary keys). How can I do it for insert and updates?
EDIT
Sorry, I posted the wrong question, what I actually need is to increase a counter by 1 every time I do an insert or update, the current value of the counter has to be stored in the row being created or updated. The counter starts from 1 and never comes back, it just keep increasing "forever" (BIGINT). Think of this counter as a lastupdate timestamp but instead of using real unix timestamps I use an ever increasing integer (monotonic increasing value).
P.S. I'm implementing a syncronization mechanism between many local SQLite databases and one master MySQL database so the behavior has to be implemented on both dbms.
The current state of the counter can be stored on a separate table of course
Simply use triggers.
Something like this:
CREATE TRIGGER trgIU_triggertestTable_UpdateColumnCountWhenColumnB
ON dbo.triggertestTable
AFTER INSERT,UPDATE
AS
BEGIN ...
OR you can do something like this:
INSERT INTO TableA (firstName, lastName, logins) VALUES ('SomeName', 'SomeLastName', 1)
ON DUPLICATE KEY UPDATE count = count + 1;
I see two ways to do what you want.
The first is for inserts, when you should use the autoincrement key. But, when we talk about autoincrement updates, it's a little bit more complicated. For me, the best solution is to do a trigger.
You could use a trigger like this:
CREATE TRIGGER update_trigger
AFTER UPDATE
ON `your_table`
FOR EACH ROW
BEGIN
UPDATE `your_table`
SET `the_field_you_want_autoincrement` = `the_field_you_want_autoincrement` + 1
WHERE `pk` = NEW.pk
END
There's no declarative auto-increment-on-update feature. And the auto-increment must be part of your primary key, so this is probably not your counter.
You can do this with triggers.
CREATE TRIGGER MyTrigger BEFORE INSERT ON MyTable
FOR EACH ROW
SET NEW.counter = 1;
CREATE TRIGGER MyTrigger BEFORE UPDATE ON MyTable
FOR EACH ROW
SET NEW.counter = OLD.counter+1;
These must be BEFORE triggers, because you can't set column values in an AFTER trigger.
Re your comments:
I don't get the "for each row on the second statement"
This is a required clause for all MySQL triggers, because the trigger runs for each row inserted. You can insert multiple rows in a single INSERT statement:
INSERT INTO MyTable VALUES (...), (...), (...), ...
INSERT INTO MyTable SELECT ... FROM ...
The insert trigger will initialize each row inserted.
Re your updated question:
The solution with triggers I show above will actually work for the scenario you describe, where you want a counter column to start at 1 at INSERT time, and increase by 1 every time you update.
The solution with INSERT...ON DUPLICATE KEY UPDATE does not work, because it won't increment the counter if a user simply does an UPDATE statement. Also the user is required to include the initial counter value 1 in their INSERT statement.
The insert trigger sets the initial value to 1 even if a user tries to give a different value in their INSERT statement. And the update trigger will increment the counter even if the user uses INSERT...ON DUPLICATE KEY UPDATE or UPDATE.
But don't use a REPLACE statement, because this would do a DELETE followed by a new INSERT, and thus it would run the insert trigger, and reset the counter to 1.

Insert unqiue record into one table and the results into a second table

Using MySQL and PHP, I have two tables ResponseTable and EntryTable
Response table has
RespID(pk,uq,ai, int), Response(string)
Entry table has
EntryID(pk,uq,ai,int), RespID(int), UserID(int)
I would like to insert a response into the ResponseTable where the Response doesn't exist, and then insert an entry into the EntryTable based on the RespID from the ResponseTable corresponding to the response.
How can this be done with the fewest statements?
EDIT:
Response is unique
The fewest statements from the front-end would be to use a stored procedure. Any way you do it, though, you will still need to have two INSERT statements. You just can't insert two different things with one query.
mysql supports TRIGGERS . you can add one to your ResponseTable that will activate "AFTER" "INSERT" and you can get it to use the values that are being inserted using the NEW key word.
the body of the trigger can be an insert into entry
something like:
CREATE TRIGGER `response_after_insert` AFTER INSERT ON `response`
FOR EACH ROW
BEGIN INSERT INTO entry SET RespID=NEW.RespID ON DUPLICATE KEY UPDATE operation=1;END;
So once you have your trigger set up, any time you do an insert on respnse, the trigger will get activated

MySQL Trigger doesnt execute if a column's default NULL value changes to some value

Here is the MYSQL Trigger that I have written:
DELIMITER //
CREATE TRIGGER updtrigger BEFORE UPDATE ON myTable
FOR EACH ROW
BEGIN
IF (OLD.column IS NOT NULL AND NEW.column IS NOT NULL AND NEW.column != OLD.column) THEN
SET NEW.col_updated_on = NOW();
END IF;
END //
DELIMITER ;
If column has a value (say, "movie"), and I update it to get new value, "movie,music", the trigger gets executed and col_updated_on should have the current timestamp.
However, when column is NULL and I update it to get the new value, "movie", the col_updated_on column will still show me the old timestamp.
Please let me know what change I must do to check for this condition as well.
Thanks in advance for your replies.
I have another question. Is the below pseudo possible??
Here are my two tables -
myTable(id, someId, col_updated_on);
myOtherTable(id, col1, col2);
myTable.someId has a 1-on-1 relation with myOtherTable.id
I want to update myTable.col_updated_on whenever myOtherTable.col1 and myOtherTable.col2 are updated. How do i do this? and should i use "BEFORE UPDATE" or "AFTER UPDATE"?
Use the NULL-safe equality operator:
IF NOT OLD.column <=> NEW.column THEN
However, note that MySQL's TIMESTAMP data type can (and, by default, will) automatically update whenever a record is updated, making such a trigger unnecessary.
But also, you REALLY shouldn't store multiple items in a delimited string in a relational database like MySQL. Read up on database design, especially one-to-many relationships: one would fare much better by having a separate table, in which each record links an identifier (key) from your existing (foreign) table to a single item; one would then join the tables together as required in your queries.

Multiple MySQL queries (no PHP)

I am wondering if it is possible to perform a SQL query then update another table with the generated ID and continue through all of the rows?
I have this SQL query that works but what I need to do is after each row is added to cards to then update merged.cars_id with the last generated ID so they are linked. normally I would do this with PHP but ideally I would like to just do it with MySQL if possible.
MAIN QUERY
INSERT INTO cards (first_contact_date, card_type, property_id, user_id)
SELECT first_contact_date, 'P', property_id, user_id FROM merged
THEN I NEED WITH MATCHING ROWS (Roughly)
UPDATE merged SET merged.card_id = LAST_INSERT_ID (FROM ABOVE) into the matching record..
Is something like this possible and how do I do it?
I would recommend using MySQL triggers to do this
http://dev.mysql.com/doc/refman/5.0/en/create-trigger.html
A trigger is a function that will be executed AFTER or BEFORE the INSERT or DELETE or UPDATE is done over any record of your table.
In your case you need to do a AFTER INSERT on cards that just updates the merged table. Make sure its AFTER insert as you wont be able to access the new row's ID otherwise.
The code would look something like this, assuming the id field from the cards table its named "id"
delimiter |
CREATE TRIGGER updating_merged AFTER INSERT ON cards
FOR EACH ROW BEGIN
UPDATE merged SET card_id = NEW.id;
END;
|
delimiter ;
May I suggest Stored Procedures?
http://dev.mysql.com/doc/refman/5.0/en/create-procedure.html
--EDIT--
Ah yes, triggers. For this particular situation, Jimmy has the answer. I will leave this post for the sake of the link.
I would set up a trigger to do this. For mysql, read http://dev.mysql.com/doc/refman/5.0/en/triggers.html. This is what triggers are designed to handle.