Im revisiting my database and noticed I had some primary keys that were of type INT.
This wasn't unique enough so I thought I would have a guid.
I come from a microsoft sql background and in the ssms you can
choose type to "uniqeidentifier" and auto increment it.
In mysql however Ive found that you have to make triggers that execute on insert for the tables you want
to generate a guide id for. Example:
Table:
CREATE TABLE `tbl_test` (
`GUID` char(40) NOT NULL,
`Name` varchar(50) NOT NULL,
PRIMARY KEY (`GUID`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Trigger:
CREATE TRIGGER `t_GUID` BEFORE INSERT ON `tbl_test`
FOR EACH ROW begin
SET new.GUID = uuid();
Alternatively you have to insert the guid yourself in the backend.
Im no DB expert but still remember that triggers cause performance problems.
The above is something I found here and is 9 years old so I was hoping something has changed?
As far as stated in the documentation, you can use uid() as a column default starting version 8.0.13, so something like this should work:
create table tbl_test (
guid binary(16) default (uuid_to_bin(uuid())) not null primary key,
name varchar(50) not null
);
This is pretty much copied from the documentation. I don't have a recent enough version of MySQL at hand to test this.
You can make a
INSERT INTO `tbl_test` VALUES (uuid(),'testname');
This would generate a new uuid, when you call it.
Or you can also use the modern uuid v4 by using one of these functions instead of the standard uuid(), which is more random than the uuid in mysql
How to generate a UUIDv4 in MySQL?
You can use since 8.0.13
CREATE TABLE t1 (
uuid_field VARCHAR(40) DEFAULT (uuid())
);
But you wanted more than unique, but here are only allowed internal functions and not user defined as for uuid v4, for that uyou need the trogger
As per the documentation, BINARY(x) adds some hidden padding bytes to the end of each entry, & VARCHAR(40) also wastes space by not being encoded directly in binary. Using VARBINARY(16) would be more efficient.
Also, more entropy (unguessability / security) per byte is available from RANDOM_BYTES(16) than standardized UUIDs, because they use some sections to encode constant metadata.
Perhaps the below will work for your needs.
-- example
CREATE TABLE `tbl_test` (
`GUID` VARBINARY(16) DEFAULT (RANDOM_BYTES(16)) NOT NULL PRIMARY KEY,
`Name` VARCHAR(50) NOT NULL
);
Related
I want to do something like this:
create table app_users
(
app_user_id smallint(6) not null auto_increment primary key,
api_key char(36) not null default uuid()
);
However this results in a error, is there a way to call a function for a default value in mysql?
thanks.
No, you can't.
However, you could easily create a trigger to do this, such as:
CREATE TRIGGER before_insert_app_users
BEFORE INSERT ON app_users
FOR EACH ROW
SET new.api_key = uuid();
As of mysql v8.0.13 it is possible to use an expression as a default value for a field:
The default value specified in a DEFAULT clause can be a literal constant or an expression. With one exception, enclose expression default values within parentheses to distinguish them from literal constant default values.
CREATE TABLE t1 (
uuid_field VARCHAR(32) DEFAULT (uuid()),
binary_uuid BINARY(16) DEFAULT (UUID_TO_BIN(UUID()))
);
As already stated you can't.
If you want to simulate this behavior you can use a trigger in this way:
CREATE TRIGGER before_insert_app_users
BEFORE INSERT ON app_users
FOR EACH ROW
IF new.uuid IS NULL
THEN
SET new.uuid = uuid();
END IF;
You still have to update previously existing rows, like this:
UPDATE app_users SET uuid = (SELECT uuid());
Unfortunately no, MySQL 5 requires constants for the default. The issue was discussed in much more detail in the link below. But the only answer is to allow null and add a table trigger.
MySQL only recently accepted UUID as part of their DB package, and it's not as feature rich as we'd like.
http://www.phpbuilder.com/board/showthread.php?t=10349169
I believe you can't:
the default value must be a constant; it cannot be a function or an expression
Note that MySQL's UUID() returns CHAR(36), and storing UUIDs as text (as shown in the other answers) is obviously inefficient. Instead, the column should be BINARY(16), and you can use UUID_TO_BIN() when inserting data and BIN_TO_UUID() when reading it back.
CREATE TABLE app_users
(
app_user_id SMALLINT(6) NOT NULL AUTO_INCREMENT PRIMARY KEY,
api_key BINARY(16)
);
CREATE TRIGGER before_insert_app_users
BEFORE INSERT ON app_users
FOR EACH ROW
IF new.api_key IS NULL
THEN
SET new.api_key = UUID_TO_BIN(UUID());
END IF;
Note that since MySQL doesn't really know this is a UUID, it can be difficult to troubleshoot problems with it stored as binary. This article explains how to create a generated column that will convert the UUID to text as needed without taking up any space or worrying about keeping separate binary and text versions in sync: https://mysqlserverteam.com/storing-uuid-values-in-mysql-tables/
In MariaDB starting from version 10.2.1 you can. See its documentation.
CREATE TABLE test ( uuid BINARY(16) PRIMARY KEY DEFAULT unhex(replace(uuid(),'-','')) );
INSERT INTO test () VALUES ();
SELECT * FROM test;
I'm not sure if the above answers are for an older version, but I saw somewhere that you can do this using the unhex() function. I tried it and it works. (maria db version 10.2)
You can do
.... column_name binary(16) not null default unhex(replace(uuid(),'-',''))
and it works. To see the uuid just do hex(column_name).
Harrison Fisk's answer was great when it was written, but now it's outdated.
Nowadays you can use an expression as a DEFAULT value. This is supported since MySQL 8.0 and MariaDB 10.2. Note that, if you're going to use non-deterministic functions like NOW() or USER(), you should not use binlog_format=statement.
Im revisiting my database and noticed I had some primary keys that were of type INT.
This wasn't unique enough so I thought I would have a guid.
I come from a microsoft sql background and in the ssms you can
choose type to "uniqeidentifier" and auto increment it.
In mysql however Ive found that you have to make triggers that execute on insert for the tables you want
to generate a guide id for. Example:
Table:
CREATE TABLE `tbl_test` (
`GUID` char(40) NOT NULL,
`Name` varchar(50) NOT NULL,
PRIMARY KEY (`GUID`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Trigger:
CREATE TRIGGER `t_GUID` BEFORE INSERT ON `tbl_test`
FOR EACH ROW begin
SET new.GUID = uuid();
Alternatively you have to insert the guid yourself in the backend.
Im no DB expert but still remember that triggers cause performance problems.
The above is something I found here and is 9 years old so I was hoping something has changed?
As far as stated in the documentation, you can use uid() as a column default starting version 8.0.13, so something like this should work:
create table tbl_test (
guid binary(16) default (uuid_to_bin(uuid())) not null primary key,
name varchar(50) not null
);
This is pretty much copied from the documentation. I don't have a recent enough version of MySQL at hand to test this.
You can make a
INSERT INTO `tbl_test` VALUES (uuid(),'testname');
This would generate a new uuid, when you call it.
Or you can also use the modern uuid v4 by using one of these functions instead of the standard uuid(), which is more random than the uuid in mysql
How to generate a UUIDv4 in MySQL?
You can use since 8.0.13
CREATE TABLE t1 (
uuid_field VARCHAR(40) DEFAULT (uuid())
);
But you wanted more than unique, but here are only allowed internal functions and not user defined as for uuid v4, for that uyou need the trogger
As per the documentation, BINARY(x) adds some hidden padding bytes to the end of each entry, & VARCHAR(40) also wastes space by not being encoded directly in binary. Using VARBINARY(16) would be more efficient.
Also, more entropy (unguessability / security) per byte is available from RANDOM_BYTES(16) than standardized UUIDs, because they use some sections to encode constant metadata.
Perhaps the below will work for your needs.
-- example
CREATE TABLE `tbl_test` (
`GUID` VARBINARY(16) DEFAULT (RANDOM_BYTES(16)) NOT NULL PRIMARY KEY,
`Name` VARCHAR(50) NOT NULL
);
I am currently facing an issue with designing a database table and updating/inserting values into it.
The table is used to collect and aggregate statistics that are identified by:
the source
the user
the statistic
an optional material (e.g. item type)
an optional entity (e.g. animal)
My main issue is, that my proposed primary key is too large because of VARCHARs that are used to identify a statistic.
My current table is created like this:
CREATE TABLE `Statistics` (
`server_id` varchar(255) NOT NULL,
`player_id` binary(16) NOT NULL,
`statistic` varchar(255) NOT NULL,
`material` varchar(255) DEFAULT NULL,
`entity` varchar(255) DEFAULT NULL,
`value` bigint(20) NOT NULL)
In particular, the server_id is configurable, the player_id is a UUID, statistic is the representation of an enumeration that may change, material and entity likewise. The value is then aggregated using SUM() to calculate the overall statistic.
So far it works but I have to use DELETE AND INSERT statements whenever I want to update a value, because I have no primary key and I can't figure out how to create such a primary key in the constraints of MySQL.
My main question is: How can I efficiently update values in this table and insert them when they are not currently present without resorting to deleting all the rows and inserting new ones?
The main issue seems to be the restriction MySQL puts on the primary key. I don't think adding an id column would solve this.
Simply add an auto-incremented id:
CREATE TABLE `Statistics` (
statistis_id int auto_increment primary key,
`server_id` varchar(255) NOT NULL,
`player_id` binary(16) NOT NULL,
`statistic` varchar(255) NOT NULL,
`material` varchar(255) DEFAULT NULL,
`entity` varchar(255) DEFAULT NULL,
`value` bigint(20) NOT NULL
);
Voila! A primary key. But you probably want an index. One that comes to mind:
create index idx_statistics_server_player_statistic on statistics(server_id, player_id, statistic)`
Depending on what your code looks like, you might want additional or different keys in the index, or more than one index.
Follow the below hope it will solve your problem :-
- First use a variable let suppose "detailed" as money with your table.
- in your project when you use insert statement then before using statement get the maximum of detailed (SELECT MAX(detailed)+1 as maxid FROM TABLE_NAME( and use this as use number which will help you to FETCH,DELETE the record.
-you can also update with this also BUT during update MAXIMUM of detailed is not required.
Hope you understand this and it will help you .
I have dug a bit more through the internet and optimized my code a lot.
I asked this question because of bad performance, which I assumed was because of the DELETE and INSERT statements following each other.
I was thinking that I could try to reduce the load by doing INSERT IGNORE statements followed by UPDATE statements or INSERT .. ON DUPLICATE KEY UPDATE statements. But they require keys to be useful which I haven't had access to, because of constraints in MySQL.
I have fixed the performance issues though:
By reducing the amount of statements generated asynchronously (I know JDBC is blocking but it worked, it just blocked thousand of threads) and disabling auto-commit, I was able to improve the performance by 600 times (from 60 seconds down to 0.1 seconds).
Next steps are to improve the connection string and gaining even more performance.
I have been testing a database i am doing right now and i am noticing that it is letting me insert null values into fields that are part of a primary key, despite stating in the script that the value of the field should be NOT NULL. I am using MAC's MySQL Workbench, and I have been googling around and can't figure out why this is happening. (Maybe I am too brain-fried right now... I am even starting to doubt myself)
Part of the script of the database creation (these are the tables I have tested..):
DROP DATABASE IF EXISTS solytierra ;
CREATE DATABASE IF NOT EXISTS solytierra DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci ;
USE solytierra ;
DROP TABLE IF EXISTS solytierra.Cliente ;
CREATE TABLE IF NOT EXISTS solytierra.Cliente (
CIF VARCHAR(25) NOT NULL,
Nombre VARCHAR(100) NULL,
EmailGeneral VARCHAR(45) NULL,
Web VARCHAR(45) NULL,
Notas VARCHAR(150) NULL,
insertado Timestamp,
CONSTRAINT pk_Cliente PRIMARY KEY (CIF)
) ENGINE=InnoDB;
DROP TABLE IF EXISTS solytierra.PersonaContacto ;
CREATE TABLE IF NOT EXISTS solytierra.PersonaContacto (
Cliente_CIF VARCHAR(25) NOT NULL,
Nombre VARCHAR(50) NOT NULL,
Apellidos VARCHAR(100) NOT NULL,
Notas VARCHAR(150) NULL,
CONSTRAINT pk_PersonaContacto PRIMARY KEY (Cliente_CIF , Nombre , Apellidos),
CONSTRAINT fk_PersonaContacto_Cliente FOREIGN KEY (Cliente_CIF)
REFERENCES solytierra.Cliente (CIF)
ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;
...
It will let me create Clients without CIF, "PersonaContacto" without Cliente_CIF or without "Nombre"....
I have also tested other databases that i already had that used to work and it is happening the same in an all them.
Got it!!
I don't know what sql mode i was running on by default, but with this:
SET sql_mode = TRADITIONAL;
It is now running perfectly! I didn't know that there were different sql modes! Thanks a lot to everyone for your time and efforts! It really helped me to see that the problem was in my workbench, not the code and look for the answer accordingly! I hope this thread will be useful for future beginners like me!
If the value being stored in the column CIF is actually a NULL, then the expression LENGTH(CIF) should also return NULL. (If it's a zero length string, then LENGTH(CIF) will return 0.
To verify:
SELECT c.CIF, LENGTH(c.CIF) FROM solytierra.Cliente c ;
SELECT c.CIF FROM solytierra.Cliente c WHERE c.CIF IS NULL;
If you are running an INSERT statement, I can't explain the behavior you are observing, either MySQL allowing a NULL value to be stored or MySQL providing an implicit default value.)
If it's a zero length string being stored, that's the behavior we would expect if the columns were not explicitly declared to be NOT NULL but were later declared to part of the primary key. It's also the behavior we'd expect if the column were defined NOT NULL DEFAULT ''.
When the NOT NULL is omitted from the column declaration and the column is later declared to be part of the PRIMARY KEY, MySQL will use an an implicit default value based on the datatype of the column (zero length string for VARCHAR, zero for an integer, etc.)
But I'm not able to reproduce the problem you report, with the table definitions you've posted.
I recommend you check the table definition by getting the output from:
SHOW CREATE TABLE solytierra.Cliente;
I have this table for users that stores their usernames and other data, thats done like this (stripped down):
CREATE TABLE `prod_users` (
`p_user_id` INT(11) NOT NULL AUTO_INCREMENT,
`p_user_name` VARCHAR(200) NOT NULL DEFAULT ''
`p_comp_name` VARCHAR(300) NOT NULL DEFAULT '',
PRIMARY KEY (`p_user_id`)
)
COLLATE='utf8_general_ci'
ENGINE=MyISAM
Each time a user signs up, he'll provide a company name as well.
There's another table called prod_profiles, which stores profile details like phone nos. fax nos. etc.
CREATE TABLE `prod_profiles` (
`pf_gen_id` INT(11) NOT NULL AUTO_INCREMENT,
`pf_user_id` INT(11) NOT NULL DEFAULT '0',
`pf_user_name` VARCHAR(200) NOT NULL DEFAULT ''
`pf_comp_name` VARCHAR(300) NOT NULL DEFAULT '',
PRIMARY KEY (`pf_gen_id`)
)
COLLATE='utf8_general_ci'
ENGINE=MyISAM
When a new user signs up and his details are added to prod_users, is it possible to automatically add his new user_id, user_name and comp_name details to prod_profile using MySql itself? Since each user will have a new p_user_id and we wont know it, it'll be difficult using php. Can this be achieved inside MySql itself without any problems?
It isn't difficult using PHP, since you have the LAST_INSERT_ID() available for use, be it via mysql_insert_id() or mysqli::$insert_id, PDO::lastInsertId() or whatever your API provides. As long as you call the two INSERT statements in immediate succession on the same script (it is connection dependent), MySQL will supply the correct p_user_id.
However, you can use an AFTER INSERT trigger to force MySQL to create the new row automatically:
CREATE TRIGGER build_profile AFTER INSERT ON prod_users
FOR EACH ROW
BEGIN
INSERT INTO prod_profiles
(pf_user_id, pf_user_name, pf_comp_name)
VALUES (NEW.p_user_id, NEW.p_user_name, NEW.p_comp_name)
END
Review the MySQL CREATE TRIGGER syntax reference for full details and options.
You can use the next mysql function: LAST_INSERT_ID(); which returns the last auto increased id.
Therefore , add a user and then add a prod_profile , while pf_user_id value will be the returned value of last_insert_id().
INSERT INTO `prod_users`(`p_user_name`,`p_comp_name`) VALUES('Dan' , 'Stackover')
INSERT INTO `prod_profiles`(`pf_user_id`,`pf_user_name`,`pf_comp_name`) VALUES(LAST_INSERT_ID(),'Dan','Stackover')
Please notice: I have to say , that storing the username and company_name twice for the same user in two different tables is a reall waste...
Consider re-thinking about your DB structre and logic.