LEFT JOIN runs very slowly, but nested select does not - mysql

I heard, that JOINS are more efficient, but when i try to implement it, then SELECT is stuck in executing.
I'm using MySQL and I have two tables filled with 100,000 records with a for loop.
CREATE TABLE `offers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY(`id`)
);
CREATE TABLE `files` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`path` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`object_id` int(11) NOT NULL,
PRIMARY KEY(`id`)
);
And following query takes 0,3s to execute:
SELECT o.id, (SELECT f.path FROM `files` AS f WHERE f.object_id = o.id LIMIT 1) as path FROM `offers` AS o;
But, this query which is working when files table is almost empty in exactly the same way. But when i fill files table, executing never finished.
SELECT o.id, f.path FROM `offers` o LEFT OUTER JOIN `files` f ON f.object_id = o.id
What i'm doing wrong?

for left join performance
SELECT o.id, f.path
FROM `offers` o
LEFT OUTER JOIN `files` f ON f.object_id = o.id
be suure you have a composite index
on table files column(object_id, path)

Related

Trying to join multiple tables in mysql - get error 1054, Unknown Column

I want to join multiple tables using JOINs, and mysql/mariadb is refusing to find one column. The column exists, of course, and I can't figure out what the cause might be.
Table Layout
CREATE TABLE `shops` (
`id` int(11) NOT NULL,
`name` varchar(32) NOT NULL,
);
CREATE TABLE `shops2categories` (
`shopid` int(11) NOT NULL,
`categoryid` int(11) NOT NULL,
);
CREATE TABLE `categories` (
`id` int(11) NOT NULL,
`name` varchar(64) NOT NULL,
);
CREATE TABLE `categories2items` (
`itemid` int(11) NOT NULL,
`categoryid` int(11) NOT NULL
);
CREATE TABLE `items` (
`id` int(11) NOT NULL,
`name` varchar(32) NOT NULL,
);
Query:
In order to avoid confusion with aliases, I now ran the query with the original table names.
SELECT
shops.name,
categories.name,
items.name
FROM shops
LEFT JOIN shops2categories ON shops2categories.shopid = shops.id
LEFT JOIN categories2items ON categories2items.categoryid = categories.id
LEFT JOIN categories ON categories.id = shops2categories.categoryid
LEFT JOIN items ON items.id = categories2items.itemid
Error Message:
#1054 - Unknown column 'categories.id' in 'on clause'
No matter how I restructured my query (foreign key first, primary key first, items table first, categories table first, ..., using different JOIN types), I can't seem to get this to work. Also I read through a whole lot of SO questions to this topic, but I feel that the order is correct, I am not inserting nor updating, and it's not about quoting. Something is fundamentally broken in my concept, and I'm very keen on learning what this could be.
Look at your from clause. You reference c.id before you have defined c.
A table cannot be referenced until it is defined in the FROM clause.
You would seem to want:
FROM shops s LEFT JOIN
shops2categories s2c
ON s2c.shopid = s.id LEFT JOIN
categories c
ON c.id = s2c.categoryid LEFT JOIN
categories2items c2i
ON c2i.categoryid = c.id LEFT JOIN
items i
ON i.id = c2i.itemid

Is there anyway to insert only unique entries into a pivot table in MYSQL?

I have tried to use INSERT IGNORE but still I can see that when I runs the query again. It still inserts the records. I don't want to insert duplicate records in there.
Can anyone help me out?
INSERT INTO enquiry_location_status
(enquiry_id,
location_id,
enquiry_status)
SELECT we.id AS enquiry_id,
wl.location_id AS location_id,
we.status AS enquiry_status
FROM enquiry we
LEFT JOIN wishlist w
ON w.enquiry_id = we.id
LEFT JOIN wishlist_location wl
ON wl.wishlist_id = w.id
WHERE wl.wishlist_id IS NOT NULL;
My table definition:
CREATE TABLE `enquiry_location_status` (
`id` bigint(20) NOT NULL,
`enquiry_id` int(10) NOT NULL,
`location_id` int(11) NOT NULL,
`enquiry_status` varchar(30) NOT NULL
);
ALTER TABLE `enquiry_location_status`
ADD PRIMARY KEY (`id`);
ALTER TABLE `enquiry_location_status`
MODIFY `id` bigint(20) NOT NULL AUTO_INCREMENT;
Here is a screenshot how my pivot table data looks like:
I have tried this, it seems to be working but it's very slow. It's taking 80 seconds for 40 K+ records just.
INSERT INTO enquiry_location_status
(enquiry_id,
location_id,
enquiry_status)
SELECT we.id AS enquiry_id,
wl.location_id AS location_id,
we.status AS enquiry_status
FROM enquiry we
LEFT JOIN wishlist w
ON w.enquiry_id = we.id
LEFT JOIN wishlist_location wl
ON wl.wishlist_id = w.id
WHERE wl.wishlist_id IS NOT NULL
AND NOT EXISTS (SELECT els.enquiry_id,
els.location_id,
els.enquiry_status
FROM enquiry_location_status els
WHERE els.enquiry_id = we.id
AND els.location_id = wl.location_id
AND els.enquiry_status = we.status)
Thanks

How to joint Two customs Queries with two Joins in only One Query in MySQL

The Queries are working perfectly each one separatedly:
SELECT asf.surface_name, am.*
FROM atp_matchs_to_surfaces m2s
LEFT JOIN atp_surfaces asf ON m2s.surfaces_id = asf.surfaces_id
LEFT JOIN atp_matchs am ON am.matchs_id = m2s.matchs_id;
SELECT att.tournament_type_name, am.*
FROM atp_matchs_to_tournament_type m2s
LEFT JOIN atp_tournament_type att ON m2s.tournament_type_id = att.tournament_type_id
LEFT JOIN atp_matchs am ON am.matchs_id = m2s.matchs_id;
The tables 'atp_matchs_to_surfaces' and 'atp_matchs_to_tournament_type' are defined in that way:
CREATE TABLE IF NOT EXISTS `atp_matchs_to_tournament_type` (
`tournament_type_id` int(4) NOT NULL,
`matchs_id` int(6) NOT NULL,
PRIMARY KEY (`tournament_type_id`,`matchs_id`)
CREATE TABLE IF NOT EXISTS `atp_matchs_to_surfaces` (
`surfaces_id` int(4) NOT NULL,
`matchs_id` int(6) NOT NULL,
PRIMARY KEY (`surfaces_id`,`matchs_id`)
And the other Tables with all the data:
CREATE TABLE IF NOT EXISTS `atp_matchs` (
`matchs_id` int(7) NOT NULL AUTO_INCREMENT,
`tournament_name` varchar(36) NOT NULL,
`tournament_year` year NOT NULL,-- DEFAULT '0000',
`tournament_country` varchar(26) NOT NULL,
`match_datetime` datetime NOT NULL,-- DEFAULT '0000-00-00 00:00:00',
`match_link` varchar(85) NOT NULL,
`prize_money` int(12) NOT NULL,
`round` varchar(8) NOT NULL,-- DEFAULT '1R',
`sets` varchar(34) NOT NULL,-- DEFAULT '0-0',
`result` varchar(4) NOT NULL,-- DEFAULT '0-0',
`p1_odd` decimal(4,2) NOT NULL,-- DEFAULT '0.00',
`p2_odd` decimal(4,2) NOT NULL,-- DEFAULT '0.00',
PRIMARY KEY (`matchs_id`)
CREATE TABLE IF NOT EXISTS `atp_surfaces` (
`surfaces_id` int(4) NOT NULL AUTO_INCREMENT,
`surface_name` varchar(24) NOT NULL,
PRIMARY KEY (`surfaces_id`)
CREATE TABLE IF NOT EXISTS `atp_tournament_type` (
`tournament_type_id` int(4) NOT NULL AUTO_INCREMENT,
`tournament_type_name` varchar(22) NOT NULL,
PRIMARY KEY (`tournament_type_id`)
I want in the same Query all the records of match and surface name+tournament type. It's clear? I hope...
I tried to implement this with SubQueries: http://www.w3resource.com/mysql/subqueries/ and How can an SQL query return data from multiple tables but i can't do it to work.
OK, this is your current schema. As you can see, one match can be played on multiple surfaces and one match can be played within multiple tournament types.
If this schema is OK, you can get your result with this query:
SELECT am.*, asu.surface_name, att.tournament_type_name
FROM atp_matchs AS am
LEFT JOIN atp_matchs_to_surfaces AS m2s ON m2s.matchs_id = am.matchs_id
LEFT JOIN atp_surfaces AS asu ON asu.surfaces_id = m2s.surfaces_id
LEFT JOIN atp_matchs_to_tournament_type AS m2t ON m2t.matchs_id = am.matchs_id
LEFT JOIN atp_tournament_type AS att ON att.tournament_type_id = m2t.tournament_type_id
However, if one match can be played on one surface only and within one tournament type only, I would change your schema to:
Tables atp_matchs_to_surfaces and atp_matchs_to_tournament_type are removed and fields surfaces_id and tournament_type_id moved to atp_matchs table. Your query is now:
SELECT am.*, asu.surface_name, att.tournament_type_name
FROM atp_matchs AS am
LEFT JOIN atp_surfaces AS asu ON asu.surfaces_id = am.surfaces_id
LEFT JOIN atp_tournament_type AS att ON att.tournament_type_id = am.tournament_type_id
The LEFT JOIN keyword returns all rows from the left table (table1), with the matching rows in the right table (table2).
SELECT asf.surface_name, am.*
FROM atp_matchs_to_surfaces m2s
LEFT JOIN (SELECT att.tournament_type, am.*
FROM atp_matchs_to_tournament_type m2s) as......
SELECT asf.surface_name, am.*
FROM atp_matchs_to_surfaces m2s
LEFT JOIN atp_surfaces asf ON m2s.surfaces_id = asf.surfaces_id
LEFT JOIN atp_matchs am ON am.matchs_id = m2s.matchs_id
LEFT JOIN(
SELECT att.tournament_type, am.*
FROM atp_matchs_to_tournament_type m2s
LEFT JOIN atp_tournament_type att AS Q1 ON m2s.surfaces_id = att.surfaces_id
LEFT JOIN atp_matchs am AS Q2 ON am.matchs_id = m2s.matchs_id);
I added some "AS" because I had the error: Every derived table must have its own alias. I'm a little lost here!

MySQL joining most recent record from another query is slow

I have the two following tables:
CREATE TABLE `modlogs` (
`mod` int(11) NOT NULL,
`ip` varchar(39) CHARACTER SET ascii NOT NULL,
`board` varchar(58) CHARACTER SET utf8 DEFAULT NULL,
`time` int(11) NOT NULL,
`text` text NOT NULL,
KEY `time` (`time`),
KEY `mod` (`mod`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4
CREATE TABLE `mods` (
`id` smallint(6) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(30) NOT NULL,
`password` char(64) CHARACTER SET ascii NOT NULL COMMENT 'SHA256',
`salt` char(32) CHARACTER SET ascii NOT NULL,
`type` smallint(2) NOT NULL,
`boards` text CHARACTER SET utf8 NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`,`username`)
) ENGINE=MyISAM AUTO_INCREMENT=933 DEFAULT CHARSET=utf8mb4
I want to join the most recent log entry with the mod's name, however my query is very slow (takes 5.23 seconds):
SELECT *
FROM mods LEFT JOIN
modlogs
ON modlogs.mod = mods.id
AND modlogs.time = (SELECT MAX(time)
FROM mods
WHERE mods.id = modlogs.mod
);
All other answers on SO also seem to use dependent subqueries. Is there a way I can do this in a way that will return results more quickly?
Here's another solution, putting the subquery into a derived table avoids the problem of a dependent subquery. It'll run the subquery just once.
SELECT *
FROM mods AS m
LEFT JOIN (
SELECT ml1.*
FROM modlogs AS ml1
JOIN (
SELECT `mod`, MAX(time) AS time
FROM modlogs
GROUP BY `mod`
) AS ml2 USING (`mod`, time)
) AS ml ON m.id = ml.`mod`;
This is your query:
SELECT *
FROM mods LEFT JOIN
modlogs
ON modlogs.mod = (SELECT MAX(time)
FROM modlogs
WHERE mods.id = modlogs.mod
);
This query does not make sense. You are comparing something called mod to a max time. Sounds like it won't work to me, but then there are some very "clever" data models out there. I suspect you really want:
SELECT *
FROM mods LEFT JOIN
modlogs
ON mods.id = modlods.mod and
modlogs.time = (SELECT MAX(time)
FROM mods
WHERE mods.id = modlogs.mod
);
I wouldn't write the query this way, because join conditions in the on clause seem confusing to me. But, you did. You can get better performance with an index. I would suggest:
create index modlogs_mod_time on modlogs(mod, time);
I would write the query as:
SELECT *
FROM mods LEFT JOIN
modlogs
ON mods.id = modlods.mod
WHERE NOT EXISTS (SELECT 1
FROM modlogs ml2
WHERE modlogs.mod = ml2.mod and
ml2.time > modlogs.time
);
I think you can also solve this one with an anti-join, though I'm skeptical of the performance on this one:
SELECT mods.*, modlogs.*
FROM mods
LEFT JOIN modlogs
ON modlogs.mod = mods.id
LEFT JOIN mods m2
ON m2.id = modlogs.mod
AND m2.time < modlogs.time
WHERE m2.id IS NULL
Ensure you have an index on modlogs(mod), and consider the index mods(id, time) for better performance.

