Regex vs Join Performance Mysql - mysql

I am at hesitation what type should I use. There will be really a lot of IDs and Groups, normally they learned me to do multiple tables but the second version with regexp seems great and less redundant. I would love to know with of those two version is better.
+----+ +----+-------+
| ID | | ID | Group |
+----+ +----+-------+
| 1 | | 1 | gr1 |
| 1 | | 1 | gr2 |
| 2 | | 2 | gr2 |
| 2 | | 2 | gr3 |
+----+ +----+-------+
SELECT * FROM tbl1 join tbl2 USING(ID) WHERE Group="gr1";
+----+-----------+
| ID | Group |
+----+-----------+
| 1 | gr1,gr2 |
| 2 | gr2,gr3 |
+----+-----------+
SELECT * FROM tbl1 WHERE Group REGEXP '(^|,)gr1($|,)';

As long as you have small tables, you can use both at your discretion.
If you expect the table to grow, you really need to opt for the first choice. The reason behind is that a query with where regexp will not make use of the indexes. And as you know, indexes are the key to fast queries.
If you're using InnoDB, define a foreign key that links the two tables.
You say, you find the first choice having more redundance than the second one. It doesn't seem to me. Thinking about this your sketch might show that there is a misunderstanding. It should rather look like this, i.e. there is only one row per id in table 1, and therefore, there is no redundance.
Tbl1 Tbl2
+----+----------+ +----+-------+
| ID |Other cols| | ID | Group |
+----+----------+ +----+-------+
| 1 | | | 1 | gr1 |
| 2 | | | 1 | gr2 |
+----+----------+ | 2 | gr2 |
| 2 | gr3 |
+----+-------+

Related

How to write a join query for many to many relationship? for dictionary project

I have working on database for dictionary project. I have to store a word and meaning with -- many to many -- relationship.
Below I have mentioned the sample with my table structure. I hope the table structure is right, but I don't know how to select all meanings for single word while user searching.
And also I have to write a query to select all word linked to a single meaning.
And also I have to write a query to select all meaning linked to a single word.
word_table
+----+------+
| id | word |
+----+------+
| 1 | A |
| 2 | B |
| 3 | C |
+----+------+
meaning_table
+----+--------+
| id | meaning|
+----+--------+
| 1 | X |
| 2 | Y |
| 3 | Z |
+----+--------+
word_meaning_table
+---------+-----------+
| word_id | meaning_id|
+---------+-----------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 3 |
| 3 | 2 |
| 3 | 3 |
+---------+-----------+
Expected output should be like this.
If user searching for a word "A" in word table the result should be
Result for word "A"
+----+----------+
| word| meaning |
+----+----------+
| A | X |
| A | Y |
| A | Z |
+----+----------+
I don't know how to write a join query for this scenario.
SELECT wt.word, mt.meaning
FROM word_table wt
INNER JOIN word_meaning_table wmt
ON wt.id = wmt.word_id
INNER JOIN meaning_table mt
ON wmt.meaning_id = mt.id
WHERE wt.word = 'A'
Follow the link below for a working demo:
SQLFiddle
Try
select word, meaning
from word_table
join meaning_table on word_table.id=meaning_table.id;

MySQL insert skipped IDs

I have a mysql table which has two columns, id and value.
id(auto_increment primary key)
value(varchar 255)
insert into table columns(`id`,`value`)VALUES(,'something1');
insert into table columns(`id`,`value`)VALUES(,'something2');
output
+----+---------------+
| id | value |
+----+---------------+
| 1 | something1 |
| 2 | something2 |
+----+---------------+
Now Inserting one value again
insert into table columns(`id`,`value`)VALUES(8,'something8');
Updated Table
+----+---------------+
| id | value |
+----+---------------+
| 1 | something1 |
| 2 | something2 |
| 8 | something8 |
+----+---------------+
Now I am inserting one value again
insert into table columns(`id`,`value`)VALUES(,'something');
Final Output
+----+---------------+
| id | value |
+----+---------------+
| 1 | something1 |
| 2 | something2 |
| 8 | something8 |
| 9 | something |
+----+---------------+
But I want the final output like this
+----+---------------+
| id | value |
+----+---------------+
| 1 | something1 |
| 2 | something2 |
| 8 | something8 |
| 3 | something |
+----+---------------+
Now id is 3 and further insertion will create id 4,5,6,7,9 and so on.
Is there any way to get achieve this ?
I know it is old post but mybie it will help someone :)
I am afraid it cannot be done automatically, however, I solved it to my client, maybe some of you could find it useful:
DECLARE FirstEmptyId int;
SELECT l.id +1 AS
START
FROM TableName AS l
LEFT OUTER JOIN TableName AS r ON l.id +1 = r.id
WHERE r.id IS NULL
LIMIT 1 INTO FirstEmptyId;
Insert Into TableName (Id..) Values (FirstEmptyId...)
STILL, YOU HAVE TO MIND THAT IT IS NOT ADVISABLE TO INSERT ID VALUE OTHER WAY THAN BY AUTOINCREMENT IT AND THERE IS A REASON FOR THAT :)
Consider creating the second column just for performance purposes.
Good luck fellow coders!

Query Peformance for Tags

