How to create a public id? - mysql

I have a database. As you can see the primary key is an auto_increment and is also unique. I read that publically sharing a row's primary key of a table to the public is unsafe. I want to assign each row in customers a unique ID that I can publically share. How can I do this without having to specify each time what the public_id is in the INSERT statement? The database should automatically find a unique ID to assign to that row just like it does for id because of auto_increment.
CREATE TABLE customers (
id int primary key auto_increment,
name varchar(32) not null,
-- public_id (an ID I can give to the public to uniquely identify this row
);
INSERT INTO customers (name) VALUES ('Bob'), ('Sarah'), ('Bob');

Well, here's one way:
CREATE TABLE customers (
id int primary key auto_increment,
name varchar(32) not null,
public_id char(36) not null unique default uuid()
);
Note that the manual says:
Warning
Although UUID() values are intended to be unique, they are not necessarily unguessable or unpredictable. If unpredictability is required, UUID values should be generated some other way.
So this is simple, and maybe will float your goat, but we can also try better:
CREATE TABLE customers (
id int primary key auto_increment,
name varchar(32) not null,
public_id char(24) not null unique default to_base64(random_bytes(18))
);
This will be a nice and dense identifier, but it will have characters + and / which don't play well with URLs. You can encode them, of course, but if you want to go one lazier, you can also do this:
CREATE TABLE customers (
id int primary key auto_increment,
name varchar(32) not null,
public_id char(32) not null unique default hex(random_bytes(16))
);
Mind you, the identifier will get quite a bit longer this way.
To get the best of both worlds, we can do this, at the expense of a really long default value:
CREATE TABLE customers (
id int primary key auto_increment,
name varchar(32) not null,
public_id char(24) not null unique default replace(replace(to_base64(random_bytes(18)), '+', '_'), '/', '-')
);
Also note that messing around with MD5()/SHA()/SHA1()/SHA2() is no better than just generating a random hex string with a given length.

Related

Two examples of a mySQL database structure, which one is more efficient and preserves data integrity?

I'm creating a mySQL database for a small blog. This blog will have articles of different "types", like "public interest", "DIY", etc.
My question is about how to organize the database structure: should I create a table for the articles, a table for the types, and a third table that connect the two of them? Or should I just create the first two tables and add a field in the articles table that points out to the id number of the types table?
Option 1:
CREATE TABLE articles(
id int unsigned not null auto_increment primary key,
title varchar(300) NULL,
body TEXT NULL
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE articleType(
id int unsigned not null auto_increment primary key,
name char(200) NULL
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `articleType` (`name`) VALUES
('public interest'),
('DIY')
CREATE TABLE articlesArticleType (
ID int unsigned not null auto_increment primary key,
typeID int not null,
articleID int not null
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
Option 2:
CREATE TABLE articles(
id int unsigned not null auto_increment primary key,
title varchar(300) NULL,
body TEXT NULL,
articleType int NOT NULL DEFAULT 1
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE articleType(
id int unsigned not null auto_increment primary key,
name char(200) NULL
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `articleType` (`nombre`) VALUES
('public interest'),
('DIY')
In the second case I just need two tables. Which way is more efficient and preserves data integrity?
First and foremost it is important to decide on the cardinality of relationship between the 2 tables - Articles and Types as it will influence the choice of the tables structure. Broadly there are 3 cardinalities possible:
One to One
One to Many
Many to Many
Option 1 will satisfy One to Many and Many to Many cardinalities while Option 2 will satisfy One to One cardinality.

modeling issue with field with 4 values (1 or more)

lets say I have an account object in my application, which currently represented as:
CREATE TABLE Account (
accountId int NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
PRIMARY KEY (accountId)
);
Now, Account object need to also have Solution field...and Status have 4 different possible values:
Solution1, Solution2, Solution3, Solution4
What would be the right way to represent it in the database?
Account can have few statuses, and status can have few accounts...
So at first I thought create in the db table of Solutions and than have another table to hold the relationship, but its seems too complicated for a field that have only 4 possible values...
Create a junction table to represent the relationships between accounts and solutions:
CREATE TABLE account_solution (
accountId int NOT NULL,
solutionId int NOT NULL
PRIMARY KEY (accountId, solutionId)
)
For your solution table, since there are only 4 values, you might be able to take advantage of MySQL's enum type, e.g.
CREATE TABLE solution
solutionId int NOT NULL PRIMARY KEY,
status ENUM('Solution1', 'Solution2', 'Solution3', 'Solution4')
);
You can use set Mysql SET type
CREATE TABLE Account (
accountId int NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
status set('Solution1','Solution2','Solution3','Solution4') NOT NULL,
PRIMARY KEY (accountId)
);
And if you want to select a specific status
SELECT *
FROM `Account`
WHERE FIND_IN_SET( 'Solution2', `status` ) >0

MySQL - add data into 2 tables and 1 has foreign key

I'm a totally MySQL newcomer. Sr if my question is quite obvious. I got 2 tables
CREATE TABLE tbl_addresses(
PK_ADDRESS_ID int NOT NULL AUTO_INCREMENT,
house_number int NOT NULL,
street varchar(35),
district varchar(35),
city varchar(35),
postcode varchar(8),
PRIMARY KEY (PK_ADDRESS_ID)
);
CREATE TABLE tbl_people(
PK_PERSON_ID int NOT NULL AUTO_INCREMENT,
title varchar(6) NOT NULL, # Master / Mister therefor 6 is max
forename varchar(35) NOT NULL,
surname varchar(35) NOT NULL,
date_of_birth DATE NOT NULL,
contact_number varchar(12) NOT NULL,
FK_ADDRESS_ID int NOT NULL,
PRIMARY KEY (PK_PERSON_ID),
FOREIGN KEY (FK_ADDRESS_ID) REFERENCES tbl_addresses (PK_ADDRESS_ID)
);
and I'm trying to import data into these tables from Java using below syntaxes
INSERT INTO tbl_addresses (house_number,street,district,city,postcode) VALUES ('1','abc','','abc','abc');
INSERT INTO tbl_people (title,forename,surname,date_of_birth,contact_number) VALUES ('Mr','Tri ','Nguyen','1991-1-1','0123456789');
I got an error Field 'FK_ADDRESS_ID'doesn't have a default value and data actually goes into tbl_addresses but not tbl_people. Am I missing anything? Thanks in advance!
This error is being caused by that you labelled the FK_ADDRESS_ID field in the tbl_people table as NOT NULL, yet you are trying to do an INSERT without specifying a value for this column.
So something like this would work without error:
INSERT INTO tbl_people (title, forename, surname, date_of_birth,
contact_number, FK_ADDRESS_ID)
VALUES ('Mr', 'Tri', 'Nguyen', '1991-1-1', '0123456789', 1);
You could also specify a default value for FK_ADDRESS_ID (the error message you got alluded to this). Here is how you could adda default value:
ALTER TABLE tbl_people MODIFY COLUMN FK_ADDRESS_ID int NOT NULL DEFAULT 1
But because FK_ADDRESS_ID is a key into another table, the value should really be based on the primary key in tbl_addresses.
The fact that you are using a foreign key isn't the reason that you are getting this error. Let's take a look at your column definition.
FK_ADDRESS_ID int NOT NULL,
This is not null but does not a default. Now a look at your insert statement
INSERT INTO tbl_people (title,forename,surname,date_of_birth,contact_number)
FK_ADDRESS_ID isn't in your column list but it cannot be null and doesn't have a default so what can mysql do? Produce an error of course.
The best bet is to define that column as nullable.
Let's revisit the foreign key constraint.
FOREIGN KEY (FK_ADDRESS_ID) REFERENCES tbl_addresses (PK_ADDRESS_ID)
What this really says is that if you asign a value to FK_ADDRESS_ID that value should be present in PK_ADDRESS_ID column in tbl_address
as a side note, it's customary to use lower case for table/column names.

Table localization - One column for a table

I have got only one column for a table when i create two localized tables. Code as bellow.
-- Month
CREATE TABLE `month` (
`id` INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
);
-- Month Localized
CREATE TABLE `month_loc` (
`month_id' INT NOT NULL,
`name` VARCHAR(200) NOT NULL,
`description` VARCHAR(500) NOT NULL,
`lang_id` INT NOT NULL
);
month_loc.month_id is the foreign key.
month table holds only the primary key. Other all fields should be localized. Is this table structure correct ?
Thanks.
If correct implies a certain degree of normalization, and the content of your columns name and description vary per month_id, lang_id (which would be the combined primary key of month_loc), then yes, your design has reached the 3rd grade of normlization.

What is the best way to have a varchar primary key column with an auto-increment?

I would like to create some tables in MySQL. One table would be for users, one for topics, one for comments, and so on.
I need each table to have its own ID column in the following format:
USERS table: ID column
Values:
USR00001
USR00002
USR00003
..
..
USR99999
where as topics table would have IDs like:
TPC00001
TPC00002
TPC00003
similarly, the comments table would have the following IDs:
CMT00001
CMT00002
I tried to use UNIQUE key but did not work: (inspired by this answer)
CREATE TABLE `users` (
`ID` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT ,
`firstname` VARCHAR(100) NOT NULL ,
`lastname` VARCHAR(100) NOT NULL ,
`email` VARCHAR(100) NOT NULL ,
PRIMARY KEY (`ID`)
UNIQUE KEY ( 'USR' + `ID`)
);
Can it be done using triggers (Before Insert) maybe?
Please note that I don't want to handle the insertion of the primary keys on the application level. I would prefer the database engine to handle all the work for that.