MySQL - Insert UUID as BIGINT yet check if unique - mysql

I am trying to create a unique identifier that can be stored as a BIGINT within a table.
I am working with the pre-existing table that uses a BIGINT for a unique identifier. The database itself does not generate the identifier rather an external program that inserts the values does this. I need to insert my own values into this table and in doing so need to generate a unique identifier that is BIGINT(24).
I was thinking of using SELECT UTC_TIMESTAMP() + 0 but microseconds are not included in the version of MySQL I am using (out of my control).
So after some research it looks as though SELECT UUID_Short() would be the better way to go. However I am concerned that the precision wont be enough.
If I use SELECT UUID_SHORT() Is there a way to check , before insertion, that the value from UUID_Short does not already exist in the table? I should mention that I would really like to do all this in SQL so I can create an event in the database that will run once every 24 hours. The number of inserts each time the event runs is about 60 records so I don't believe performance will be an issue.

This is how UUID_SHORT() value constructed
(server_id & 255) << 56
+ (server_startup_time_in_seconds << 24)
+ incremented_variable++;
which guarantee to create an unique if you do not run it more than 2^24 = 16,777,216 per server startup. If you restart your MySQL, incremented_variable is reset, but your server_startup_time_in_seconds increase so big that it will not produce duplicated value with previous UUID_SHORT() that has been called on the previous MySQL startup.

Related

RAND() used in SQL trigger to get random number. What happens if it generates already used number?

