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'
Related
I have a table tag_thread that associates tags with threads, just like here on Stackoverflow where one thread can have multiple tags and one tag can be used on multiple threads.
Now I would like to give a tag_id as an input to get tags that are often used together with the given tag (so to get relevant tags):
Example table tag_thread:
| tag_id | thread_id |
|:-----------|------------:|
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 1 | 2 |
| 21 | 2 |
| 3 | 2 |
Expected output for the query:
getRelevantTagIdsForThreadId(1): [3,2,21]
getRelevantTagIdsForThreadId(2): [1,3]
getRelevantTagIdsForThreadId(3): [1,2,21]
So the query should search for the given tag_id, then take the associated thread_ids, collect the tag_ids of the thread_id`s received in the step before and order them by how often the tag_id was found.
I already have a working query, however, it is not efficient at all and thus doesn't work properly for larger tables:
select `t2`.`tag_id`
from `tag_thread` as `t1`
inner join `tag_thread` as `t2`
on `t1`.`thread_id` = `t2`.`thread_id`
and t1.tag_id = :tagId
where t2.tag_id <> :tagId2
group by `t2`.`tag_id`
order by count(t2.tag_id) desc
Any idea for an efficient solution? I would be okay with limiting the number of tags that are looked at in the first place, too.
result of SHOW CREATE TABLE tag_thread:
CREATE TABLE `tag_thread` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`tag_id` int(11) NOT NULL,
`thread_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=38496 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
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
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.
I have a requirement that is
I have a table with partner and event details, each partner having number of events.Now i need to get the event details based on partnerid, means
partner1->event1
partner2->event1
.......
partnern->event1
Partner1->event2
partner2->event2
........
partnern->event2
...
.....
partner1->eventn
partner2->eventn
......
Here each partner having their own number of events.Please give me suggetions, I am using mysql
Here is my table sructure
CREATE TABLE `event` (
`eventid` varchar(30) NOT NULL DEFAULT '',
`partnerid` varchar(30) DEFAULT NULL,
PRIMARY KEY (`eventid`),
KEY `partnerid` (`partnerid`),
KEY `locationid` (`locationid`),
CONSTRAINT `event_ibfk_1` FOREIGN KEY (`partnerid`) REFERENCES `partnerprofile` (`userid`),
CONSTRAINT `event_ibfk_2` FOREIGN KEY (`locationid`) REFERENCES `location` (`locatinid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
I am not that clear with your requirement. Anyway giving a try.
After you have mastered the basics of MySQL, take the next step and take on Aggregate Functions. Group BY is good for retrieving information about a group of data. If you only had one product of each type, then GROUP BY would not be all that useful.
GROUP BY only shines when you have many similar things. For example, if you have a number of partners of the same type, and you want to find out some statistical information like the minimum, maximum, or other top-level info, you would use GROUP BY.
Here is the updated solution:
SELECT A.`event_id` AS 'Event(ID)', B.`partner_names` AS 'Partners' FROM `event` A
JOIN
SELECT (SELECT GROUP_CONCAT( P.`partner_name`) AS 'partner_names'
FROM (SELECT `partner_name` FROM information_schema.columns
WHERE table_name='partnerprofile') as P) FROM `partnerprofile` B
GROUP BY A.`partnerid`
Here assume that partner_name is the name field in the partnerprofile table.
And the expected output is:
+---------------+--------------------------------------------+
| Event(ID) | Partners |
+---------------+--------------------------------------------+
| event_001 | Partener1, Partener2, Partener4 |
| event_002 | Partener2, Partener3, Partener9 |
| event_003 | Partener15 |
| event_004 | Partener5, Partener7, Partener9 |
| _________ | _________, _________ |
| _________ | _________ |
| _________ | _________, _________ _________, _________ |
| _________ | _________, _________, |
+---------------+--------------------------------------------+
I think an INNER JOIN should work. something like:
SELECT * FROM event INNER JOIN partner ON event.partnerID = partner.partnerID
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.