I have a MYSQL table, with 5 columns in it:
id bigint
name varchar
description varchar
slug
Can I get MySQL to automatically generate the value of slug as a 256 Bit Hash of name+description?
I am now using PHP to generate an SHA256 value of the slug prior to saving it.
Edit:
By automatic, I mean see if it's possible to change the default value of the slug field, to be a computed field that's the sha256 of name+description.
I already know how to create it as part of an insert operation.
MySQL 5.7 supports generated columns so you can define an expression, and it will be updated automatically for every row you insert or update.
CREATE TABLE IF NOT EXISTS MyTable (
id int NOT NULL AUTO_INCREMENT,
name varchar(50) NOT NULL,
description varchar(50) NOT NULL,
slug varchar(64) AS (SHA2(CONCAT(name, description), 256)) STORED NOT NULL,
PRIMARY KEY (id)
) DEFAULT CHARSET=utf8;
If you use an earlier version of MySQL, you could do this with TRIGGERs:
CREATE TRIGGER MySlugIns BEFORE INSERT ON MyTable
FOR EACH ROW SET slug = SHA2(CONCAT(name, description));
CREATE TRIGGER MySlugUpd BEFORE UPDATE ON MyTable
FOR EACH ROW SET slug = SHA2(CONCAT(name, description), 256);
Beware that concat returns NULL if any one column in the input is NULL. So, to hash in a null-safe way, use concat_ws. For example:
select md5(concat_ws('', col_1, .. , col_n));
Use MySQL's CONCAT() to combine the two values and SHA2() to generate a 256 bit hash.
CREATE TABLE IF NOT EXISTS `mytable` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`description` varchar(50) NOT NULL,
`slug` varchar(64) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `mytable` (`name`,`description`,`slug`)
VALUES ('Fred','A Person',SHA2(CONCAT(`name`,`description`),256));
SELECT * FROM `mytable`
OUTPUT:
COLUMN VALUE
id 1
name Fred
description A Person
slug ea76b5b09b0e004781b569f88fc8434fe25ae3ad17807904cfb975a3be71bd89
Try it on SQLfiddle.
Related
I'm learning SQL.
I'm trying to insert data. My MySQL database looks like this.
CREATE TABLE category (
category_id CHAR(100),
category_name VARCHAR(120) NOT NULL,
PRIMARY KEY (category_id)
)
I ran this command
INSERT INTO category (category_name) VALUES ("test");
But I got this error
ERROR 1364 (HY000): Field 'category_id' doesn't have a default value
Thank you in advance.
If you want to have an incrementing ID it would need to be an int. You Generally want to make ID's integers not chars to speed up lookup regardless.
CREATE TABLE IF NOT EXISTS category (
`category_id` INT NOT NULL AUTO_INCREMENT,
`category_name` VARCHAR(120) NOT NULL,
PRIMARY KEY (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_unicode_ci;
That will let you insert without adding your own ID, and automatically generate unique ID's for the records.
Issue was you set your category_id field to not have a default value and also not allow null, which means you -have- to set a value for it in the insert. If you wanted to use your existing table you would need to do this:
INSERT INTO category (category_id, category_name) VALUES ("someid", "test");
I'm using MySQL Workbench.
I would like to create a table named courseInfo and I want to put a column named moduleCode in it, but I want it to always be similar in format: CFSM H0000 where the four zeros are a number that increases starting with 0000.
For example:
CFSM H0001
CFSM H0002
[..]
You cannot auto-increment character type columns in MySQL, as auto-increment is only possible on integer type columns. One (alphanumeric) auto-incrementing moduleCode column would therefore not be possible. However, you could try splitting up the moduleCode into two columns, for example like so:
CREATE TABLE `courseInfo` (
`prefix` CHAR(6) NOT NULL DEFAULT 'CFSM H',
`id` SMALLINT(4) UNSIGNED ZEROFILL NOT NULL AUTO_INCREMENT,
KEY (`id`)
) AUTO_INCREMENT = 0;
Where prefix could for example be "CFSM H" and id could be 0001
Then, upon executing SELECT statements, you could merge the prefix column with the id column into a moduleCode column with CONCAT, e.g.:
SELECT CONCAT(`prefix`, `id`) as `moduleCode` FROM `courseInfo`;
An alternative approach (from MySQL version 5.7 and up) seems to be the use of a generated column, for example:
CREATE TABLE `courseInfo` (
`prefix` CHAR(6) NOT NULL DEFAULT 'CFSM H',
`id` SMALLINT(4) UNSIGNED ZEROFILL NOT NULL AUTO_INCREMENT,
`moduleCode` CHAR(10) AS (CONCAT(`prefix`, `id`)),
KEY (`id`)
) AUTO_INCREMENT = 0;
However, the above example of a generated column would not work, because moduleCode is dependent on an auto-increment column, and the auto-increment is not executed yet at the time the generated column is computed. See also: https://dev.mysql.com/doc/refman/5.7/en/create-table-generated-columns.html. It would throw an ER_GENERATED_COLUMN_REF_AUTO_INC error.
You could therefore use the first solution, or try to add moduleCode as a column and use an AFTER INSERT trigger to update its value:
CREATE TABLE `courseInfo` (
`prefix` CHAR(6) NOT NULL DEFAULT 'CFSM H',
`id` SMALLINT(4) UNSIGNED ZEROFILL NOT NULL AUTO_INCREMENT,
`moduleCode` CHAR(10),
KEY (`id`),
UNIQUE KEY `unique_index` (`prefix`,`id`)
) AUTO_INCREMENT = 0;
DELIMITER //
CREATE TRIGGER `addModuleCode` AFTER INSERT ON `courseInfo`
FOR EACH ROW
BEGIN
UPDATE `courseInfo` SET `moduleCode` = CONCAT(NEW.`prefix`, NEW.`id`) WHERE `prefix` = NEW.`prefix` AND `id` = NEW.`id`;
END;//
DELIMITER ;
I have a stored procedure that I've used to 'de-identify' client information when I want to use it in a test environment. I am replacing actual names and addresses with random values. I have database tables in a database called dict (for dictionary) for female names, male names, last names, and addresses.
Each of these has a field called f_row_id that is a sequential number from 1 to x, one for each record in the table.
We recently upgraded to mySQL 8 and the stored procedure quit working. I ended up with NULL for every field where I tried filling in a random value out of the other table. In trying to find what will now work, I'm unable to get the following query to work as I expect:
SELECT
f_enroll_id,
(SELECT f_name FROM dict.dummy_female_first_name fn WHERE fn.f_row_id = (FLOOR(RAND() * 850) + 1) LIMIT 1)
FROM
t_enroll
My data table (that I eventually want to have contain random names) is called t_enroll. There is an ID field in that (f_enroll_id) I want to get a list of each ID and a random first name for each record in that table.
There are 850 records in the table of random first names (dummy_female_first_name) (in my stored procedure this is a session variable that I compute at the start of the procedure).
When I first tried running this I got an error that my sub-query returned more than one value. I don't understand why it would do that since (FLOOR(RAND() * 850) + 1) should return a single integer. So I added the LIMIT 1. But when I run this, about half of the returned rows have NULL for the first name.
I have verified that all the rows in my first name table have a row ID, that the row ID is unique, and there not any gaps in the numbers.
What do you think is causing this?
Thanks in advance!
Here is the schema for the table that I'm updating:
CREATE TABLE `t_enroll` (
`f_enroll_id` int(15) NOT NULL AUTO_INCREMENT,
`f_status` int(2) DEFAULT NULL,
`f_date_enrolled` date NOT NULL DEFAULT '0000-00-00',
`f_first_name` varchar(20) DEFAULT NULL,
`f_mi` char(1) DEFAULT NULL,
`f_last_name` varchar(20) NOT NULL DEFAULT '',
`f_maiden_name` varchar(20) DEFAULT NULL,
`f_dob` date NOT NULL DEFAULT '0000-00-00',
`f_date_fee_received` date NOT NULL DEFAULT '0000-00-00',
`f_gender` int(11) NOT NULL DEFAULT '2',
`f_address_1` varchar(40) DEFAULT NULL,
`f_address_2` varchar(20) DEFAULT NULL,
`f_quadrant` char(2) DEFAULT NULL,
`f_city` varchar(25) DEFAULT NULL,
`f_state` char(2) NOT NULL DEFAULT '',
`f_county` varchar(3) NOT NULL,
`f_zip_code` varchar(10) DEFAULT NULL,
PRIMARY KEY (`f_enroll_id`),
KEY `f_date_enrolled` (`f_date_enrolled`),
KEY `f_last_name` (`f_last_name`),
KEY `f_first_name` (`f_first_name`),
KEY `f_dob` (`f_dob`),
KEY `f_gender` (`f_gender`)
ENGINE=InnoDB AUTO_INCREMENT=532 DEFAULT CHARSET=latin1 COMMENT='InnoDB free: 15360 kB';
Here is the schema for the dictionary table where I pull names from:
CREATE TABLE `dummy_female_first_name` (
`f_row_id` int(11) NOT NULL,
`f_name` varchar(25) NOT NULL,
PRIMARY KEY (`f_row_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
As I mentioned in my comment, I have found an alternate approach using the ORDER BY RAND() LIMIT 1 variation. But I am still curious as to what is going on that prevented my original method to fail. This is something that changed in the more recent mySQL version because it used to work.
Thanks again.
It is a much more expensive approach, but you can use:
SELECT f_enroll_id,
(SELECT f_name FROM dict.dummy_female_first_name fn ORDER BY rand() LIMIT 1)
FROM t_enroll;
You can make this more efficient using:
SELECT f_enroll_id,
(SELECT f_name
FROM dict.dummy_female_first_name fn
WHERE rand() < 0.01
ORDER BY rand() LIMIT 1
)
FROM t_enroll;
The where clause means that about 8 rows will filter through so the sorting will be much faster.
I want to create a table name Users where I should have have columns User, cookieID, sessionID, Geo and then I want to first three columns to have some random unique value assigned automatically. I tried to make all three columns AUTO_INCREMENT with User column PRIMARY and 'cookieIDandsessionIDcolumnUNIQUE`. The SQL code is:
CREATE TABLE `users` ( `User` VARCHAR(20) NOT NULL AUTO_INCREMENT ,
`cookieID` INT(20) NULL DEFAULT NULL AUTO_INCREMENT ,
`sessionID` INT(20) NULL DEFAULT NULL AUTO_INCREMENT ,
`Geo` VARCHAR(30) NULL DEFAULT NULL ,
PRIMARY KEY (`User`), UNIQUE (`cookieID`), UNIQUE (`sessionID`), UNIQUE (`Geo`));
But, it did not work because only one column can be declared as AUTO_INCREMENT which must be PRIMARY.
What is the another approach to do this?
Since the auto-increment cannot be applied to multiple to rows and there no option for sequence in MySQL. You can use triggers for the unique update of the row with datetime.
Change to table creation to be of single auto-increment row.
CREATE TABLE `users` ( `User` VARCHAR(20) NOT NULL,
`cookieID` INT(20) NULL DEFAULT NULL,
`sessionID` INT(20) NULL DEFAULT NULL AUTO_INCREMENT ,
`Geo` VARCHAR(30) NULL DEFAULT NULL,
PRIMARY KEY (`User`), UNIQUE (`cookieID`), UNIQUE (`sessionID`), UNIQUE (`Geo`));
Create a trigger on the same table as below. You can set the unique values under the SET for as many column as you want.
CREATE DEFINER=`root`#`localhost` TRIGGER `users_BEFORE_INSERT` BEFORE INSERT ON `users` FOR EACH ROW BEGIN
SET
NEW.cookieID = (SELECT curdate()+curtime());
END
Now when you insert into the table as below.
insert into `users`(`User`) values("test");
You table looks like this.
User cookieID sessionID Geo
test 20315169 0 NULL
If the value which are auto incrementing, you wanna keep both values the same. Then copy the value of one column to another during insertion time of new value.
Here's what I'm trying to do:
CREATE TABLE IF NOT EXISTS hashes (
id int NOT NULL AUTO_INCREMENT,
text varchar(50) NOT NULL,
hash varchar(64) NOT NULL AS (SHA2(CONCAT(text), 256) STORED,
PRIMARY KEY (id)
) DEFAULT CHARSET=utf8;
And then I want to run an insert like this:
INSERT INTO `hashes` (`text`) VALUES ('testing');
From the research I've done, the id should be automatically generated since auto_increment is enabled, so I don't need to define it in the insert query.
From my CREATE TABLE query, the hash should be automatically generated based upon the data entered into the text field. However, when I run the CREATE TABLE command I get an error with this line:
hash varchar(64) NOT NULL AS (SHA2(CONCAT(text), 256) STORED
I'm just wanting the hash to be automatically generated similar to how CURRENT_TIMESTAMP will automatically generate the current time by default.
What am I doing wrong?
It seems you have syntax error. You should write NOT NULL after SHA2 hash function. Please try:
CREATE TABLE IF NOT EXISTS hashes (
id int NOT NULL AUTO_INCREMENT,
text varchar(50) NOT NULL,
hash varchar(64) AS (SHA2(CONCAT(text), 256)) STORED NOT NULL ,
PRIMARY KEY (id)
) DEFAULT CHARSET=utf8;
INSERT INTO `hashes` (`text`) VALUES ('testing');
You don't need to declare your hash column as NOT NULL. It's based on another NOT NULL column, text, so the hash will naturally be NOT NULL as well.
You also have forgotten a closing parenthesis.
hash varchar(64) AS (SHA2(CONCAT(text), 256) STORED,
1 2 3 3 2 ^
You need another closing paren where I indicated ^.
If you already have the table filled by some content, you can Alter it with :
ALTER TABLE `page` ADD COLUMN `hash` char(64) AS (SHA2(`content`, 256)) AFTER `content`
This solution will add hash column right after the content one, make hash for existing and new records too. Unique index can be added to prevent insertion of large content duplicates.