I'm working on a system where I need to generate a six digit code for every user that signs up. So I'm using this statement (SELECT LEFT(CAST(RAND()*1000000000+999999 AS INT),6) for generating it. I have made that particular row UNIQUE. The thing is that this is all happening through a trigger. My question is, What happens if the number generated by this RAND() is already in use? Will the trigger be executed again as that particular is UNIQUE? or Do I need to write any condition in the trigger itself? If I need to write any condition, Please help me with it.
If the randomizer generates a value that has already been used, and stores it in a column that has a UNIQUE constraint, then the row will violate the constraint, and the INSERT and any other data changed by the trigger will be cancelled.
The trigger will not retry. A retry would need to be executed by your application code, after catching the error.
It would be far simpler to use a table's auto-increment mechanism to guarantee that values are not reused.
An example. Use with caution!!!
CREATE TRIGGER tr_bi_generate_pin
BEFORE INSERT
ON test
FOR EACH ROW
BEGIN
REPEAT
SET NEW.pin = CEIL(255 * RAND()); -- 255 is MAXVALUE for TINYINT UNSIGNED
SET NEW.iterations = NEW.iterations + 1;
UNTIL NOT EXISTS ( SELECT NULL
FROM test
WHERE pin = NEW.pin ) END REPEAT;
END
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=11c263a2eb07b8db133ae13a3d22e549
This code is relatively safe - it counts the amount of iterations, and if it reaches 256 the insertion will fail. But on real system, without such counting and with more wide datatype, the code may cause server hang because of too long, infinite-like, loop. So add maximal iterations amount checking - query fail is better than server hang.

create autoincrement in MySQL with pre text

I have trouble for my project using mysql, i want to create Auto Increment in view table, i create sintax like this one:
SELECT
concat(#AI:= #AI + 1,`code`)
FROM
`TEST TABLE`, (SELECT #AI:=0) as `AI`
Why if i add syntax in first line like this one:
CREATE VIEW `TEST VIEW` as
I have some error :
How fix it, or other method for this?. thanks for advance!
If you were using Oracle, you would use an object called a sequence for this purpose. But, who has the money for Oracle licenses?
If you need a series of numbers and you're using the MariaDB fork, you can do
SELECT seq FROM seq_0_to_99
or some such use of the SEQUENCE engine.
If you need persistent sequence numbers in MySQL, here's a workaround. It's a kludge: If you create the following table:
CREATE TABLE sequence ( /*MySQL*/
sequence_id BIGINT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`sequence_id`)
)
Then issue these three queries one after the other:
INSERT INTO sequence () VALUES (); /*MySQL*/
DELETE FROM sequence WHERE sequence_id < LAST_INSERT_ID();
SELECT LAST_INSERT_ID() AS sequence;
The third query is guaranteed to return a unique sequence number. This guarantee holds even if you have dozens of different client programs connected to your database. (The DELETE query merely keeps this otherwise pointless table from taking up too much space.)
The error message you received makes it clear that you can't use a session variable in a view.
https://dev.mysql.com/doc/refman/5.7/en/create-view.html says:
A view definition is subject to the following restrictions:
The SELECT statement cannot refer to system variables or user-defined variables.
You can't create a view for your query. You'll have to execute the query directly. The only other suggestion I can make is to develop a stored procedure for the query.
It sounds like you want to create a row number for a query result, not an auto-increment column to store in the table.
MySQL 8.0.2 has added the window function ROW_NUMBER(), but 8.0 is still under development as we're writing this. Perhaps in 2018 it will be finished and released as GA.

How to create Trigger to prevent inserting the duplicate row in MYSQL 5.0.27?

This question follows up the following previous question.
is it possible to make a field in Mysql table to to have length max=700, to be unicode (3 bytes) and to be unique?
I have a table MyText which have ID column, text column & many other columns
Id - text - type ..& other fileds
1 - this is my text
2 - xxxx
I want the text column support unicode with max length can hold 700 Unicode characters. I can't set Unique (text) because MYSQL only supports 765 bytes max length for unique column while Unicode takes 3 bytes so I need 2100 bytes (700*3) unique column.
So, the solution is to crate a trigger that prevents the user to insert the duplicate. For example, if user inserts "THIS is My Text" (We won't care case sensitive) into MyText table, then Mysql will abort completely ALL Queries that contain that Inserting Statement and will generate an SQLException to prevent the system to do other query.
Ok, suppose you have to run a series of sql statements in your Java code
insert into MyText('THIS is My Text',1);
insert into OtherTable ('some text');
update othetTable...
Then when the system doing the insert into MyText('THIS is My Text',1);, it should stop doing other queries below it.
Also, some people suggests to do the prefix index to help Nysql to do the select quicker, but I am not sure it is too necessary since I got ID colum which was indexed.
Note: MYSQL 5.027 is 2006 version which is pretty old, but I love it
SO how to create trigger that meets my requirement or how to fix my problem?
I would recommend not using a trigger for this because of performance reasons.
Instead, create an additional column to store an MD5 or SHA1 hash of your value, and make that column unique using a constraint.
As per the above links, both hashing functions exist in your version of MySQL. Alternatively, if it's easier to integrate this in your Java code, you could do the hashing in Java using the MessageDigest class.
The part in your question where you indicate that no further queries should be executed if the insert statement fails because of a duplicate, is best handled using transactions. These are also supported in Java using plain JDBC or most ORM frameworks.

MySql Insert Select uuid()

Say you have a table:
`item`
With fields:
`id` VARCHAR( 36 ) NOT NULL
,`order` BIGINT UNSIGNED NOT NULL
And:
Unique(`id`)
And you call:
INSERT INTO `item` (
`item`.`id`,`item`.`order`
) SELECT uuid(), `item`.`order`+1
MySql will insert the same uuid into all of the newly created rows.
So if you start with:
aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa, 0
bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb, 1
You'll end up with:
aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa, 0
bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb, 1
cccccccc-cccc-cccc-cccc-cccccccccccc, 1
cccccccc-cccc-cccc-cccc-cccccccccccc, 2
How do I command MySql to create a different uuid for each row?
I know that the following works as expected in MSSQL:
INSERT INTO item (
id,[order]
) SELECT newid(), [order]+1
n.b. I know I could SELECT the results, loop through them and issue a separate INSERT command for each row from my PHP code but I don't want to do that. I want the work to be done on the database server where it's supposed to be done.
Turns out uuid() is generating a different uuid per row.
But instead of generating all the chunks randomly, as I would normally expect, MySql appears to only be generating the 2nd chunk randomly. Presumably to be more efficient.
So at a glance the uuids appear identical when in fact MySql has altered the 2nd chunk. e.g.
cccccccc-cccc-cccc-cccc-cccccccccccc
ccccdddd-cccc-cccc-cccc-cccccccccccc
cccceeee-cccc-cccc-cccc-cccccccccccc
ccccffff-cccc-cccc-cccc-cccccccccccc
I assume if there is a collision it would try again.
My bad.
How do I command MySql to create a different uuid foreach row?
MySQL won't allow expressions as a default value. You can work around this by allowing the field to be null. Then add insert/update triggers which, when null, set the field to uuid().
Please try with MID(UUID(),1,36) instead of uuid().
MySQL's UUID() function generates V1 UUIDs, which are split into time, sequence and node fields. If you call it on a single node, only a few bits in the time field will be different; this is referred to as temporal uniqueness. If you call it on different nodes at the exact same time, the node fields will be different; this is referred to as spatial uniqueness. Combining the two is very powerful and gives a guarantee of universal uniqueness, but it also leaks information about the when and where each V1 UUID was created, which can be a security issue. Oops.
V4 UUIDs are generally more popular now because they hash that data (and more) together and thus don't leak anything, but you'll need a different function to get them--and beware what they'll do to performance if you have high INSERT volume; MySQL (at least for now) isn't very good at indexing (pseudo)random values, which is why V1 is what they give you.
First generate an uniq string using the php uniqid() function
and insert to the ID field.

SERIAL-like INT column

I have an app where depending on the type of transaction being added or updated, the ticket number may or may not increment. I can't use a SERIAL datatype for ticket number because it would increment regardless of the transaction type, so I defined ticket number as an INT. So in a multi-user environment if user A is adding or updating a transaction and user B is also doing the same, I test for tran type and if next ticket number is required, then
LET ticket = (SELECT MAX(ticket) [WITH ADDLOCK or UPDLOCK?] FROM transactions) + 1
However this has to be done exactly when the row is being committed or troubles will begin. Can you think of a better way of doing this with: Informix, Oracle, MySQL, SQL-Server, 4Js/Genero or other RDBMS? This is one main factor which will determine what RDBMS I'm going to re-write my app in.
With the Informix DBMS, the SERIAL column will not change after it is inserted; indeed, you cannot update a SERIAL value at all. You can insert a new one with either 0 as the value - in which case a new value is generated - or you can insert some other value. If the other value already exists and there is a unique constraint, that will fail; if it does not exist, or if there is no unique constraint on the serial column, then it will succeed. If the value inserted is larger than the largest value previously inserted, then the next number to be inserted will be one larger again. If the number inserted is smaller, or negative, then there is no effect on the next number.
So, you could do your update without changing the value - no problem. If you need to change the number, you will have to do a delete and insert (or insert and delete), where the insert has a zero in it. If you prefer consistency and you use transactions, you could always delete, and then (re)insert the row with the same number or with a zero to trigger a new number. This assume you have a programming language running the SQL; I don't think you can tweak ISQL and Perform to do that automatically.
So, at this point, I don't see the problem on Informix.
With the appropriate version of IDS (anything that is supported), you can use SEQUENCE to control the values inserted too. This is based on the Oracle syntax and concept; DB2 also supports this. Other DBMS have other equivalent (but different) mechanisms for handling the auto-generated numbers.
That's what sequences were created for and which is supported by most databases (MySQL being the only one that does not have sequences - not 100% sure about Informix though)
Any algorithm that relies on the SELECT MAX(id) anti-pattern is either dead-slow in a multi-user environment or will simply not work correctly in a multi-user environment.
If you need to support MySQL as well, I'd recommend to use the "native" "auto increment" type in each database (serial for PostgreSQL, auto_increment for MySQL, identity for SQL Server, sequence + trigger in Oracle and so on) and let the driver return the generated ID value
In JDBC there is a getGeneratedKeys() method and I'm sure other interfaces have something similar.
From your tags it's hard to tell what database you are using.
For SQL Server (since it's listed) I suggest
ticket_num = (SELECT MAX(ticket_number) FROM transactions with (updlock)) + 1