SQL auto_increment non-int-only values - mysql

I was looking into auto_increment and I would like to automatically generate unique id primary key. The problem is I only know how to do that with numerical ids. As far as I know, primary keys have to be unique. So I cant use numerical auto_increment for multiple tables because the id wont be unique. Here's how I do it now:
CREATE TABLE students (
id int(3) NOT NULL AUTO_INCREMENT PRIMARY KEY,
name char(20),
lastname char(20)
);
To the best of my knowledge this will generate ids like: 1, 2, 3, 4 unless I name a different starting number. Would it be possible to autoi_increment something like "stud_1" or "stud_01" and then keep going increasing the number? I haven't come across such an example so far.

I wrote a lot in the comments above, but let me apply it to a pratical application in an answer. We'll begin with your table and then add two more:
Tables:
CREATE TABLE students (
id int(3) NOT NULL AUTO_INCREMENT PRIMARY KEY,
name char(20),
lastname char(20)
) ENGINE = INNODB;
CREATE TABLE subjects (
id int(3) NOT NULL AUTO_INCREMENT PRIMARY KEY,
name char(20)
) ENGINE = INNODB;
CREATE TABLE student_subject (
student_id int(3) NOT NULL,
subject_id int(3) NOT NULL,
PRIMARY KEY(student_id, subject_id)
FOREIGN KEY(student_id) references students(id),
FOREIGN KEY(subject_id) references subjects(id)
) ENGINE = INNODB;
With a setup of tables like this, it is possible to have multiple numerical auto_increment primary key columns within the same database. In this case, there are two students.id and subjects.id. The third table student_subject shows how to link these together where both student_id and subject_id are primary keys for that table but each is a foreign key of their entity table as well.
With a setup like this querying becomes simple:
Sample Queries:
/* Get All students */
SELECT
students.id,
students.name,
students.lastname
FROM students;
/* Get All Subjects */
SELECT
subjects.id,
subjects.name
FROM subjects;
/* Get all subjects for a particular student */
SELECT
subjects.name
FROM subjects
INNER JOIN student_subject ON subjects.id = student_subject.subject_id
INNER JOIN students ON student_subject.student_id = students.id
WHERE students.id = 1;
EDIT:
Below is some sample data that demonstrates the three table many-to-many concept that the above structure illustrates:
/* Students Table */
+----+----------------+----------------+
| id | name | lastname |
+----+----------------+----------------+
| 1 | John | Doe |
|----|----------------|----------------|
| 2 | Joe | Bob |
+----+----------------+----------------+
/* Subjects Table */
+----+----------------+
| id | name |
+----+----------------+
| 1 | Calculus |
|----|----------------|
| 2 | Physics |
|----|----------------|
| 3 | Geography |
+----+----------------+
/* Student Subject Table */
+------------+------------+
| student_id | subject_id |
+------------+------------+
| 1 | 1 |
|------------|------------|
| 1 | 2 |
|------------|------------|
| 2 | 2 |
|------------|------------|
| 2 | 3 |
+------------+------------+
With this table structure, each student can have many subjects and each subject can belong to many students. For example, John Doe (student 1) is enrolled in both Calculus (subject 1) and Physics (subject 2). Joe Bob (student 2) is enrolled in Geography (subject 3) and also in Physics (subject 2) with John Doe.
In this example, each student is enrolled in two subjects and both students are enrolled in one subject. With the example you linked to from w3schools.com, this many-to-many relationship would not be possible.

Primary key values only need to be unique within a table, so there's no reason you can't use simple auto-increment numeric ids for every table.
As far as the question - I don't believe there's a direct way to "auto-increment" a string value in the way you describe. You could set something up with triggers and a sequence (or regular auto-increment column) if you really wanted to.

Related

MySQL add dynamic amount to an table

