mysql choose between unique key and primary key for user id - mysql

Im creating a user database ... i want to separate user - cellphone number from 'user' table and create another table for it (user_cellphone (table))
but i have a problem to select best index !
in user_cellphone table, we get user_id and cellphone number ... but all SELECT queries are more based on 'user_id' so i want to know if it's better to choose 'user_id' column as primary key or not !!!
(Also each user have only one cellphone number !)
which option of these 2 options are better ?
CREATE TABLE `user_cellphone_num` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`cellphone_country_code` SMALLINT UNSIGNED NOT NULL,
`cellphone_num` BIGINT UNSIGNED NOT NULL,
`user_id` INT UNSIGNED NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `cellphone` (`cellphone_country_code`, `cellphone_num`),
UNIQUE INDEX `user_id` (`user_id`)
)
CREATE TABLE `user_cellphone_num` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`cellphone_country_code` SMALLINT UNSIGNED NOT NULL,
`cellphone_num` BIGINT UNSIGNED NOT NULL,
`user_id` INT UNSIGNED NOT NULL,
PRIMARY KEY (`user_id`),
UNIQUE INDEX `id` (`id`),
UNIQUE INDEX `cellphone` (`cellphone_country_code`, `cellphone_num`)
)
choosing 'user_id' as primary key or just set 'user_id' as a unique key ?! is there any different here in performance ? (Im talking about when i have millions of rows)
in future im going to use some queries like this:
select u.*,cell.* FROM user AS u LEFT JOIN user_cellphone AS cell ON cell.user_id = u.id
so which one of these options give me better performance for some queries like this ?