I’m creating a system, backed by a MySQL database, and a question appeared about what would be the best practice.
I have a “group“ entity, which have zero or more "tags".
The tables could be represented by:
Groups
+-------+--------+
| id | name |
+-------+--------+
| 1 | Group1 |
| 2 | Hey Guy|
| 3 | Chacko |
| 4 | Dropo |
+-------+--------+
GroupsTags
+-------+--------+
|idGroup| idTag |
+-------+--------+
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
+-------+--------+
Tags
+-------+--------+
| id | name |
+-------+--------+
| 1 | Health |
| 2 | Happy |
+-------+--------+
The system, during the “group” registration, I want that to autocomplete every key press, showing the most popular tags, similar to StackOverflow. In other words, I have to do a query at database for each letter type, order by popularity.
What solution is the best?
What is the best?
select count(*) as qt,Tags.nome from GroupsTags
inner join Tags
on Tags.id = GroupsTags.idTag
where Tags.name like "phrase%"
group by GroupsTags.idTag
order by qt desc
or, save in the Tags table, the amount of times it was used. Like this
Tags
+-------+--------+--------+
| id | name | qtUsed |
+-------+--------+--------+
| 1 | Health | 2 |
| 2 | Happy | 1 |
+-------+--------+--------+
select * from Tags where name like "phrase%" order by qtUsed desc
I wonder if it is really needed to put the number of times that the tag was used to get a better performance. Would it be a bad practice ?

Fast SQL search and sort without temporary tables

I have two tables.
Tab1:
+------------+
| id | title |
+------------+
| 1 | B |
| 2 | C |
| 3 | A |
| 4 | A |
| 5 | A |
| 6 | A |
| ... |
+------------+
PK: ID
Index: title
Tab2:
+-------------------------------------------+
| id | item_id | item_key | item_value |
+-------------------------------------------+
| 1 | 1 | value | $4 |
| 2 | 1 | url | http://h.com/ |
| 3 | 2 | value | $5 |
| 4 | 3 | url | http://i.com/ |
| 5 | 3 | value | $1 |
| 6 | 3 | url | http://y.com/ |
| 7 | 4 | value | $2 |
| 8 | 4 | url | http://z.com/ |
| 9 | 5 | value | $1 |
| 10 | 5 | url | http://123.com/ |
| ... |
+-------------------------------------------+
PK: id
Index: item_id, item_key
item_id is a foreign key from tab1.
How do I make it so I get a table of ids from Tab1 in order according to criteria from both tables. The criteria are the following:
Order ASC by title. If title is the same,
Order DESC by value. If both title and value is the same,
Prioritize items who's 'url' key contains '123.com'.
The resulting table with the ordered results would be:
+------------+
| id | title |
+------------+
| 4 | A |
| 5 | A |
| 3 | A |
| 6 | A |
| 1 | B |
| 2 | C |
| ... |
+------------+
I know I can do it with:
SELECT Tab1.id, Tab1.title
FROM Tab1
JOIN Tab2 t2_val ON t2_val.item_id = Tab1.id AND t2_val.item_key='value'
JOIN Tab2 t2_url ON t2_url.item_id = Tab1.id AND t2_url.item_key='url'
ORDER BY title,
t2_val.item_value DESC,
t2_url.item_value LIKE '%123.com%' DESC
but for large data sets, it's too slow. Is there a way to do it faster? I've set index on id and title in Tab 1, and on item key in Tab 2. Now I'd like to drop temporary tables if I could, so that means no joins, right?
How else could this be done?
First, for larger data sets, your result set is going to be larger. What are you doing with the data afterwards? The decrease in performance could be primarily related to the data coming out of the database and not to the processing in the database.
Next, what indexes do you have? The query seems to be begging for an index on tab2(item_key, item_id) to resolve the joins.
And, finally, I don't see how you can get around the final sorting for order by, because it is using values from both tables.
You are using an "entity-attribute-value" (EAV) model. This can be inherently slow when choosing lots of columns for lots of records. If you know that you have these two fields, think about changing the data model so url and value are columns in tab1.
Try this:
SELECT t1.id, t1.title
FROM Tab1 t1
INNER JOIN (SELECT item_id, MAX(item_key='value', item_value, '') AS 'value',
MAX(item_key='url', item_value, '') AS 'url'
FROM Tab2 GROUP BY item_id
) t2 ON t2.item_id = t1.id
ORDER BY t1.title, t2.value DESC, IF(t2.url LIKE '%123.com%', 0, 1);

mySQL query; Ignore specific ID values

I need to make a mySQL query and am not sure what the format should be.
Here is the situation, I have a table with the fields - id, name, type
I would use a query similar to the following to get results from the table:
SELECT * FROM table WHERE type='1'
However, I have a list of ID's from another query. These are items that should be excluded from the results.
I'm sure the answer is simple, but I don't know enough about mySQL queries to find the answer.
A simple NOT IN will be what you want :) It lets you send a list of values and makes sure that its not in them :)
SELECT * FROM table WHERE ID NOT IN (1,2,3)
You could also do it with a subquery with something like:
SELECT id FROM table WHERE ID NOT IN (SELECT id FROM table2 WHERE type = 1)
You can use NOT IN query like this:
SELECT * FROM `table` WHERE `type`=1 WHERE `id` NOT IN (SELECT `blocked_ids` FROM `block`);
So, you would be having blocked IDs in the block table! Hope this helps! :)
Consider this table:
+----+-------------+
| id | name |
+----+-------------+
| 1 | America |
| 2 | Europe |
| 3 | India |
| 4 | Japan |
| 5 | Brazil |
| 6 | Switzerland |
| 7 | Syria |
| 8 | Wales |
| 9 | Taiwan |
| 10 | Zaire |
+----+-------------+
And the blocked table:
+-----+
| IDs |
+-----+
| 1 |
| 4 |
| 6 |
| 8 |
| 9 |
+-----+
Now, when I give a query like:
SELECT * FROM `countries` WHERE `id` NOT IN (SELECT * FROM `blocked`);
I get this result:
+----+--------+
| id | name |
+----+--------+
| 2 | Europe |
| 3 | India |
| 5 | Brazil |
| 7 | Syria |
| 10 | Zaire |
+----+--------+
Hope this helps! :)