i am very new to MySQL and making my first steps with it. I have right now 2 tables that look like this:
Event:
+-------+-------------+
| Field | Type |
+-------+-------------+
| id | int(11) |
| name | varchar(30) |
| date | datetime |
+-------+-------------+
User:
+----------+-------------+
| Field | Type |
+----------+-------------+
| name | varchar(30) |
| amount | int |
+----------+-------------+
Now i would like to be able to add an dynamic amount of Users to an Event but i am not sure how i should create an dynamic table for that or how otherwise i should implement such an behaviour. What would be the best practice?
Help would be appreciated.
That is a classic many-to-many relation. You handle it by adding a new table
event_users table
-----------------
event_id
user_id
For an event (id=1) having two users (id=3,4) it looks like this
event_id | user_id
1 | 3
1 | 4
To get all users of an event you would do this
select u.name
from users u
join event_users eu on eu.user_id = u.id
join events e on eu.event_id = e.id
where e.name = 'my event'
You need a third table, a junction table:
create table UserEvents (
UserEventId int auto_increment primary key,
UserId int,
EventId int,
constraint fk_userevents_userid foreign key (UserId) references Users(id),
constraint fk_userevents_eventid foreign key (EventId) references Events(id)
);
Notes:
I name tables in the plural.
The id for the table is the singular followed by "id" (so I prefer Users(UserId) and Events(EventId).
This has declared foreign key relationships.
If duplicates are not allowed, then you also want a unique constraint on (UserId, EventId).
For this you should use a third table in which you should maintain event and related user id.
user_event
id user_id event_id created_date
user_id and event_id are foreign keys for this table and will point primary keys of user and event table respectively

Is it necessary to have a primary key in the foriegn table?

Table one
ID | product_code | product name | company_id
1 | 12345 | beer cake |343434defee
2 | 12346 | vodka cake |343434deereee
Table two
Product_code |Quantity | price | weight
12345 | 34 |345 |0.5
12345 | 343 |600 |1.0
12345 | 4 |845 |1.5
12346 | 341 |345 |0.5
CREATE TABLE `one`(
ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
product_code VARCHAR(32) NOT NULL ,
name VARCHAR(30) NOT NULL ,
company_id VARCHAR(30) NOT NULL)
CREATE TABLE two(
product_code VARCHAR(32) ,
weight VARCHAR(20) NOT NULL ,
price INT(4) NOT NULL ,
Quantity INT(4) NOT NULL ,
FOREIGN KEY (product_code) REFERENCES one(product_code))
This is what my table looks like, each type of cake has to be displayed on the product landing page.The relationship between two tables is given by the column product_code.
Is it necessary to have a primary key in the foreign table?
Please, Show me a proper schema creation for these ?
Your data structure is set up wrong. This is your first table:
ID | product_code | product name | company_id
1 | 12345 | beer cake |343434defee
2 | 12346 | vodka cake |343434deereee
This is fine. ID is -- presumably -- a unique id for each row. It should be declared as a primary key. product_code should be declared unique.
The second table should contain id, not product_code. Product_code is an attribute on each row of the first table. You have the id to refer to the row.
It is possible, if your FK points to a column that has a unique constraint - check here for details.
Before doing this, you should clarify your design:
1) Provide meaningful names for your tables. E.g. Table 1 - Product, Table 2 - ProductTransaction
2) Create a primary key for table ProductTransaction: a ProductTransactionId column should be fine for most usage scenarios
3) Create a foreign key between ProductTransaction and Product tables. Although, you can use product_code column, I would recommend to normalize your definitions:
a) remove product_code from ProductTransaction
b) add ProductId and define a foreign key that points to Product.ID
This way, you will store less data (just some integers instead of strings) and joins between the tables will be faster.

Create a one to many relationship in MySQL

I have two tables, one with arbitrary rows of books and one with specific rows of subjects and ids.
mysql> describe books;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| title | varchar(250) | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
mysql> describe subjects;
+----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| subjects | varchar(250) | YES | | NULL | |
+----------+--------------+------+-----+---------+-------+
I want to associate one book to many subject ids, is there any array type I can use so that one column in my books table contains an array of different subject ids?
Can I then do a query on the books table to see which books are related to one subject?
This is a many-to-many relationship, not a one-to-many relationship. (A book can have several subjects, and a subject can have several books.)
The normative pattern is to implement a third table to store the "relationship" between books and subjects.
As an example of a minimal relationship table:
CREATE TABLE books_subjects
( subjects_id INT(11) NOT NULL COMMENT 'PK, FK ref subjects(id)'
, books_id INT(11) NOT NULL COMMENT 'PK, FK ref books(id)'
, PRIMARY KEY (subjects_id, books_id)
);
For InnoDB, we can define the foreign key constraints:
ALTER TABLE books_subjects ADD CONSTRAINT FK_books_subjects_books
FOREIGN KEY (books_id) REFERENCES books(id);
ALTER TABLE books_subjects ADD CONSTRAINT FK_books_subjects_subjects
FOREIGN KEY (subjects_id) REFERENCES subjects(id);
This effectively resolves the many-to-many relationship into two one-to-many relationships.
A books can have multiple books_subjects, but a books_subjects is related to exactly one books.
A subjects can have multiple books_subjects, but a books_subjects is related to exactly one subjects.
Queries to retrieve books related to a particular subjects would involve a JOIN operation to the books_subjects table.
For example, to find all books of particular subjects:
SELECT b.id
, b.title
FROM books b
JOIN books_subjects j
ON j.books_id = b.id
JOIN subjects s
ON s.id = j.subjects_id
WHERE s.subjects = 'Psychology'
ORDER BY b.title
To find books that don't have any subjects, e.g.
SELECT b.id
, b.title
FROM books b
LEFT
JOIN books_subjects j
ON j.books_id = b.id
WHERE j.books_id IS NULL
ORDER BY b.title
NOTE: I typically use singular names for tables. So, there would be a lot fewer "s" characters in my column names and foreign key names. I may have omitted an "s" or two in my typing.

