I have been bashing my head against a brick wall trying to get this working and I don't know why it's not.
I join tables A and B using my_field. I then run a sub query to get my_field from table B where complete = 1. This is what I want to use to query tables C and D
This is my current query
SELECT
table_A.*,
table_B.*,
table_C.*,
table_D.*
FROM table_A
INNER JOIN table_B ON
table_A.my_field = table_B.my_field
LEFT JOIN (SELECT my_field FROM table_B WHERE complete ='1') test ON
table_B.my_field = test.my_field
RIGHT JOIN table_C ON
test.my_field = table_C.my_field
INNER JOIN table_D ON
table_C.my_field = table_D.my_field
This is the output of the current query
table_A.field1 | table_A.field2 | table_B.field1 | table_B.field2 | table_C.field1 | table_C.field2 | table_D.field1 | table_D.field2 | test.complete
=============================================================================================================================================================================
something | something | something | something | something | something | something | something | 1
null | null | null | null | something | something | something | something | 0
and this is what i want to get
table_A.field1 | table_A.field2 | table_B.field1 | table_B.field2 | table_C.field1 | table_C.field2 | table_D.field1 | table_D.field2 | test.complete
=============================================================================================================================================================================
something | something | something | something | something | something | something | something | 1
something | something | something | something | null | null | null | null | 0
UPDATE:
Here is the table structures. I've removed columns that have do not link to any other table
CREATE TABLE IF NOT EXISTS `table_A` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`code` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `code` (`code`)
);
CREATE TABLE IF NOT EXISTS `table_B` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`code` varchar(255) NOT NULL,
`complete` enum('0','1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
);
CREATE TABLE IF NOT EXISTS `table_C` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`code` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `code` (`code`)
);
CREATE TABLE IF NOT EXISTS `table_D` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`code` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
);
why don't you get rid of the Left Join? This makes it easier to read.
SELECT
table_A.*,
table_B.*,
table_C.*,
table_D.*
FROM table_A
INNER JOIN table_B ON
table_A.my_field = table_B.my_field
LEFT JOIN table_C ON
table_B.my_field = table_C.my_field and table_B.complete ='1'
LEFT JOIN table_D ON
table_C.my_field = table_D.my_field
Related
I have the following tables in my database:
product_fav:
CREATE TABLE `product_fav` (
`user_id` int(9) unsigned NOT NULL,
`asin` varchar(10) NOT NULL DEFAULT '',
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`price` decimal(7,2) NOT NULL,
PRIMARY KEY (`user_id`,`asin`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
product_info:
CREATE TABLE `product_info` (
`asin` varchar(10) NOT NULL,
`name` varchar(200) DEFAULT NULL,
`brand` varchar(50) DEFAULT NULL,
`part_number` varchar(50) DEFAULT NULL,
`url` text,
`image` text,
`availabillity` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`asin`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
product_price:
CREATE TABLE `product_price` (
`asin` varchar(10) NOT NULL,
`date` date NOT NULL,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`price` decimal(7,2) NOT NULL DEFAULT '0.00',
PRIMARY KEY (`asin`,`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
I have the following query:
SELECT pi.*,
pp.price,
pf.date,
pf.price AS price_added,
round((100.0 (pp.price - pf.price) / pf.price),0) AS percentdiff
FROM product_info pi
JOIN
(
SELECT *
FROM product_price
ORDER BY date DESC) pp
ON pp.asin = pi.asin
JOIN product_fav pf
ON pp.asin = pf.asin
WHERE pf.user_id=". $user['user_id'] ."
GROUP BY asin
Product price has many records and query needs about 3 second. Is it possible to make it faster?
I have also the same issue with search query:
SELECT pi.*,
price,
date
FROM product_info pi
JOIN (SELECT *
FROM product_price
ORDER BY date DESC) pp
ON pi.asin = pp.asin
WHERE ( ` NAME ` LIKE '%".$search."%' )
GROUP BY pi.asin
ORDER BY price
EXPLAIN return this:
+----+-------------+---------------+--------+---------------+---------+---------+---------------+--------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------+--------+---------------+---------+---------+---------------+--------+---------------------------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 106709 | Using temporary; Using filesort |
| 1 | PRIMARY | pi | eq_ref | PRIMARY | PRIMARY | 32 | pp.asin | 1 | |
| 1 | PRIMARY | pf | eq_ref | PRIMARY | PRIMARY | 36 | const,pp.asin | 1 | |
| 2 | DERIVED | product_price | ALL | NULL | NULL | NULL | NULL | 112041 | Using filesort |
+----+-------------+---------------+--------+---------------+---------+---------+---------------+--------+---------------------------------+
You dont ORDER before JOIN, If you need order do it after the WHERE and GROUP BY so less data to sort.
JOIN
(
SELECT *
FROM product_price
ORDER BY date DESC) pp
Create index for asin so JOIN for ON pp.asin = pi.asin will be more efficient
Create index for user_id so the WHERE pf.user_id=". $user['user_id'] ." will be more efficient
Try running an EXPLAIN on your query to figure out where the bottle-neck is.
What's with the ORDER BY date in the inner query? Try getting rid of it. Also try replacing the inner query with a JOIN, they tend to be faster.
Also, do you have an index on the date field? Try adding one for the ORDER BY at the end of the query.
I currently join 5 tables to select 20 objects to show the user, unfortunately if I use GROUP BY and ORDER BY it gets really slow.
An example query looks Like this:
SELECT r.name, l.name, o.typ, o.id, persons, children, description, rating, totalratings, minprice, picture FROM angebote as a
JOIN objekte as o ON a.fid_objekt = o.id
JOIN regionen as r ON a.fid_region = r.id
JOIN laender as l ON a.fid_land = l.id
WHERE l.slug="aegypten" AND a.letztes_angebot >= 1
GROUP BY a.fid_objekt ORDER BY rating DESC LIMIT 0,20
The EXPLAIN of the Query shows this:
+------+-------------+-------+--------+----------------------------+------------+---------+---------------------------------------+--------+--------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+-------+--------+----------------------------+------------+---------+---------------------------------------+--------+--------------------------------------------------------+
| 1 | SIMPLE | l | ref | PRIMARY,slug | slug | 767 | const | 1 | Using index condition; Using temporary; Using filesort |
| 1 | SIMPLE | o | ALL | PRIMARY | NULL | NULL | NULL | 186779 | Using join buffer (flat, BNL join) |
| 1 | SIMPLE | a | ref | unique_key,letztes_angebot | unique_key | 8 | ferienhaeuser.o.id,ferienhaeuser.l.id | 1 | Using where |
| 1 | SIMPLE | r | eq_ref | PRIMARY | PRIMARY | 4 | ferienhaeuser.a.fid_region | 1 | |
+------+-------------+-------+--------+----------------------------+------------+---------+---------------------------------------+--------+--------------------------------------------------------+
So it looks like it doesn't use a key for the table objekte, the Profiling says it uses 2.7s for Copying to tmp table.
Instead of FROM angebote or JOIN objekte I tried it with (SELECT * GROUP BY id) but unfortunately this doesn't improve.
The fields used for WHERE, ORDER BY and GROUP BY are also indexed.
I think I missed some basic concept here and any help will be appreciated.
Since it's most probable I made a mistake with the Tables, here the description of them:
Objekte
+---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| objekte | CREATE TABLE `objekte` (
`id` int(11) NOT NULL,
`typ` varchar(50) NOT NULL,
`persons` int(11) NOT NULL,
`children` int(11) NOT NULL,
`description` text NOT NULL,
`rating` float NOT NULL,
`totalratings` int(11) NOT NULL,
`minprice` float NOT NULL,
`picture` varchar(255) NOT NULL,
`last_offer` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `minprice` (`minprice`),
KEY `rating` (`rating`),
KEY `last_offer` (`last_offer`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+---------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
Angebote
+-----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| angebote | CREATE TABLE `angebote` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`fid_objekt` int(11) NOT NULL,
`fid_land` int(11) NOT NULL,
`fid_region` int(11) NOT NULL,
`fid_subregion` int(11) NOT NULL,
`letztes_angebot` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_key` (`fid_objekt`,`fid_land`,`fid_region`,`fid_subregion`),
KEY `letztes_angebot` (`letztes_angebot`),
KEY `fid_objekt` (`fid_objekt`),
KEY `fid_land` (`fid_land`),
KEY `fid_region` (`fid_region`),
KEY `fid_subregion` (`fid_subregion`)
) ENGINE=InnoDB AUTO_INCREMENT=2433073 DEFAULT CHARSET=utf8 |
+-----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
laender, regionen, subregionen (same structure)
+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| laender | CREATE TABLE `laender` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`iso` varchar(2) NOT NULL,
`name` varchar(255) NOT NULL,
`slug` varchar(255) NOT NULL,
`letztes_angebot` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `iso` (`iso`),
KEY `slug` (`slug`),
KEY `letztes_angebot` (`letztes_angebot`)
) ENGINE=InnoDB AUTO_INCREMENT=107 DEFAULT CHARSET=utf8 |
+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
First of all this is a non standard group by. As such it will stop working when you upgrade to mysql 5.7.
The biggest problem comes from the fact that no index is used on the objekte table. To make matters worse you are ordering on the ratings field on that table but the index is still not being used. A possible solution is to create a composite index like this:
CREATE INDEX objekte_idx ON objekte(id,rating);
You do not need to use GROUP BY here. You have not use aggregrate functions. So remove GROUP BY from query. Remove the Group By will increase query performance. Also no need to define 0 for limit.
SELECT r.name, l.name, o.typ, o.id, persons, children, description, rating, totalratings, minprice, picture FROM angebote as a
JOIN objekte as o ON a.fid_objekt = o.id
JOIN regionen as r ON a.fid_region = r.id
JOIN laender as l ON a.fid_land = l.id
WHERE l.slug="aegypten" AND a.letztes_angebot >= 1
ORDER BY rating DESC LIMIT 20
I have some speed problems with query, that shows list of users in my DB.
I want to show list of users with traffic info and the last employee who works with user.
DB looks like this:
users table (contains users info):
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ip` tinytext NOT NULL,
`name` varchar(64) NOT NULL,
... some other fields
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),
KEY `ip` (`ip`(15)) USING BTREE,
)
users_trf table (contains information about users traffic; uid - id of users from users table):
CREATE TABLE `users_trf` (
`uid` int(11) unsigned NOT NULL,
`uip` varchar(15) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
`in` bigint(20) NOT NULL DEFAULT '0',
`out` bigint(20) NOT NULL DEFAULT '0',
`test` tinyint(4) NOT NULL,
UNIQUE KEY `uid` (`uid`),
KEY `test` (`test`)
)
employees with list of all employees:
CREATE TABLE `employees` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`full_name` varchar(16) NOT NULL,
PRIMARY KEY (`id`)
)
and log table where I store data about jobs which employee did with client (uid - id of the client from users table, mid - id of employees from employees table):
CREATE TABLE `employees_log` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`uid` int(10) unsigned NOT NULL,
`mid` int(10) unsigned NOT NULL,
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`note` text NOT NULL,
PRIMARY KEY (`id`)
)
My query:
SELECT SQL_CALC_FOUND_ROWS *
FROM users u
LEFT JOIN users_trf t ON u.id = t.uid
LEFT JOIN (
SELECT e2.full_name, e1.uid, e1.mid AS moid
FROM employees_log e1
LEFT JOIN employees e2 ON e1.mid = e2.id
WHERE NOT
EXISTS (
SELECT *
FROM employees_log e3
WHERE e1.uid = e3.uid
AND e1.id < e3.id
)
) e ON e.uid = u.id
LIMIT 0 , 50
it works very slow, I think the reason of this is this subquery (I'm trying to select the last employee who works with client):
SELECT e2.full_name, e1.uid, e1.mid AS moid
FROM employees_log e1
LEFT JOIN employees e2 ON e1.mid = e2.id
WHERE NOT
EXISTS (
SELECT *
FROM employees_log e3
WHERE e1.uid = e3.uid
AND e1.id < e3.id
)
Is it possible to speed up my query?
UPD:
I added index ALTER TABLE employees_log ADD INDEX ( uid, id ); and query become 2 times faster, but can I make it more faster?
+----+--------------------+------------+--------+---------------+---------+---------+-------------+-------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+------------+--------+---------------+---------+---------+-------------+-------+--------------------------+
| 1 | PRIMARY | u | ALL | NULL | NULL | NULL | NULL | 12029 | |
| 1 | PRIMARY | t | eq_ref | uid | uid | 4 | bill.u.id | 1 | |
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 2239 | |
| 2 | DERIVED | e1 | ALL | NULL | NULL | NULL | NULL | 2288 | Using where |
| 2 | DERIVED | e2 | eq_ref | PRIMARY | PRIMARY | 4 | bill.e1.mid | 1 | |
| 3 | DEPENDENT SUBQUERY | e3 | ref | PRIMARY,uid | uid | 4 | bill.e1.uid | 1 | Using where; Using index |
+----+--------------------+------------+--------+---------------+---------+---------+-------------+-------+--------------------------+
first of all, i think you have to expalin to yourself why using int and bigint. do you really expect so much data? try using smallint or mediumint, they need less memory and are much faster. if you use the mediumint and smallint as unsigned, they can have a pretty large value, take a look at: http://dev.mysql.com/doc/refman/5.0/en/integer-types.html
second, you need to combine some field to one key:
ALTER TABLE `employees_log ` ADD INDEX ( `uid` , `id` ) ;
If you are creating a new MySQL table you can specify a column to index by using the INDEX term.Indexes are something extra that you can enable on your MySQL tables to increase performance
http://www.databasejournal.com/features/mysql/article.php/1382791/Optimizing-MySQL-Queries-and-Indexes.htm
http://www.tutorialspoint.com/mysql/mysql-indexes.htm view this it gives you much idea..
With your goal of trying to join to the log for the last employee for that user in the log table (based on the key at least), maybe just try a = <subquery> instead of a NOT EXISTS?
SELECT e2.full_name, e1.uid, e1.mid AS moid
FROM employees_log e1
LEFT JOIN employees e2 ON e1.mid = e2.id
WHERE e1.id = (
SELECT MAX(e3.id)
FROM employees_log e3
WHERE e1.uid = e3.uid
)
Consider adding an index on the column MID and UID on employees_log - the explain suggests that this join is not using an index.
Like so: create index compound on employees_log (mid, uid)
I've got the following tables:
Products:
+---------------------------+
|id|name |details |price|cn|
|__|_____|_________|_____|__|
| 1|pen |somethi |100 |10|
| 2|paper|something|30 |11|
+---------------------------+
Categories:
+----------------------------+
|id | name |parent_id |
|____________________________|
|1 | granny | 0 |
|2 | dad | 1 |
|3 | grandson | 2 |
|4 | grandson 2| 2 |
+----------------------------+
Products2categories:
+-------------------------------+
| id | product_id | category_id|
|_______________________________|
| 1 | 1 | 3 |
| 2 | 1 | 2 |
| 3 | 1 | 1 |
+-------------------------------+
I want to do a query that will return all of the categories that some product is related to.
for example:
When I supply a product ID of 1 I would like to get as a result the result
grandson,dad,granny (name of the categories that this product is related to)
This is my attempt:
SELECT `categories`.`name`
FROM `categories`
JOIN (
SELECT `products2categories`.`category_id`,`products2categories`.`product_id`
FROM `products2categories` a
JOIN `products`
ON `products`.`id` = `products2categories`.`product_id`
)
ON `categories`.`id` = `products2categories`.`category_id`
I've got the following error:
Every derived table must have its own alias
I would like to get some help here :)
Thanks in advance!
Unless I'm misunderstanding your statement, I believe you're overcomplicating things. Based on your verbal description of what you want (and ignoring your code), this will do what you're asking:
SELECT name FROM categories WHERE id IN (SELECT id FROM products2categories WHERE product_id = 1);
EDIT: HERE IS A COMPLETE TEST
CREATE TABLE `categories` (
`id` int(11) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`parent_id` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `categories` VALUES (1,'granny',0),(2,'dad',1),(3,'grandson',2),(4,'grandson 2',2);
CREATE TABLE `products` (
`id` int(11) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`details` varchar(255) DEFAULT NULL,
`price` int(11) DEFAULT NULL,
`cn` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `products` VALUES (1,'pen','somethi',100,10),(2,'paper','something',30,11);
CREATE TABLE `products2categories` (
`id` int(11) DEFAULT NULL,
`product_id` int(11) DEFAULT NULL,
`category_id` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `products2categories` VALUES (1,1,3),(2,1,2),(3,1,1);
mysql> SELECT name FROM categories WHERE id IN (SELECT id FROM products2categories WHERE product_id = 1);
+----------+
| name |
+----------+
| granny |
| dad |
| grandson |
+----------+
3 rows in set (0.00 sec)
Well, I've just found out that I was doing it way too hard that it actually is.
For who-ever will come across it somewhen in the future, here is the solution:
SELECT `categories`.`name`
FROM `categories`
JOIN `products2categories`
ON `categories`.`id` = `products2categories`.`category_id`
WHERE `products2categories`.`product_id` = 1
Example table content
'main'
| id | total |
| 1 | 10 |
| 2 | 20 |
'timed'
| id | id_main | date_from | date_to | total |
| 1 | 2 | 2012-03-29 | 2012-04-29 | 50 |
Desired result
| id | total |
| 1 | 10 |
| 2 | 50 |
Not exactly working query
SELECT main.id AS id, COALESCE(timed.total, main.total) AS total
FROM main
LEFT JOIN timed
ON main.id = timed.id_main
WHERE SYSDATE() BETWEEN timed.date_from AND timed.date_to
Result
| id | total |
| 2 | 50 |
In tables 'main' and 'timed' 'total' field will never be NULL.
In some 'timed' records there will be no relative 'id_main', or there will be few, but they will differ, 'date_from' 'date_to' never intersect.
Table 'main' is large, but in 'timed' will always be two or three relative records.
CREATE TABLE `main` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`total` decimal(10,2) unsigned NOT NULL DEFAULT '0.00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
INSERT INTO `main` VALUES (1,10);
INSERT INTO `main` VALUES (2,20);
CREATE TABLE `timed` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`id_main` int(11) unsigned NOT NULL DEFAULT '0',
`date_from` date DEFAULT NULL,
`date_to` date DEFAULT NULL,
`total` decimal(10,2) unsigned NOT NULL DEFAULT '0.00',
PRIMARY KEY (`id`),
KEY `link` (`id_main`)
) ENGINE=InnoDB;
INSERT INTO `timed` VALUES (1,2,'2012-03-29','2012-03-30',50);
ALTER TABLE `timed`
ADD CONSTRAINT `link` FOREIGN KEY (`id_main`)
REFERENCES `main` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
Sorry for my english.
You should move the date condition in the join condition:
SELECT main.id AS id, COALESCE(timed.total, main.total) AS total
FROM main
LEFT JOIN timed
ON main.id = timed.id_main and SYSDATE() BETWEEN timed.date_from AND timed.date_to
In your query, those rows not matched are filtered out by the WHERE condition because timed.date_form and timed.date_to are null, so sysdate can't be between them :)