How to get values through connected tables? - mysql

I have such a question. I got two tables, the first one contains comments, and the second id comments and album id to which the comment was left
> CREATE TABLE `review` (`id` VARCHAR(32) NOT NULL,
> `user_id` VARCHAR(32) NOT NULL,`comment` MEDIUMTEXT NOT NULL,
> PRIMARY KEY (`id`) )
> CREATE TABLE `review_album` (`review_id` VARCHAR(32) NOT NULL,
> `album_id` VARCHAR(32) NOT NULL, PRIMARY KEY (`review_id`,
> `album_id`), INDEX `review_album_review_idx` (`review_id`) )
I tried this way:
SELECT * from review_album JOIN review WHERE album_id = '300001'
But i got result two times.
How can I get comment text for a specific album_id?

The general syntax is:
SELECT column-names
FROM table-name1 JOIN table-name2
ON column-name1 = column-name2
WHERE condition
The general syntax with INNER is:
SELECT column-names
FROM table-name1 INNER JOIN table-name2
ON column-name1 = column-name2
WHERE condition
Note: The INNER keyword is optional: it is the default as well as the most commonly used JOIN operation.
Refrence : https://www.dofactory.com/sql/join

Try with InnerJoin
SELECT *
FROM review_album
JOIN review ON review_album.review_id=review.id
WHERE album_id = '300001'
Reference

you have forgotten the on condition, everytime you have a join you'd better specify the condition of join, otherwais you have every connection available.
Hovewer the solution
SELECT *
FROM review_album RA
JOIN review R ON RA.column_fk = R.column_fk
WHERE album_id = '300001'
Here the documentation for join https://www.w3schools.com/sql/sql_join.asp

try using this :
SELECT *
FROM review_album ra
JOIN review r ON rareview_id=r.id
WHERE album_id = '300001'

Related

why does index not work as expected in mysql?

I really want to why my index not working.
I have two table post, post_log.
create table post
(
id int auto_increment
primary key,
comment int null,
is_used tinyint(1) default 1 not null,
is_deleted tinyint(1) default 0 not null
);
create table post_log
(
id int auto_increment
primary key,
post_id int not null,
created_at datetime not null,
user int null,
constraint post_log_post_id_fk
foreign key (post_id) references post (id)
);
create index post_log_created_at_index
on post_log (created_at);
When I queried below, created_at index works well.
explain
SELECT *
FROM post p
INNER JOIN post_log pl ON p.id = pl.post_id
WHERE pl.created_at > DATE('2022-06-01')
AND pl.created_at < DATE('2022-06-08')
AND p.is_used is TRUE
AND p.is_deleted is FALSE;
When I queried below, it doesn't work and post table do full scan.
explain
SELECT *
FROM post p
INNER JOIN post_log pl ON p.id = pl.post_id
WHERE pl.created_at > DATE('2022-06-01')
AND pl.created_at < DATE('2022-06-08')
AND p.is_used = 1
AND p.is_deleted = 0;
And below not working either.
explain
SELECT *
FROM post p
INNER JOIN post_log pl ON p.id = pl.post_id
WHERE pl.created_at > DATE('2022-06-01')
AND pl.created_at < DATE('2022-06-08')
and p.comment = 111
what is different between 'tinyint = 1' and 'tinyint is true'?
and, why first query work correctly and the others don't work correctly??
When making the query plan, MySQL has to decide whether to first filter the post_log table using the index, or first filter the post table using the is_used and is_deleted columns.
= 1 tests for the specific value 1, while IS TRUE is true for any non-zero value. I guess it decides that when you're searching for specific values, it will be more efficient to filter the post table first because there will likely be fewer matches (since these columns aren't indexed, it doesn't know that 0 and 1 are the only values).

MySQL query to select rows where one column matches and one column does not