May I offer some hard-won data design advice?
Do not use telephone numbers as any kind of unique or primary key.
Why not?
Sometimes multiple people use a single number.
Sometimes people make up fake numbers.
People punctuate numbers based on context. To my neighbors, my number is (978)555-4321. To a customer in the Netherlands it is +1.978.555.4321. Can you write a program to regularize those numbers? Of course. Can you write a correct program to do that? No. Why bother trying. Just take whatever people give you.
(Unless you work for a mobile phone provider, in which case ask your database administrator.
Read this carefully. https://github.com/google/libphonenumber/blob/master/FALSEHOODS.md

InnoDB tables are stored as a clustered index, also called an index-organized table. If the table has a PRIMARY KEY, then that is used as the key for the clustered index. The other UNIQUE KEY is a secondary index.
Queries where you look up rows by the clustered index are a little bit more efficient than using a secondary index, even if that secondary index is a unique index. So if you want to optimize for the most common query which you say is by user_id, then it would be a good idea to make that your clustered index.
In your case, it would be kind of strange to separate the cellphones into a separate table, but then make user_id alone be the PRIMARY KEY. That means that only one row per user_id can exist in this table. I would have expected that you separated cellphones into a separate table to allow each user to have multiple phone numbers.
You can get the same benefit of the clustered index if you just make sure user_id is the first column in a compound key:
CREATE TABLE `user_cellphone_num` (
`user_id` INT UNSIGNED NOT NULL,
`num` TINYINT UNSIGNED NOT NULL,
`cellphone_country_code` SMALLINT UNSIGNED NOT NULL,
`cellphone_num` BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (`user_id`, `num`)
)
So a query like SELECT ... FROM user_cellphone_num WHERE user_id = ? will match one or more rows, but it will be an efficient lookup because it's searching the first column of the clustered index.
Reference: https://dev.mysql.com/doc/refman/8.0/en/innodb-index-types.html

Related

Clustered index for foreign key column

Consider the following example of a messaging system:
create table chat_group
(
id int auto_increment primary key,
title varchar(100) not null,
date_created date not null
)
create table chat_message
(
id int auto_increment,
user_id int not null,
chat_group_id int not null,
message text charset utf8mb4 not null,
date_created datetime not null
)
Now I see that the most common request for the chat_message table is SELECT * FROM chat_message where chat_group_id = ?. So my idea is to put a clustered index on chat_group_id column so the chat messages will be organized by groups on the disk.
But in MySQL it requires PRIMARY KEY(which actually is a clustered index) to be unique, so what is the solution here? What clustered index do I make for the given situation.
Yes, "you can have your cake and eat it, too":
PRIMARY KEY(chat_group_id, id),
INDEX(id)
The PK provides "clustering" by the group; this is likely to speed up your main queries. Including id makes it UNIQUE, which is a requirement (in MySQL) for the PK.
The secondary INDEX(id) is the minimum needed to keep AUTO_INCREMENT happy -- namely having some index starting with the id.

MySQL indexes with multiple field

Would the following table considered to have duplicate indexes on TypeId, or is it perfectly fine to do it this way? Some of my queries perform faster using the KEY "covered', while others perform better just using the 'TypeId'. Any knowledge on the matter would be appreciated.
CREATE TABLE module(
Id INT unsigned not null auto_increment,
name VARCHAR(30) not null,
TypeId INT unsigned not null,
cSign tinyint not null,
orderId int not null,
PRIMARY KEY(Id),
KEY 'covered' ('cSign','TypeId','orderId'),
KEY 'TypeId' ('TypeId')
);
No, the indexes are different.
MySQL documentation does a good job of explaining indexes with composite keys. What is important is that the left-most columns in the indexes are different.
As a note: you should fix your code by removing the single quotes. They are incorrect in this context.

MySQL performance using AUTO_INCREMENT on a PRIMARY KEY

I ran a comparison INSERTing rows into an empty table using MySQL 5.6.
Each table contained a column (ascending) that was incremented serially by AUTO_INCREMENT, and a pair of columns (random_1, random_2) that receive random, unique numbers.
In the first test, ascending was PRIMARY KEY and (random_1, random_2) were KEY. In the second test, (random_1, random_2) were PRIMARY KEY and ascending was KEY.
CREATE TABLE clh_test_pk_auto_increment (
ascending_pk BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, -- PK
random_ak_1 BIGINT UNSIGNED NOT NULL, -- AK1
random_ak_2 BIGINT UNSIGNED, -- AK2
payload VARCHAR(40),
PRIMARY KEY ( ascending_pk ),
KEY ( random_ak_1, random_ak_2 )
) ENGINE=MYISAM
AUTO_INCREMENT=1
;
CREATE TABLE clh_test_auto_increment (
ascending_ak BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, -- AK
random_pk_1 BIGINT UNSIGNED NOT NULL, -- PK1
random_pk_2 BIGINT UNSIGNED, -- PK2
payload VARCHAR(40),
PRIMARY KEY ( random_pk_1, random_pk_2 ),
KEY ( ascending_ak )
) ENGINE=MYISAM
AUTO_INCREMENT=1
;
Consistently, the second test (where the auto-increment column is not the PRIMARY KEY) runs slightly faster -- 5-6%. Can anyone speculate as to why?
Primary keys are often used as the sequence in which the data is actually stored. If the primary key is incremented, the data is simply appended. If the primary key is random, that would mean that existing data must be moved about to get the new row into the proper sequence. A basic (non-primary-key) index is typically much lighter in content and can be moved around faster with less overhead.
I know this to be true for other DBMS's; I would venture to guess that MySQL works similarly in this respect.
UPDATE
As stated by #BillKarwin in comments below, this theory would not hold true for MyISAM tables. As a followup-theory, I'd refer to #KevinPostlewaite's answer below (which he's since deleted), that the issue is the lack of AUTO_INCREMENT on a PRIMARY KEY - which must be unique. With AUTO_INCREMENT it's easier to determine that the values are unique since they are guaranteed to be incremental. With random values, it may take some time to actually walk the index to make this determination.

Having trouble with foreign key

I am trying to have categories in my budget2000 table be the foreign key to category in mainBudget. Category is not a unique number so it cannot be a primary key. When I run the code I get the famous error 1005. When I make category part of the primary key in mainBudget with id the code runs, however this will create problems later on. What can I do to make categories a foreign key. I am using mysql 5.5.
Here is my code
create table mainBudget(
id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
year Year NOT NULL,
amount double(10,2) NOT NULL,
category SMALLINT UNSIGNED NOT NULL,
primary key(id)
)ENGINE=INNODB;
create table budget2000(
id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
categories SMALLINT UNSIGNED NOT NULL,
INDEX categoryNumber (categories),
subCategory SMALLINT NOT NULL,
amount FLOAT(10,2) NOT NULL,
date DATE NOT NULL,
description VARCHAR(300) NOT NULL,
primary key(id),
FOREIGN KEY (categories) REFERENCES mainBudget(category)
)ENGINE=INNODB;
category is not indexed in mainBudget. The column in the referenced table has to be indexed (or the left prefix of an index).
Incidentally, are you sure it isn't better to have an additional table category and have mainBudget.category and budget200.categories both foreign keys to this table? Your current setup looks a little odd, particularly with the referenced column in mainBudget not being unique.
Having FKs referencing non-unique columns is not standard SQL. Even when MySQL InnoDB allows this, it does not mean that it is a good idea.
Make some ER-Diagrams and normalize your tables. (Use 3.NF if nothing else forces you not to do.) Having a separate table for Category seems to be the way to go. On the other hand the naming of your exiting tables makes me thinking these should be only one table or their naming is bad.
And when this 2000 has something to do with a year or what then forget about it. You can select this easy in your Queries. Just put everything in one table no matter what year it is.
Your question/problem seems to be design-related to me.

Creating tables for user authentication - room for improvements?

At the moment I'm developing a web application. For that, I need to create a database for user authentication.
I have something like the following in mind:
create table users
(
id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
username VARCHAR(100) NOT NULL,
password VARCHAR(60) NOT NULL,
PRIMARY_KEY(id),
UNIQUE(username)
);
create table roles
(
id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
role VARCHAR(100) NOT NULL,
PRIMARY_KEY(id),
UNIQUE(role)
);
create table user_roles
(
user_id SMALLINT UNSIGNED NOT NULL,
role_id SMALLINT UNSIGNED NOT NULL,
PRIMARY_KEY(user_id)
UNIQUE (user_id, role_id),
);
Passwords are fixed size but I use varchar because I've read somewhere that in a table where you have both char and varchar columns, the char columns get converted to varchar.
Also, would it be beneficial to use FOREIGN KEY CONSTRAINTS?
Don't use SMALLINTs for IDs. Seriously. Why would anybody do that!? Use a larger integer type.
The primary key on user_roles isn't going to work. Make (user_id, role_id) the primary key and put separate (non-unique) indexes on the fields you will be querying on.
You could add foreign keys that link user_roles to the other tables. If you use ON DELETE CASCADE, it'll even delete the stale links if you ever delete a user or a role.