Initial goal:
I would like to generate random and unique codes (6 digits) in a table.
I use a SQL query like this one to do that:
SELECT SUBSTRING(CRC32(RAND()), 1, 6) as myCode
FROM `codes`
HAVING myCode NOT IN (SELECT code FROM `codes`)
I asked me about how it will react when there will be no more available codes so I do the following test
Test context:
MySQL version: 5.5.20
MySQL Table:
CREATE TABLE `codes` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`code` VARCHAR( 10 ) NOT NULL ,
UNIQUE (
`code`
)
) ENGINE = InnoDB;
Initial data:
INSERT INTO `codes` (`id`, `code`)
VALUES (NULL, '1'), (NULL, '2'), (NULL, '3'), (NULL, '4'), (NULL, '5'), (NULL, '6'), (NULL, '7'), (NULL, '8');
SQL Query:
SELECT SUBSTRING(CRC32(RAND()), 1, 1) as myCode
FROM `codes`
HAVING myCode NOT IN (SELECT code FROM `codes`)
By execute this query, I expect that it will always return 9 because it is the only code of one digit which does not exists.
But the result is:
Sometime it return any rows
Sometime it return rows with values that already exists
I don't understand this behavior so if someone can help :)
So the big question is:
How MySQL can return rows with values that already exists?
Thanks
I would fill a sequencetable table with all the possible values, in sequence.
Then the random query just randomly selects records from the sequencetable, and each time it picks a record it deletes it. This way you will surely get all the numbers, without wasting time in finding a "hole" number (not already picked up).
CREATE TABLE `sequencetable`
(
`sequence` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`sequence`)
)
ENGINE=InnoDB
AUTO_INCREMENT=1;
Fill the sequence (no need for the AUTOINCREMENT actually).
DECLARE i INT;
SET i=1;
REPEAT
INSERT INTO sequencetable VALUES (i);
SET i=i+1;
UNTIL i>999999 END REPEAT;
Select a random record from the sequence (do this in a loop until records are available):
DECLARE sequencen INT;
SET sequencen =
(SELECT sequence FROM sequencetable ORDER BY RAND() LIMIT 1);
DELETE FROM sequencetable WHERE sequence = sequencen;
Related
Triggers are new to me and I'm just wondering how can I fully benefit on using them.
Here are the two tables shelfs and shelfs_log:
CREATE TABLE `shelfs` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`shelf_name` VARCHAR(50) NULL DEFAULT NULL
`storage_id` INT(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`) USING BTREE
)
CREATE TABLE `shelfs_log` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`shelf_name` VARCHAR(50) NULL DEFAULT NULL
`storage_id` INT(11) NOT NULL,
`user_id` INT(11) NOT NULL,
PRIMARY KEY (`id`) USING BTREE
)
PHP function that gets executed when a new shelf is created to a storage: $shelf_name, $storage_id (and $user_id):
public static function createNewShelf($shelf_name, $storage_id)
{
global $conn;
$sql = "INSERT INTO shelfs (shelf_name, storage_id)
VALUES ('$shelf_name', '$storage_id')";
$result = $conn->query($sql);
$newShelf = array();
if ($result) {
$newShelf['success'] = true;
} else {
$newShelf['success'] = false;
}
return $newShelf;
$conn->close();
}
But I would like to have a trigger that keeps log of these inserts to a separete table but also having the user_id as an extra parameter that I could pass on these INSERT queries without writing the user_id to shelfs table.
Trigger would be something like:
CREATE DEFINER=`root`#`localhost` TRIGGER `shelfs`
AFTER INSERT ON `shelfs`
FOR EACH ROW
INSERT INTO shelfs_log (shelf_name, storage_id, user_id)
VALUES (NEW.shelf_name, NEW.storage_id, NEW.user_id)
A little hard me to explain and I couldn't find help for this matter on google.
Thanks in advance!
Full solution for my problem was provided by the help of Akina. The extra parameters are added by using SET #paramater_name := $parameter_value; in the original INSERT query and is run by using multi_query():
$sql = "SET #user_id := $user_id; INSERT INTO shelfs (shelf_name, storage_id) VALUES ('$shelf_name', '$storage_id');";
$result = $conn->multi_query($sql);
And after the trigger will pick up the extra parameter named #user_id:
CREATE DEFINER=`root`#`localhost` TRIGGER `shelfs`
AFTER INSERT ON `shelfs`
FOR EACH ROW
INSERT INTO shelfs_log (shelf_name, storage_id, user_id)
VALUES (NEW.shelf_name, NEW.storage_id, #user_id)
Please, show me how I can pass the user_id without writing it to the first table if its possible. – Manny
Extra data not passed into the query can be passed via user-defined variable:
CREATE DEFINER=`root`#`localhost` TRIGGER `shelfs`
AFTER INSERT ON `shelfs`
FOR EACH ROW
INSERT INTO shelfs_log (shelf_name, storage_id, user_id)
VALUES (NEW.shelf_name, NEW.storage_id, #user_id);
Method 1. Assign the value before INSERT query.
-- set the variable
SET #user_id := '444';
-- now insert
INSERT INTO shelfs (shelf_name, storage_id)
VALUES ('name 1', '11'), ('name 2', '22');
Both queries must be executed in the same connection. Multiple rows with the same user_id can be inserted.
Method 2. Assign the value in the query using INSERT .. SELECT.
INSERT INTO shelfs (shelf_name, storage_id)
SELECT 'name 3', '33'
FROM ( SELECT #user_id := 555) set_variable
UNION ALL
SELECT 'name 4', '44';
Multiple rows can be inserted using UNION ALL in SELECT. But all of them will use the same user_id value taken from the most last subquery (i.e. only one subquery should contain FROM clause, all other may not have it).
Method 3. Use inline assignment.
INSERT INTO shelfs (shelf_name, storage_id)
VALUES ('name 5', CASE WHEN (#user_id := 666) IS NOT NULL THEN '55' END),
('name 6', CASE WHEN (#user_id := 777) IS NOT NULL THEN '66' END);
Multiple rows with different user_id values can be inserted.
DEMO fiddle
I have a table with auto-incremented ID, colA and colB (with set default value), e.g.:
CREATE TABLE `some_db`.`test` ( `id` INT NOT NULL AUTO_INCREMENT , `colA` INT NOT NULL , `colB` INT NOT NULL DEFAULT '0' , PRIMARY KEY (`id`)) ENGINE = InnoDB;
Now, I want user to be able to insert the new row, but at the same time DO NOT allow to modify id (auto-incremented) or colB (just use default value).
Is that possible?
I tried to give INSERT and UPDATE only for colA, but that still gives me INSERT command denied error:
GRANT SELECT, INSERT (`colA`), UPDATE (`colA`) ON `some_db`.`test` TO 'test_user'#'%';
I don't know if that matters, I'm using MariaDB.
Thanks!
EDIT:
OK, I need to re-state my question now.
After executing e.g.:
INSERT INTO `test` (`colA`) VALUES (10)
Everything is working fine.
But phpmyadmin GUI (that I use as a front-end here) is translating to:
INSERT INTO `test` (`id`, `colA`, `colB`) VALUES (NULL, 10, ``)
...when no values are given, and it can't be executed (#1143 - INSERT command denied to user test_user#...
So, is there any way that rows can be inserted through phpmyadmin GUI (not SQL command) with such restrictions? (I edited the question title).
CREATE TABLE `test` ( `id` INT NOT NULL AUTO_INCREMENT ,
`colA` INT NOT NULL ,
`colB` INT NOT NULL DEFAULT '0' ,
PRIMARY KEY (`id`)) ENGINE = InnoDB;
CREATE TRIGGER tr_bi_test
BEFORE INSERT
ON test
FOR EACH ROW
SET NEW.id = NULL, NEW.colB = 0;
INSERT INTO test (colA) VALUES (11);
INSERT INTO test (colA, colB) VALUES (22, 222);
INSERT INTO test (id, colA) VALUES (3333, 33);
INSERT INTO test (id, colA, colB) VALUES (4444,44,444);
SELECT * FROM test;
id
colA
colB
1
11
0
2
22
0
3
33
0
4
44
0
db<>fiddle here
You cannot use DEFAULT keyword for assigning the value to NEW.colB - in MariaDB it is treated as NULL while using in a trigger. So you need to hardcode this default value, or you may query it from INFORMATION_SCHEMA.COLUMNS.
I'm importing comment data in mysql from one database to another where some data may already exist. Relevant here, our comments table has a column display_text of type text. Inside this column we have mentions to other users stored in the format "#{{"user":id}}#" where id is a number representing the user's id in another table. It is possible for a single comment to have multiple mentions needing updated ids as well.
Since these users may already exist in the new database, part of our import process populates an id_translations table with old_id and new_id columns mapping the user's id in the source database to their id in the new database. Is it possible to write a mysql UPDATE statement to find the format of our mentions, and replace only the id with the new id?
I already have a method for selecting only the comments that will need ids updated, so statements can be written as though they would update the entire table.
MySQL version 5.6. Here's a sample of what my tables look like.
CREATE TABLE IF NOT EXISTS `comments` (
`id` int(6) unsigned NOT NULL,
`display_text` text NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE IF NOT EXISTS `id_translations` (
`old_id` int(6) unsigned NOT NULL,
`new_id` int(6) unsigned NOT NULL,
PRIMARY KEY (`old_id`, `new_id`)
);
INSERT INTO `comments` (`id`, `display_text`) VALUES
(1, 'Hey #{{"user":12}}#, look at this'),
(2, 'Thats pretty cool'),
(3, '#{{"user":41}}# could you take a peek at this?');
INSERT INTO `id_translations` (old_id, new_id) VALUES (12, 100), (41, 101);
The goal would be for comments to look like:
1, 'Hey #{{"user":100}}#, look at this'
2, 'Thats pretty cool'
3, '#{{"user":101}}# could you take a peek at this?'
CREATE PROCEDURE replacing ()
BEGIN
REPEAT
UPDATE comments
SET display_text = CONCAT( LEFT(display_text, LOCATE('#{{"user":', display_text) - 1),
CHAR(0),
( SELECT new_id
FROM id_translations
WHERE old_id = 0 + SUBSTRING(display_text FROM LOCATE('#{{"user":', display_text) + 10)),
CHAR(1),
SUBSTRING(display_text FROM LOCATE('}}#', display_text) + 3))
WHERE LOCATE('#{{"user":', display_text);
UNTIL !ROW_COUNT() END REPEAT;
UPDATE comments
SET display_text = REPLACE(REPLACE(display_text, CHAR(0), '#{{"user":'), CHAR(1), '}}#');
END
DEMO fiddle
I need to insert a new row into the database and then only if a previous value in the row has changed.
--
-- Table structure
--
CREATE TABLE IF NOT EXISTS `macip` (
`intId` int(11) NOT NULL AUTO_INCREMENT,
`strPort` varchar(255) NOT NULL,
`strIp` varchar(255) DEFAULT NULL,
`strVlan` varchar(255) DEFAULT NULL,
`strMac` varchar(255) DEFAULT NULL,
PRIMARY KEY (`intId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;
--
-- Test data
--
INSERT INTO `macip` (`intId`, `strPort`, `strIp`, `strVlan`, `strMac`) VALUES
(1, 'Gig1/0/1', '192.168.100.8', '10', 'AA-BB'),
(2, 'Gig1/0/1', '192.168.100.8', '10', 'CC-DD'),
(6, 'Gig1/0/1', '192.168.100.8', '20', 'AA-BB');
I have got a unchanging column strPort
Next want do INSERT like this:
First insert to database ('Gig1/0/1', '192.168.100.8', '10', 'AA-BB') = > do INSERT
Next insert to database ('Gig1/0/1', '192.168.100.8', '10', 'AA-BB') => do not insert, exists
Next insert to database ('Gig1/0/1', '192.168.100.8', '10', 'XX-EE') => do insert, changed MAC (or IP or VLAN or all this two/three values)
Next insert to database ('Gig1/0/1', '192.168.100.8', '10', 'XX-EE') => do not insert, exists
Next insert to database ('Gig1/0/1', '192.168.100.8', '10', 'AA-BB') => do insert, changed MAC (or IP or VLAN or all this two/three values), but there is a problem in my SQL query, because this value exists in table, I need to compare with previous row with strPort
This query doesn't work:
INSERT INTO macip (strPort, strIp, strVlan, strMac)
SELECT * FROM (SELECT 'Gig1/0/1', '192.168.100.8', '10', 'AA-BB') AS tmp
WHERE NOT EXISTS (
SELECT strIp, strVlan, strMac FROM macip WHERE
strIp = '192.168.100.8' AND strVlan = '10' AND strMac = 'AA-BB'
ORDER BY macip.`strPort` DESC LIMIT 1
) LIMIT 1;
I can't do a unique keys to columns, because SQL query return Exception.
How about just creating a unique constraint/index to prevent this problem?
create unique index idx_macip_strip_strvlan_strMac on macip(strIp, strVlan, strMac);
Then you can do the insert as:
INSERT INTO macip (strPort, strIp, strVlan, strMac)
SELECT 'Gig1/0/1', '192.168.100.8', '10', 'AA-BB'
ON DUPLICATE KEY SET strPort = VALUES(strPort);
The ON DUPLICATE KEY portion just prevents the INSERT from returning an error when there is a duplicate.
I am using FULLTEXT index on a column and when I use MATCH....AGAINST in boolean mode for any search term which is less than 4 characters in length then it doesn't return any records. And when I use LIKE then it does return records.
What could be the problem? Is it because of some MySQL limitation like it doesn't index words that are less than 4 chars in length or some limitation specific to FULLTEXT index?
Thanks
Check your ft_min_word_len system variable. That defines the minimum length of words to be indexed.
Update: Ok I did some tests with ft_min_word_len=3
First a test table
CREATE TABLE `test`.`table1` (
`id` INT NOT NULL AUTO_INCREMENT ,
`name` TEXT NULL ,
PRIMARY KEY (`id`) ,
FULLTEXT INDEX `Name` (`name` ASC) )
ENGINE = MyISAM;
Next some test data:
INSERT INTO `test`.`table1` (`id`, `name`) VALUES ('1', 'This has led in it');
INSERT INTO `test`.`table1` (`id`, `name`) VALUES ('2', 'Led is nice');
INSERT INTO `test`.`table1` (`id`, `name`) VALUES ('3', 'Leds are nicer');
INSERT INTO `test`.`table1` (`id`, `name`) VALUES ('4', 'Nothin here');
INSERT INTO `test`.`table1` (`id`, `name`) VALUES ('5', 'some word which does not exists: abcleddef');
Running this:
SELECT * FROM `test`.`table1` t1 WHERE match(`t1`.`name`) against ('led' in boolean mode)
Returns this:
1 This has led in it
2 Led is nice
Running this:
SELECT * FROM `test`.`table1` t1 WHERE match(`t1`.`name`) against ('led*' in boolean mode)
Returns this:
1 This has led in it
2 Led is nice
3 Leds are nicer
So FT search works as expected. Any chance the word you are trying to find is actually something like leds and not the single word led?