Here are my table setups
Results_class
CREATE TABLE appfilter.results_class (
activity varchar(120) NOT NULL,
class varchar(255) NOT NULL,
PRIMARY KEY (activity, class)
)
Users
CREATE TABLE appfilter.user (
user varchar(15) NOT NULL,
name varchar(100) NOT NULL,
activity_full varchar(200) NOT NULL,
activity varchar(125) NOT NULL,
class varchar(125) NOT NULL
)
The results i want is simple. I want to see all rows where activity matches and class does not.
Example
Results_class
activity class
com.google.android.apps.plus com.google.android.apps.circles.realtimechat.ConversationListActivity
com.google.android.apps.plus com.google.android.apps.plus.phone.ConversationListActivity
Users
activity class
com.google.android.apps.plus com.google.android.apps.circles.realtimechat.ConversationListActivity
I need it to select this missing activity/class which is
activity class
com.google.android.apps.plus com.google.android.apps.plus.phone.ConversationListActivity<br>
I have tried this and it gives me all the activity/classes. Now if i can just remove the ones already in users thats what i need.
SELECT
rc.activity, rc.class
FROM results_class rc
INNER JOIN user b
ON b.activity = rc.activity
Try this .. The below query emulates the MINUS operation ie matches all in class a which are not in b.
SELECT a.activity,a.class FROM results_class a
left join user b on (a.activity = b.activity and a.class = b.class)
where b.class is null
Working fiddle
AFTER EDIT
considering the missed information in the question
"If there is a activity in results_class and not in users its bringing it back too. I only need matching activities to bring non matching classes"
select a1.activity,a1.class from (select a.activity,a.class FROM results_class a
left join user b on (a.activity = b.activity and a.class = b.class)
where b.class is null) a1 inner join user b1 on a1.activity = b1.activity
Try using a subquery with exists
SELECT rc.activity, rc.class
FROM results_class rc
JOIN users c on c.activity = rc.activity
WHERE NOT EXISTS
( SELECT 1
FROM user b
WHERE b.class = rc.class
)
So basically you get all of the results_class activities when joined to users.. then filter out the classes that are in results_class but not in users.
WORKING FIDDLE

Mysql count gives me a very bad performance, am I doing it wrong?

When I want to get the count of a left join SQL, it takes me very very long time,
I cancelled the query after 1 minutes and didn't get the result.
I have two tables.
One is customer, it looks like:
----------------customer---------------
`ID` int(11) NOT NULL AUTO_INCREMENT,
`drpc` int(10) DEFAULT NULL,
`VIN` varchar(60) COLLATE utf8_bin DEFAULT NULL,
`cph` varchar(30) COLLATE utf8_bin DEFAULT NULL,
//... another 60+ columns here
`invalid` int(1) DEFAULT NULL,
PRIMARY KEY (`ID`),
KEY `index_drpc_cph` (`drpc`,`cph`),
KEY `index_drpc_vin` (`drpc`,`VIN`),
KEY `index_drpc_invalid` (`drpc`,`invalid`),
KEY `index_cph` (`cph`)
The other is repair, and it looks like:
-------------repair----------------
`ID` int(11) NOT NULL AUTO_INCREMENT,
`drpc` int(10) NOT NULL,
`cph` varchar(10) DEFAULT NULL,
`czbh` varchar(15) DEFAULT NULL,
`gdh` varchar(12) DEFAULT NULL,
`kdrq` date DEFAULT NULL,
// ... another 20+ columns here
`invalid` int(1) DEFAULT '0',
PRIMARY KEY (`ID`),
KEY `gmrepair_cph` (`cph`),
KEY `gmrepair_czbh` (`czbh`),
KEY `gmrepair_gdh` (`gdh`),
KEY `gmrepair_drpc_kdrq` (`drpc`,`kdrq`),
KEY `index_drpc_invalid` (`drpc`,`invalid`),
KEY `index_drpc_cph` (`drpc`,`cph`)
Both tables have a field: 'cph'.
The original requirement is: for given drpc, get those data cph exists in customer but not exist in repair.
My sql statement looks like this:
SELECT * FROM customer c LEFT JOIN
( SELECT cph FROM repair b WHERE b.drpc=77) r ON c.cph = r.cph
WHERE c.drpc = 76 AND r.cph IS NULL
Here is the explain result:
BTW,
for drpc = 77 in repair table, there are about 20k records;
for drpc = 76 in customer table, there are about 60k records.
And both tables' storage are: InnoDB.
It takes about 3 seconds to execute the sql above.
But, when I want to get the count of the sql above refers to, it takes me very very long time. It cannot finished even in 60 seconds.
I am not sure what the issue is.
Could you please give me some pointers, thanks a million!
Try left outer join instead of left join.
SELECT C.*
FROM Customer C
LEFT OUTER JOIN (SELECT cph from
FROM Repair WHERE drpc = 77)r ON C.cph = r.cph
WHERE C.drpc = 76 AND R.cph IS NULL
My understanding is that the query you provide:
SELECT * FROM customer c LEFT JOIN
( SELECT cph FROM repair b WHERE b.drpc=77) r ON c.cph = r.cph
WHERE c.drpc = 76 AND r.cph IS NULL
Should be the same as a simple left join (this is the count version):
select count(*) from customer c
where c.drpc = 76 and c.cph not in (
select cph from repair where drpc = 77
)
Does this second query take too long too?
It always helps to look at the explain for the plans. It looks like the index on drpc, cph should be used for the query.
However, if your base query works, perhaps this will give you better performance.
select count(*)
from (SELECT *
FROM customer c LEFT JOIN
(SELECT distinct cph
FROM repair b
WHERE b.drpc=77
) r
ON c.cph = r.cph
WHERE c.drpc = 76 AND r.cph IS NULL
) t;
EDIT:
You may be able to force the execution plan by phrasing the query like this:
select count(*)
from customer c
where c.drpc = 76 and
not exists (select 1 from repair r where r.drpc = 77 and r.cph = c.cph);
I don't understand why others did not mention, but the subquery in your query does not allow indexes to be used efficiently, you actually left join on an unindexed table with 20k rows.
For the query you need 2 indexes:
(drpc, cph) on customers and (cph, drpc) on repair (mind the order, you do not have it yet).
Then you need to rewrite the query:
SELECT COUNT(*)
FROM customer c
LEFT JOIN repair r ON c.chp = r.chp AND r.drpc = 77
WHERE c.drpc = 76 AND r.chp IS NULL;
I think I have found the real trick.
It is because of the left join filed cph, which is a varchar(10), that caused the VERY VERY slow when doing left join job.
I create a new column: hash_cph numberic(30,0) on both tables and then convert the cph to some MD5 hash numbers in this way:
UPDATE customer SET hash_cph = CONV(RIGHT(MD5(cph),16),16,10).
So I can apply left join on the new created column hash_cph now, it will be much much more faster.
The final SQL looks like:
SELECT COUNT(*)
FROM customer c
LEFT JOIN repair r ON c.hash_cph= r.hash_cph AND r.drpc = 32 WHERE c.drpc = 1
AND r.hash_cph IS NULL;
btw, I also added index on drpc and hash_cph for both tables.
Thanks for everyone's help!!