Is relating (image) table with (user), (product), (company) and any independent tables advised?

I'm thinking about having one image table to store images of any other independent table like user, product and so... (of course any single instances of independent tables (like John Smith as a user, and laptop as a product) may have 0, 1, or multiple images.
The image table has id, title and filename.
And I'm thinking an imagetable table to relate images to their proper image owners like user with these fields: image_id, table_id and table.
Some entries might look like this:
image_id | table_id | table
-----------------------------
1 | 1 | user
2 | 1 | user
3 | 2 | user
4 | 2 | user
5 | 1 | product
6 | 1 | product
7 | 1 | product
8 | 2 | product
9 | 3 | product
10 | 3 | product
11 | 4 | product
Now the question is:
Is this database design advised? What's the best approach to this request?
Of course the other way is to have user_image, product_image and company_image tables instead of a single image_table table.
No, because then you lose the advantage of foreign keys.
Use junction tables:
create table product (
product_id bigserial primary key,
name citext not null unique
);
create table user (
user_id bigserial primary key,
name citext not null unique
);
-- personally, I would store the file in the db and use incremental backups
-- pedantically, I prefer "picture" over "image" as "image" has 2 meanings in computers
create table picture (
picture_id bigserial primary key,
filename citext not null,
...
);
create table product_picture (
product_id bigint references product(product_id),
picture_id bigint references picture(picture_id),
primary key (product_id, picture_id)
);
create table user_picture (
user_id bigint references user(user_id),
picture_id bigint references picture(picture_id),
primary key (user_id, picture_id)
);

StringInString Query?

I have this database where I have stored some tags in it.
I stored the tags like this:
"humor,funny,animal"
Now I need a mysql query that selects this line when I search for "humor", "funny" or "animal". What I have until now:
SELECT id FROM database WHERE tags REGEXP 'humor' LIMIT 1
Unfortunately, it does not work. Could someone of you please help me out?
Edit: Thanks for all the responses! I will now need to study this first! But problem solved :)
Short Term
Because the tags are stored as denormalized data, use the FIND_IN_SET function:
SELECT t.id
FROM YOUR_TABLE t
WHERE FIND_IN_SET('humour', t.tags) > 0
Long Term Solution
Setup the tables to properly handle a many-to-many relationship:
TAGS
tag_id (primary key)
tag_description
ITEMS
item_id (primary key)
ITEM_TAGS
item_id (primary key, foreign key to ITEMS.item_id)
tag_id (primary key, foreign key to TAGS.tag_id)
Making the two columns in ITEM_TAGS the primary key means you don't have to worry about duplicates. And yes, this means using the InnoDB engine...
Then, you can use:
SELECT i.item_id
FROM ITEMS i
WHERE EXISTS (SELECT NULL
FROM ITEM_TAGS it
JOIN TAGS t ON t.tag_id = it.tag_id
WHERE t.tag_description = 'humour'
AND it.item_id = i.item_id)
You can use LIKE
SELECT id FROM database WHERE tags LIKE '%humor%' LIMIT 1
Which will search for any entry where 'humor' is a substring. Note this will also return items tagged 'humorous'.
But like others said, having a separate table for tags would be best. To do this you will also need a pivot table.
So for example
-------------- data -------------
| ID | NAME |
| 1 | example |
| 2 | example 2 |
-----------------------------------
-------------- tags -------------
| ID | NAME |
| 1 | humor |
| 2 | cats |
| 3 | wumpus |
-----------------------------------
------------ data_tags ----------
| DATA_ID | TAG_ID |
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 2 | 3 |
-----------------------------------
To expand on Tomalak's comment, this would be best solved using a many-to-many relationship for database to tag relationships. This involves adding two new tables. Something like this (forgive my rusty MySQL)
CREATE TABLE `Tag` (
id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
tag VARCHAR(64) NOT NULL,
PRIMARY KEY (id),
UNIQUE (tag)
) ENGINE=InnoDB;
CREATE TABLE `DatabaseTag` (
database_id INT(11) UNSIGNED NOT NULL, -- just guessing your database.id type here
tag_id INT(11) UNSIGNED NOT NULL,
PRIMARY KEY (database_id, tag_id),
FOREIGN KEY (database_id) REFERENCES `database` (id)
ON DELETE CASCADE,
FOREIGN KEY (tag_id) REFERENCES `Tag` (id)
ON DELETE CASCADE
) ENGINE=InndoDB;
Then, to find all the database records matching tag "humour", your query would look like
SELECT id FROM `database` d
INNER JOIN `DatabaseTag` dt ON d.id = dt.database_id
INNER JOIN `Tag` t ON dt.tag_id = t.id
WHERE t.tag = 'humour'