LEFT JOIN not working as expected with sub-query

I've got the SQL query below:
SELECT message, sent_date, user_id
FROM messages
LEFT JOIN numbers ON messages.from_id = numbers.id
It returns all the rows (about 4000) in the messages table with additional columns coming from the numbers table. So far, this is what I would expect.
Now I left join this sub-query to another table, again using a left join:
SELECT message, sent_date
FROM (
SELECT message, sent_date, user_id
FROM messages
LEFT JOIN numbers ON messages.from_id = numbers.id
) AS table1
LEFT JOIN users ON table1.user_id = users.id
However, it only returns about 200 rows so many are missing. Since this is a left join I would expect all the rows from table1 to be in the result. Can anybody see what the issue is?
Edit:
So for information here are the 3 relevant tables (with irrelevant columns removed):
CREATE TABLE IF NOT EXISTS `messages` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`message` text CHARACTER SET utf8 NOT NULL,
`from_id` int(11) DEFAULT NULL,
`sent_date` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `from_id` (`from_id`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=101553 ;
CREATE TABLE IF NOT EXISTS `numbers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`number` varchar(32) NOT NULL,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6408 ;
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(256) CHARACTER SET utf8 DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2395 ;
You can try alternative method to debug the issue:
CREATE TEMPORARY table tmp1 AS SELECT message, sent_date, user_id
FROM messages
LEFT JOIN numbers
ON messages. from_id = numbers.id;
and then see whether this query works.
SELECT message, sent_date
FROM tmp1 table1
LEFT JOIN users
ON table1.user_id = users.id;
Also for your case make sure that there are no other insert or updates in between. otherwise use transactions.
table1 sometimes won't have a UserID - so that'll be null, so those results will be missing?
I don't have an exact answer to your question, but if I have to start thinking, I will first find out what 3800 rows are missing and try to see the pattern (is it because user_id are null or duplicate)
SELECT message, sent_date, user_id
FROM messages
LEFT JOIN numbers ON messages.from_id = numbers.id
MINUS
(SELECT table1.message, table1.sent_date, table1.user_id
FROM (
SELECT message, sent_date, user_id
FROM messages
LEFT JOIN numbers ON messages.from_id = numbers.id
) AS table1
LEFT JOIN users ON table1.user_id = users.id)
Try this, I think it's a scoping issue on user_id.
SELECT table1.message, table1.sent_date
FROM (
SELECT messages.message, messages.sent_date, numbers.user_id
FROM messages
LEFT JOIN numbers ON messages.from_id = numbers.id
) AS table1
LEFT JOIN users ON table1.user_id = users.id
I'm not sure if user_id is in messages or numbers.
There is no way this should happen.
Try this variation:
SELECT
m.message, m.sent_date, n.user_id
FROM
messages m
LEFT JOIN
numbers AS n ON m.from_id = n.id
LEFT JOIN
users AS u ON n.user_id = u.id ;