MySQL intersect on strings?

I do have two tables:
Quest
- (int) id
- (text) characters
User
- (int) id
- (text) characters
Entries look like this:
Quest
id | characters
1 | abcdefgh
2 | mkorti
3 | afoxi
4 | bac
User
id | characters
1 | abcd
Now I want to select the easiest Quest for User. The easiest quest is the one if the most intersections of quest.characters and user.characters. So in this example the list would look like this (for user.id = 1):
questid | easiness
4 | 100
1 | 50
3 | 40
2 | 0
The easiness simply show how many percent was matched. Is it possible with MySQL to make intersections of columns like this? What's the performance like? In fact I do have relations as well (quest -> character and user -> characters), however I guess it's not very performant. As there are a few thousand quests and also a few thousand characters.
Update #1
Okay, relational still seems the way to go, okay. Now my tables look like this:
CREATE TABLE IF NOT EXISTS `quest` (
`questid` int(10) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`questid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
CREATE TABLE IF NOT EXISTS `questcharacters` (
`questid` int(10) unsigned NOT NULL,
`characterid` int(10) unsigned NOT NULL,
PRIMARY KEY (`questid`,`characterid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `single_character` (
`characterid` int(10) unsigned NOT NULL AUTO_INCREMENT,
`single_char` varchar(10) NOT NULL,
PRIMARY KEY (`characterid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `user` (
`userid` int(10) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `usercharacters` (
`userid` int(10) unsigned NOT NULL,
`characterid` int(10) unsigned NOT NULL,
PRIMARY KEY (`userid`,`characterid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
PS.: Don't wonder why single_char has VARCHAR(10) as data type, but I will use multi-byte values and I don't know how MySQL handles them for char(1). So I just was generous there.
Update #2
My query for now is:
SELECT usercharacters.userid, questcharacters.questid
FROM `usercharacters`
LEFT OUTER JOIN questcharacters ON usercharacters.characterid = usercharacters.characterid
GROUP BY questcharacters.questid, usercharacters.userid;
But how to calculate the easiness/overlapping characters? On which field do I have to apply COUNT()?
Update #3
Okay, seems like I got it working with this query (uses a subselect):
SELECT usercharacters.userid as uid, questcharacters.questid as qid, (SELECT COUNT(questcharacters.characterid) FROM questcharacters LEFT OUTER JOIN usercharacters ON questcharacters.characterid = usercharacters.characterid WHERE questcharacters.questid = qid) as questcount
FROM `usercharacters`
LEFT OUTER JOIN questcharacters ON usercharacters.characterid = usercharacters.characterid
GROUP BY questcharacters.questid, usercharacters.userid;
Update #4
SELECT usercharacters.userid as uid, questcharacters.questid as qid, (SELECT COUNT(questcharacters.characterid) FROM questcharacters LEFT OUTER JOIN usercharacters ON questcharacters.characterid = usercharacters.characterid WHERE questcharacters.questid = qid) as user_knows, (SELECT COUNT(questcharacters.characterid) FROM questcharacters WHERE questcharacters.questid = qid) as total_characters
FROM `usercharacters`
LEFT OUTER JOIN questcharacters ON usercharacters.characterid = usercharacters.characterid
GROUP BY questcharacters.questid, usercharacters.userid
ORDER BY total_characters / user_knows DESC;
Only thing missing now: Selecting the easyiness. (As in the ORDER BY clause). Anyone knows how to do this?
So this is my final and working solution:
SELECT usercharacters.userid AS uid,
questcharacters.questid AS qid,
(SELECT Count(questcharacters.characterid)
FROM questcharacters
LEFT OUTER JOIN usercharacters
ON questcharacters.characterid =
usercharacters.characterid
WHERE questcharacters.questid = qid) AS user_knows,
(SELECT Count(questcharacters.characterid)
FROM questcharacters
WHERE questcharacters.questid = qid) AS total_characters,
(SELECT ( Count(questcharacters.characterid) / (SELECT
Count(questcharacters.characterid)
FROM questcharacters
WHERE
questcharacters.questid = qid) )
FROM questcharacters
LEFT OUTER JOIN usercharacters
ON questcharacters.characterid =
usercharacters.characterid
WHERE questcharacters.questid = qid) AS ratio
FROM `usercharacters`
LEFT OUTER JOIN questcharacters
ON usercharacters.characterid = usercharacters.characterid
GROUP BY questcharacters.questid,
usercharacters.userid
ORDER BY ratio DESC;
Do I really need that many sub-selects?
If you actually have questcharacter and usercharacters tables, then that is the best way to go:
SELECT uc.id AS userid,
qc.id AS qcid,
COUNT(*) AS NumCharacters,
COUNT(qc.char) AS Nummatches,
COUNT(qc.char) / count(*) AS Easiness
FROM UserCharacters uc
LEFT OUTER JOIN QuestCharacters qc ON uc.char = qc.char
WHERE uc.id = 1
GROUP BY uc.id, qc.id
ORDER BY easiness DESC
LIMIT 1
If you have them only as strings -- the SQL is not pretty. You have to do a cross join and lots of string manipulation. The best approach is to have things more normalized in the form of a relational database (one row per list element), rather than having lists embedded in strings.

Mysql query to check if all sub_items of a combo_item are active

I am trying to write a query that looks through all combo_items and only returns the ones where all sub_items that it references have Active=1.
I think I should be able to count how many sub_items there are in a combo_item total and then compare it to how many are Active, but I am failing pretty hard at figuring out how to do that...
My table definitions:
CREATE TABLE `combo_items` (
`c_id` int(11) NOT NULL,
`Label` varchar(20) NOT NULL,
PRIMARY KEY (`c_id`)
)
CREATE TABLE `sub_items` (
`s_id` int(11) NOT NULL,
`Label` varchar(20) NOT NULL,
`Active` int(1) NOT NULL,
PRIMARY KEY (`s_id`)
)
CREATE TABLE `combo_refs` (
`r_id` int(11) NOT NULL,
`c_id` int(11) NOT NULL,
`s_id` int(11) NOT NULL,
PRIMARY KEY (`r_id`)
)
So for each combo_item, there is at least 2 rows in the combo_refs table linking to the multiple sub_items. My brain is about to make bigbadaboom :(
I would just join the three tables usually and then combo-item-wise sum up the total number of sub-items and the number of active sub-items:
SELECT ci.c_id, ci.Label, SUM(1) AS total_sub_items, SUM(si.Active) AS active_sub_items
FROM combo_items AS ci
INNER JOIN combo_refs AS cr ON cr.c_id = ci.c_id
INNER JOIN sub_items AS si ON si.s_id = cr.s_id
GROUP BY ci.c_id
Of course, instead of using SUM(1) you could just say COUNT(ci.c_id), but I wanted an analog of SUM(si.Active).
The approach proposed assumes Active to be 1 (active) or 0 (not active).
To get only those combo-items whose all sub-items are active, just add WHERE si.Active = 1. You could then reject the SUM stuff anyway. Depends on what you are looking for actually:
SELECT ci.c_id, ci.Label
FROM combo_items AS ci
INNER JOIN combo_refs AS cr ON cr.c_id = ci.c_id
INNER JOIN sub_items AS si ON si.s_id = cr.s_id
WHERE si.Active = 1
GROUP BY ci.c_id
By the way, INNER JOIN ensures that there is at least one sub-item per combo-item at all.
(I have not tested it.)
See this answer:
MySQL: Selecting foreign keys with fields matching all the same fields of another table
Select ...
From combo_items As C
Where Exists (
Select 1
From sub_items As S1
Join combo_refs As CR1
On CR1.s_id = S1.s_id
Where CR1.c_id = C.c_id
)
And Not Exists (
Select 1
From sub_items As S2
Join combo_refs As CR2
On CR2.s_id = S2.s_id
Where CR2.c_id = C.c_id
And S2.Active = 0
)
The first subquery ensures that at least one sub_item exists. The second ensures that none of the sub_items are inactive.