I've got a short question with a many-to-many relationship.
Here are my tables:
+---------+----------------+
| film_id | title |
+---------+----------------+
| 1 | Apocalypse Now |
+---------+----------------+
+-------------+----------------------+
| category_id | name |
+-------------+----------------------+
| 1 | cool category |
+-------------+----------------------+
| 2 | not so cool category |
+-------------+----------------------+
+---------+-------------+
| film_id | category_id |
+---------+-------------+
| 1 | 1 |
+---------+-------------+
| 1 | 2 |
+---------+-------------+
As you can see there are FKs from the film- and category-table in the film_category-table.
What I want: If one wants to delete a category, then ALL entries in the film_category where the category_id occurs should be deleted, too. BUT not the related films! In addition to that: This should work while deleting a film vice versa! (That's exactly my problem here)
My Question: Can I solve the issue with a FK-definition only or do I have to delete all entries in the film_category-table manually before deleting a film or category?
Your tables should look something like the following. A delete in the film table should delete the rows where the film category has the same id, and vice versa with the film table.
CREATE TABLE `film` (
`film_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`film_id`)
) ENGINE=InnoDB;
CREATE TABLE `category` (
`category_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`category_id`)
) ENGINE=InnoDB;
CREATE TABLE `film_category` (
`film_category_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`film_id` int(10) unsigned NOT NULL DEFAULT '0',
`category_id` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`film_category_id`),
KEY `film_id` (`film_id`),
KEY `category_id` (`category_id`),
CONSTRAINT `film_category_ibfk_2` FOREIGN KEY (`category_id`) REFERENCES `category` (`category_id`) ON DELETE CASCADE,
CONSTRAINT `film_category_ibfk_1` FOREIGN KEY (`film_id`) REFERENCES `film` (`film_id`) ON DELETE CASCADE
) ENGINE=InnoDB;
You should also be able to use the following for film_category:
CREATE TABLE `film_category` (
`film_id` int(10) unsigned NOT NULL DEFAULT '0',
`category_id` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`film_id`,`category_id`),
CONSTRAINT `film_category_ibfk_1` FOREIGN KEY (`film_id`) REFERENCES `film` (`film_id`) ON DELETE CASCADE,
CONSTRAINT `film_category_ibfk_2` FOREIGN KEY (`category_id`) REFERENCES `category` (`category_id`) ON DELETE CASCADE
) ENGINE=InnoDB;
Related
looking for help to speed up this query:
SELECT tickets.ticketid, datenew, products.name
FROM tickets
INNER JOIN ticketlines ON tickets.id = ticketlines.ticket
INNER JOIN products ON ticketlines.product = products.id
INNER JOIN receipts ON receipts.id = tickets.id
WHERE (category !='feb765ef-c8a8-4fa2-969c-90f67fe6b3be' AND category!='888f4893-f300-43b5-9933-d549ade744e0' AND category !='8f2031e8-64a4-4abf-8175-3d2bedd9f950' AND category !='ca370ced-1c3b-434c-905e-ec1bc709543b' AND category !='f92ff0ac-fa11-4a5f-a3dd-e0d9ed9c171a' AND category !='445e8605-1cd9-4714-b3fd-7389ac29c206' and category !='05143c54-8a7e-4ce2-97cc-f84f9cf41395' AND category !='8c78afea-b9e2-44cf-b497-c384045b3202' AND category !='95919f7f-ff2e-4aa1-8110-ef63c022c01b' AND category !='f4f88b05-38a1-4956-9182-4c04a0808df7') AND datedone IS NULL
ORDER BY ticketid
This is from a cash register database that I am using to pull data for order display. The structure is the receipt has the primary id, the timein and timedone(DATEDONE) timestamps. The ticket table has ticket id which is the same as receipt id. Ticketlines table is the line items on the receipt/ticket. And then Products table has the human readable definitions of the products.
The query is to pull all items that aren't completed, i.e. DATEDONE is null, and display the items that were ordered.
Ticket and Receipt tables have 15K rows, Ticketlines has ~20K rows, Products has 1.5k.
Pretty small data. But this query takes over 20 seconds. I think since I am using primary key IDs for everything, I don't need to index anything, but I am a total noob, so I'm happy to be told I'm wrong. I'll appreciate any help and can provide any further details. Thanks!
EDIT:
per comments, I am showing table structures. I'm sorry for the formatting nightmare, I'm unfamiliar with this platform and unsure how to make it more readable :-(
| receipts | CREATE TABLE `receipts` (
`ID` varchar(255) NOT NULL,
`MONEY` varchar(255) NOT NULL,
`DATENEW` datetime NOT NULL,
`ATTRIBUTES` mediumblob,
`PERSON` varchar(255) DEFAULT NULL,
`DATEDONE` datetime DEFAULT NULL,
PRIMARY KEY (`ID`),
KEY `RECEIPTS_FK_MONEY` (`MONEY`),
KEY `RECEIPTS_INX_1` (`DATENEW`),
CONSTRAINT `RECEIPTS_FK_MONEY` FOREIGN KEY (`MONEY`) REFERENCES `closedcash` (`MONEY`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
| tickets | CREATE TABLE `tickets` (
`ID` varchar(255) NOT NULL,
`TICKETTYPE` int(11) NOT NULL DEFAULT '0',
`TICKETID` int(11) NOT NULL,
`PERSON` varchar(255) NOT NULL,
`CUSTOMER` varchar(255) DEFAULT NULL,
`STATUS` int(11) NOT NULL DEFAULT '0',
`DONE` varchar(255) DEFAULT NULL,
PRIMARY KEY (`ID`),
KEY `TICKETS_FK_2` (`PERSON`),
KEY `TICKETS_CUSTOMERS_FK` (`CUSTOMER`),
KEY `TICKETS_TICKETID` (`TICKETTYPE`,`TICKETID`),
CONSTRAINT `TICKETS_CUSTOMERS_FK` FOREIGN KEY (`CUSTOMER`) REFERENCES `customers` (`ID`),
CONSTRAINT `TICKETS_FK_2` FOREIGN KEY (`PERSON`) REFERENCES `people` (`ID`),
CONSTRAINT `TICKETS_FK_ID` FOREIGN KEY (`ID`) REFERENCES `receipts` (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
| ticketlines | CREATE TABLE `ticketlines` (
`TICKET` varchar(255) NOT NULL,
`LINE` int(11) NOT NULL,
`PRODUCT` varchar(255) DEFAULT NULL,
`ATTRIBUTESETINSTANCE_ID` varchar(255) DEFAULT NULL,
`UNITS` double NOT NULL,
`PRICE` double NOT NULL,
`TAXID` varchar(255) NOT NULL,
`ATTRIBUTES` mediumblob,
PRIMARY KEY (`TICKET`,`LINE`),
KEY `TICKETLINES_FK_2` (`PRODUCT`),
KEY `TICKETLINES_ATTSETINST` (`ATTRIBUTESETINSTANCE_ID`),
KEY `TICKETLINES_FK_3` (`TAXID`),
CONSTRAINT `TICKETLINES_ATTSETINST` FOREIGN KEY (`ATTRIBUTESETINSTANCE_ID`) REFERENCES `attributesetinstance` (`ID`),
CONSTRAINT `TICKETLINES_FK_2` FOREIGN KEY (`PRODUCT`) REFERENCES `products` (`ID`),
CONSTRAINT `TICKETLINES_FK_3` FOREIGN KEY (`TAXID`) REFERENCES `taxes` (`ID`),
CONSTRAINT `TICKETLINES_FK_TICKET` FOREIGN KEY (`TICKET`) REFERENCES `tickets` (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
| products | CREATE TABLE `products` (
`ID` varchar(255) NOT NULL,
`REFERENCE` varchar(255) NOT NULL,
`CODE` varchar(255) NOT NULL,
`CODETYPE` varchar(255) DEFAULT NULL,
`NAME` varchar(255) NOT NULL,
`PRICEBUY` double NOT NULL,
`PRICESELL` double NOT NULL,
`CATEGORY` varchar(255) NOT NULL,
`TAXCAT` varchar(255) NOT NULL,
`ATTRIBUTESET_ID` varchar(255) DEFAULT NULL,
`STOCKCOST` double DEFAULT NULL,
`STOCKVOLUME` double DEFAULT NULL,
`IMAGE` mediumblob,
`ISCOM` bit(1) NOT NULL DEFAULT b'0',
`ISSCALE` bit(1) NOT NULL DEFAULT b'0',
`ISKITCHEN` bit(1) NOT NULL DEFAULT b'0',
`PRINTKB` bit(1) NOT NULL DEFAULT b'0',
`SENDSTATUS` bit(1) NOT NULL DEFAULT b'0',
`ISSERVICE` bit(1) NOT NULL DEFAULT b'0',
`ATTRIBUTES` mediumblob,
`DISPLAY` varchar(255) DEFAULT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `PRODUCTS_INX_0` (`REFERENCE`),
UNIQUE KEY `PRODUCTS_INX_1` (`CODE`),
UNIQUE KEY `PRODUCTS_NAME_INX` (`NAME`),
KEY `PRODUCTS_FK_1` (`CATEGORY`),
KEY `PRODUCTS_TAXCAT_FK` (`TAXCAT`),
KEY `PRODUCTS_ATTRSET_FK` (`ATTRIBUTESET_ID`),
CONSTRAINT `PRODUCTS_ATTRSET_FK` FOREIGN KEY (`ATTRIBUTESET_ID`) REFERENCES `attributeset` (`ID`),
CONSTRAINT `PRODUCTS_FK_1` FOREIGN KEY (`CATEGORY`) REFERENCES `categories` (`ID`),
CONSTRAINT `PRODUCTS_TAXCAT_FK` FOREIGN KEY (`TAXCAT`) REFERENCES `taxcategories` (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
Also, here is EXPLAIN output:
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------+------------+--------+--------------------------+---------+---------+------------------------------------+-------+----------+----------------------------------------------+
| 1 | SIMPLE | receipts | NULL | ALL | PRIMARY | NULL | NULL | NULL | 14624 | 10.00 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | tickets | NULL | eq_ref | PRIMARY | PRIMARY | 767 | receipts.ID | 1 | 100.00 | NULL |
| 1 | SIMPLE | ticketlines | NULL | ref | PRIMARY,TICKETLINES_FK_2 | PRIMARY | 767 | receipts.ID | 1 | 100.00 | Using where |
| 1 | SIMPLE | products | NULL | eq_ref | PRIMARY,PRODUCTS_FK_1 | PRIMARY | 767 | ticketlines.PRODUCT | 1 | 97.97 | Using where |
+----+-------------+-------------+------------+--------+--------------------------+---------+---------+------------------------------------+-------+----------+----------------------------------------------+
4 rows in set, 1 warning (0.04 sec)
Try changing data types of all columns to the minimal required ones.
Add indexes on columns in WHERE.
Add index on ticketlines.ticket.
I ended up deleted 10K rows from every linked table and it queries running in .2 seconds now. I guess absent any more advice, i will be doing annual cleanup of old tickets.
Run code in http://rextester.com/OZKM95674
I am trying to come up with an fast and easy way to get the sum of quantity for all variations of an item but also include data such as all tags (comma seperated) and one of the images for an item (doesn't matter which one). There are many tables involve shown below:
Query 1: This query below gets the data I want for quantity but is missing data I need for my application (see query 2).
SELECT phppos_items.item_id,
phppos_items.name,
SUM(phppos_location_item_variations.quantity) as quantity
FROM `phppos_items`
LEFT JOIN `phppos_item_variations`
ON `phppos_item_variations`.`item_id` = `phppos_items`.`item_id`
LEFT JOIN `phppos_location_item_variations`
ON `phppos_location_item_variations`.`item_variation_id` = `phppos_item_variations`.`id`
and `phppos_location_item_variations`.`location_id` = 1
GROUP BY `phppos_items`.`item_id`
Result:
+---------+------+----------+
| item_id | name | quantity |
+---------+------+----------+
| 1 | TEST | 10 |
+---------+------+----------+
Query 2: But in the application I actually need more data about an item such as tags and one image (could be many just need one). I need the exact output as below but instead of the quantity being 60 I need it to be 20 like before. I know the reason this is happening because when I do group by; there is more than one row because there are 3 tags and 1 image.
SELECT phppos_items.item_id,
phppos_items.name, SUM(phppos_location_item_variations.quantity) as quantity,
`phppos_item_images`.`image_id` as `image_id`,
GROUP_CONCAT(DISTINCT phppos_tags.name) as tags
FROM `phppos_items`
LEFT JOIN `phppos_item_variations`
ON `phppos_item_variations`.`item_id` = `phppos_items`.`item_id`
LEFT JOIN `phppos_location_item_variations`
ON `phppos_location_item_variations`.`item_variation_id` = `phppos_item_variations`.`id`
and `phppos_location_item_variations`.`location_id` = 1
LEFT JOIN `phppos_items_tags`
ON `phppos_items_tags`.`item_id` = `phppos_items`.`item_id`
LEFT JOIN `phppos_tags`
ON `phppos_tags`.`id` = `phppos_items_tags`.`tag_id`
LEFT JOIN `phppos_item_images`
ON `phppos_items`.`item_id` = `phppos_item_images`.`item_id`
WHERE `phppos_items`.`deleted` = 0
AND `phppos_items`.`system_item` = 0
GROUP BY `phppos_items`.`item_id`
+---------+------+----------+----------+--------------------+
| item_id | name | quantity | image_id | tags |
+---------+------+----------+----------+--------------------+
| 1 | TEST | 60 | 1 | test,test 2,test 3 |
+---------+------+----------+----------+--------------------+
What is an efficient way to get all this data in one query?
Sample data:
phppos_items:
+---------+------+
| item_id | name |
+---------+------+
| 1 | TEST |
+---------+------+
phppos_item_variations:
+---------+----+
| item_id | id |
+---------+----+
| 1 | 1 |
+---------+----+
phppos_location_item_variations:
+-------------------+----------+
| item_variation_id | quantity |
+-------------------+----------+
| 1 | 10 |
+-------------------+----------+
phppos_tags:
+--------+----+
| name | id |
+--------+----+
| test | 1 |
| test 2 | 2 |
| test 3 | 3 |
+--------+----+
phppos_items_tags:
+---------+--------+
| item_id | tag_id |
+---------+--------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
+---------+--------+
phppos_item_images:
+---------+----------+
| item_id | image_id |
+---------+----------+
| 1 | 1 |
| 1 | 4 |
+---------+----------+
Tables involved
CREATE TABLE `phppos_items` (
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`item_id` int(10) NOT NULL AUTO_INCREMENT,
`deleted` int(1) NOT NULL DEFAULT '0',
`system_item` int(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`item_id`),
UNIQUE KEY `item_number` (`item_number`),
KEY `deleted` (`deleted`),
KEY `deleted_system_item` (`deleted`,`system_item`),
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
CREATE TABLE `phppos_item_variations` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`item_id` int(10) NOT NULL,
PRIMARY KEY (`id`),
KEY `phppos_item_variations_ibfk_1` (`item_id`),
CONSTRAINT `phppos_item_variations_ibfk_1` FOREIGN KEY (`item_id`) REFERENCES `phppos_items` (`item_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
CREATE TABLE `phppos_location_item_variations` (
`item_variation_id` int(10) NOT NULL,
`location_id` int(10) NOT NULL,
`quantity` int(1) DEFAULT NULL,
`reorder_level` decimal(23,10) DEFAULT NULL,
`replenish_level` decimal(23,10) DEFAULT NULL,
PRIMARY KEY (`item_variation_id`,`location_id`),
KEY `phppos_item_attribute_location_values_ibfk_2` (`location_id`),
CONSTRAINT `phppos_item_attribute_location_values_ibfk_1` FOREIGN KEY (`item_variation_id`) REFERENCES `phppos_item_variations` (`id`),
CONSTRAINT `phppos_item_attribute_location_values_ibfk_2` FOREIGN KEY (`location_id`) REFERENCES `phppos_locations` (`location_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
CREATE TABLE `phppos_items_tags` (
`item_id` int(11) NOT NULL,
`tag_id` int(11) NOT NULL,
PRIMARY KEY (`item_id`,`tag_id`),
KEY `phppos_items_tags_ibfk_2` (`tag_id`),
CONSTRAINT `phppos_items_tags_ibfk_1` FOREIGN KEY (`item_id`) REFERENCES `phppos_items` (`item_id`),
CONSTRAINT `phppos_items_tags_ibfk_2` FOREIGN KEY (`tag_id`) REFERENCES `phppos_tags` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
CREATE TABLE `phppos_tags` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ecommerce_tag_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`last_modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`deleted` int(1) NOT NULL DEFAULT '0',
`name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `tag_name` (`name`),
KEY `deleted` (`deleted`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
CREATE TABLE `phppos_item_images` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`alt_text` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`item_id` int(11) DEFAULT NULL,
`item_variation_id` int(10) DEFAULT NULL,
`ecommerce_image_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`image_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `phppos_item_images_ibfk_1` (`item_id`),
KEY `phppos_item_images_ibfk_2` (`image_id`),
KEY `phppos_item_images_ibfk_3` (`item_variation_id`),
CONSTRAINT `phppos_item_images_ibfk_1` FOREIGN KEY (`item_id`) REFERENCES `phppos_items` (`item_id`),
CONSTRAINT `phppos_item_images_ibfk_2` FOREIGN KEY (`image_id`) REFERENCES `phppos_app_files` (`file_id`),
CONSTRAINT `phppos_item_images_ibfk_3` FOREIGN KEY (`item_variation_id`) REFERENCES `phppos_item_variations` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
You said your first query already works so now you need get the remaining information
SQL DEMO
SELECT Q1.*, Q2.tags, Q3.image_id
FROM ( SELECT phppos_items.item_id,
phppos_items.name,
SUM(phppos_location_item_variations.quantity) as quantity
FROM `phppos_items`
LEFT JOIN `phppos_item_variations`
ON `phppos_item_variations`.`item_id` = `phppos_items`.`item_id`
LEFT JOIN `phppos_location_item_variations`
ON `phppos_location_item_variations`.`item_variation_id` = `phppos_item_variations`.`id`
and `phppos_location_item_variations`.`location_id` = 1
GROUP BY `phppos_items`.`item_id` ) as Q1
LEFT JOIN ( SELECT i.item_id,
GROUP_CONCAT(DISTINCT t.name) as tags
FROM phppos_items i
LEFT JOIN phppos_items_tags it
ON i.item_id = it.item_id
LEFT JOIN phppos_tags t
ON it.tag_id = t.id
GROUP BY i.item_id
) as Q2
ON Q1.item_id = Q2.item_id
LEFT JOIN ( SELECT item_id, MIN(image_id) as image_id
FROM phppos_item_images
GROUP BY item_id
) as Q3
ON Q1.item_id = Q3.item_id
OUTPUT
I am really struggling with the following two queries. I have two tables both with over a million records. The first query runs with 30 seconds and the second one runs with over 7 mins.
Basically I would like to get the count class.id and scan_layers.id based on the Lot. What would be the most effective way to do it?
select count(class.id), count(scan_layers.id), Lot, Verified from class left join scan_layers on (class.ScanLayer = scan_layers.id) group by Lot;
select count(class.id), count(distinct scan_layers.id), Lot, Verified from class left join scan_layers on (class.ScanLayer = scan_layers.id) group by Lot;
As I explain it, they both giving me the same explain.
+----+-------------+--------------------+--------+---------------+------------------------+---------+----------------------------------+---------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------------+--------+---------------+------------------------+---------+----------------------------------+---------+----------------------------------------------+
| 1 | SIMPLE | class | index | NULL | defects_scan_layers_fk | 4 | NULL | 4417159 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | scan_layers | eq_ref | PRIMARY | PRIMARY | 4 | cdb.class.ScanLayer | 1 | |
+----+-------------+--------------------+--------+---------------+------------------------+---------+----------------------------------+---------+----------------------------------------------+
CREATE TABLE `class` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`ScanLayer` int(10) unsigned NOT NULL,
`Type` enum('Regular','Critical') NOT NULL DEFAULT 'Regular',
PRIMARY KEY (`id`),
UNIQUE KEY `Defect_UNIQUE` (`ScanLayer`),
KEY `class_scan_layers_fk` (`ScanLayer`),
CONSTRAINT `class_scan_layers_fk` FOREIGN KEY (`ScanLayer`) REFERENCES `scan_layers` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB
CREATE TABLE `scan_layers` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`LayerInfo` int(10) unsigned NOT NULL,
`Lot` varchar(45) NOT NULL DEFAULT 'DEFAULT',
`PhysicalID` int(10) unsigned NOT NULL,
`Scanned` datetime DEFAULT NULL,
`ScannedMachine` int(10) unsigned DEFAULT NULL,
`DefectsCount` int(10) unsigned NOT NULL DEFAULT '0',
`MovesCount` int(10) unsigned NOT NULL DEFAULT '0',
`Verified` datetime DEFAULT NULL,
`VerifiedMachine` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ScanLayer_UNIQUE` (`LayerInfo`,`Lot`,`PhysicalID`),
KEY `scan_layers_layer_infos_fk` (`LayerInfo`),
KEY `scan_layers_scanned_machines_fk` (`ScannedMachine`),
KEY `scan_layers_verified_machines_fk` (`VerifiedMachine`),
KEY `scan_layers_verified` (`Verified`),
KEY `scan_layers_lot` (`Lot`),
CONSTRAINT `scan_layers_layer_infos_fk` FOREIGN KEY (`LayerInfo`) REFERENCES `layer_infos` (`id`) ON UPDATE CASCADE,
CONSTRAINT `scan_layers_scanned_machines_fk` FOREIGN KEY (`ScannedMachine`) REFERENCES `machines` (`id`) ON UPDATE CASCADE,
CONSTRAINT `scan_layers_verified_machines_fk` FOREIGN KEY (`VerifiedMachine`) REFERENCES `machines` (`id`) ON UPDATE CASCADE,
) ENGINE=InnoDB
Thanks a lot!
The following query is using the join buffer and I was wondering if someone could explain to me why this is so. Just trying to gain more understanding about mysql and indexing.
mysql> EXPLAIN SELECT events.event_topic_id, event_topic_name, event_topic_image, event_type_name,city_name FROM events
-> JOIN event_topic ON event_topic.event_topic_id=events.event_topic_id
-> JOIN event_type ON event_type.event_type_id = event_topic.event_type_id
-> JOIN locations ON locations.location_id=events.location_id
-> JOIN city ON city.city_id=locations.city_id
-> WHERE event_date > NOW()
-> GROUP BY events.event_topic_id, city.city_id;
+----+-------------+-------------+--------+---------------------------------------+-----------------+---------+--------------------------------------+------+----------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------+--------+---------------------------------------+-----------------+---------+--------------------------------------+------+----------+----------------------------------------------+
| 1 | SIMPLE | city | index | PRIMARY | city_name | 52 | NULL | 6 | 100.00 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | locations | ref | PRIMARY,city_id | city_id | 1 | PremiumCONNECT.city.city_id | 1 | 100.00 | Using index |
| 1 | SIMPLE | events | ref | location_id,event_topic_id,event_date | location_id | 2 | PremiumCONNECT.locations.location_id | 3 | 100.00 | Using where |
| 1 | SIMPLE | event_type | index | PRIMARY | event_type_name | 52 | NULL | 2 | 100.00 | Using index; Using join buffer |
| 1 | SIMPLE | event_topic | eq_ref | PRIMARY,event_type_id | PRIMARY | 1 | PremiumCONNECT.events.event_topic_id | 1 | 100.00 | Using where |
+----+-------------+-------------+--------+---------------------------------------+-----------------+---------+--------------------------------------+------+----------+----------------------------------------------+
Events table:
CREATE TABLE `events` (
`event_id` smallint(8) unsigned NOT NULL AUTO_INCREMENT,
`location_id` smallint(3) unsigned NOT NULL,
`event_date` datetime NOT NULL,
`event_topic_id` tinyint(3) unsigned NOT NULL,
PRIMARY KEY (`event_id`),
KEY `location_id` (`location_id`),
KEY `event_topic_id` (`event_topic_id`),
KEY `event_date` (`event_date`),
CONSTRAINT `events_ibfk_2` FOREIGN KEY (`event_topic_id`) REFERENCES `event_topic` (`event_topic_id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `events_ibfk_3` FOREIGN KEY (`location_id`) REFERENCES `locations` (`location_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=91 DEFAULT CHARSET=latin1
Event topic table:
CREATE TABLE `event_topic` (
`event_topic_id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
`event_topic_name` varchar(100) DEFAULT NULL,
`event_topic_description` text NOT NULL,
`event_topic_cost` decimal(7,2) DEFAULT NULL,
`event_type_id` tinyint(3) unsigned NOT NULL,
`event_topic_clickthrough` tinytext,
`event_topic_length` varchar(6) NOT NULL,
`event_topic_image` varchar(41) DEFAULT NULL,
`event_topic_image_md5` char(32) NOT NULL,
PRIMARY KEY (`event_topic_id`),
KEY `event_type_id` (`event_type_id`),
KEY `topic_image_sha1` (`event_topic_image_md5`),
CONSTRAINT `event_topic_ibfk_1` FOREIGN KEY (`event_type_id`) REFERENCES `event_type` (`event_type_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=latin1
Event type table:
CREATE TABLE `event_type` (
`event_type_id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
`event_type_name` varchar(50) NOT NULL,
`conf_email` text,
PRIMARY KEY (`event_type_id`),
KEY `event_type_name` (`event_type_name`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1
Locations table:
CREATE TABLE `locations` (
`location_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`location_name` varchar(50) NOT NULL,
`location_address` tinytext NOT NULL,
`location_capacity` smallint(6) NOT NULL,
`city_id` tinyint(3) unsigned NOT NULL,
`gps_coords` varchar(30) DEFAULT NULL,
PRIMARY KEY (`location_id`),
KEY `city_id` (`city_id`),
CONSTRAINT `locations_ibfk_1` FOREIGN KEY (`city_id`) REFERENCES `city` (`city_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=latin1
Cities table:
CREATE TABLE `city` (
`city_id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
`city_name` varchar(50) NOT NULL,
PRIMARY KEY (`city_id`),
UNIQUE KEY `city_name` (`city_name`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1
As it says in 'http://dev.mysql.com/doc/refman/5.1/en/explain-output.html': "Tables from earlier joins are read in portions into the join buffer, and then their rows are used from the buffer to perform the join with the current table."
So in your case, you had already joined event_topic, so the optimizer was able to use event_topic content from the join buffer.
Using a buffer is a good thing; you probably noticed the undesirable "Using temporary; Using filesort" on the first line of EXPLAIN output, which is probably from the GROUP BY and is probably unavoidable in this case.
By the way, will you run into problems with the "UNIQUE" constraint on city_name? I'm thinking of Springfield (two in New Jersey), Washington, Greenville, etc.
Try using:
"STRAIGHT_JOIN" and "FORCE INDEX":
EXPLAIN SELECT events.event_topic_id, event_topic_name, event_topic_image, event_type_name,city_name FROM events
-> straight_join event_topic force index(primary) ON event_topic.event_topic_id=events.event_topic_id
-> straight_join event_type force index(primary) ON event_type.event_type_id = event_topic.event_type_id
-> straight_join locations force index(primary) ON locations.location_id=events.location_id
-> straight_join city force index(primary) ON city.city_id=locations.city_id
-> WHERE event_date > NOW()
-> GROUP BY events.event_topic_id, city.city_id;
BTW, using a join buffer is not good. It means that you need to improve or reference to correct index.
I'm developing a application for library management.
The problem is, I have the following tables: user, employee, loan, book_loan, with them I need a query that returns me an unique
loan's ID, with the user's name, employee's name, book's name, loan's date, devolution's date and status.
I almost did this, but it returns me duplicates of the loan's ID, with the book's name separated by the rows.
These are my tables:
delimiter $$
CREATE TABLE `Loan` (
`id_loan` int(11) NOT NULL AUTO_INCREMENT,
`id_user` int(11) NOT NULL,
`id_employee` int(11) NOT NULL,
`date_loan` date NOT NULL,
`data_devolution` date NOT NULL,
`status` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`id_loan`),
KEY `fk_loan_id_user_idx` (`id_user`),
KEY `fk_loan_id_employee` (`id_employee`),
CONSTRAINT `fk_loan_id_employee` FOREIGN KEY (`id_emplyee`) REFERENCES `Employee` (`id_employee`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_loan_id_user` FOREIGN KEY (`id_user`) REFERENCES `User` (`id_user`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8$$
delimiter $$
CREATE TABLE `Employee` (
`id_employee` int(11) NOT NULL AUTO_INCREMENT,
`login` varchar(20) NOT NULL,
`password` varchar(16) NOT NULL,
`level` decimal(10,0) NOT NULL,
PRIMARY KEY (`id_employee`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8$$
delimiter $$
CREATE TABLE `Book` (
`id_book` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(50) NOT NULL,
`id_author` int(11) NOT NULL,
`edition` decimal(10,0) NOT NULL,
`editor` varchar(45) NOT NULL,
`cutter` varchar(12) NOT NULL,
`id_CDU` int(11) NOT NULL,
`status` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`id_book`),
KEY `fk_book_id_CDU_idx` (`id_CDU`),
KEY `fk_book_id_author` (`id_author`),
CONSTRAINT `fk_book_id_author` FOREIGN KEY (`id_author`) REFERENCES `Author` (`id_author`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_book_id_CDU` FOREIGN KEY (`id_CDU`) REFERENCES `CDU` (`id_CDU`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8$$
delimiter $$
CREATE TABLE `Book_Loan` (
`id_book` int(11) NOT NULL,
`id_loan` int(11) NOT NULL,
KEY `fk_book_loan_id_loan` (`id_loan`),
KEY `fk_book_loan_id_book` (`id_book`),
CONSTRAINT `fk_book_loan_id_loan` FOREIGN KEY (`id_loan`) REFERENCES `Loan` (`id_loan`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_book_loan_id_book` FOREIGN KEY (`id_book`) REFERENCES `Book` (`id_book`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8$$
delimiter $$
CREATE TABLE `User` (
`id_user` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`address` varchar(50) NOT NULL,
`email` varchar(50) NOT NULL,
PRIMARY KEY (`id_user`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8$$
This is my query:
SELECT loan.id_loan AS ID,
employee.login AS EMPLOYEE,
user.name AS USER,
book.title AS BOOK,
loan.date_loan AS LOAN,
loan.date_devolution AS DEVOLUTION,
loan.status AS STATUS
FROM Loan loan
INNER JOIN Employee employee ON employee.id_employee = loan.id_employee
INNER JOIN User user ON user.id_user = loan.id_user
INNER JOIN Book_Loan book_loan ON book_loan.id_loan = loan.id_loan
INNER JOIN Book book ON book.id_book = book_loan.id_book;
That returns me e.g.: if the loan has 3 book, which is the maximum:
+-----+-------------+-------------+---------------------------------+------------+------------+--------+
| ID | EMPLOYEE | USER | BOOK | LOAN | DEVOLUTION| STATUS |
+-----+-------------+-------------+---------------------------------+------------+------------+--------+
| 6 | RUBE | John | Book A | 2013-06-22 | 2013-06-29 | 0 |
| 6 | RUBE | John | Book B | 2013-06-22 | 2013-06-29 | 0 |
| 6 | RUBE | John | Book C | 2013-06-22 | 2013-06-29 | 0 |
+-----+-------------+-------------+---------------------------------+------------+------------+--------+
This is what I want:
+-----+-------------+-------------+---------------------------------+------------+------------+--------+
| ID | EMPLOYEE | USER | BOOK | LOAN | DEVOLUTION | STATUS |
+-----+-------------+-------------+---------------------------------+------------+------------+--------+
| 6 | RUBE | John | Book A, Book B, Book C | 2013-06-22 | 2013-06-29 | 0 |
+-----+-------------+-------------+---------------------------------+------------+------------+--------+
Look into using GROUP_CONCAT:
SELECT loan.id_loan AS ID,
employee.login AS EMPLOYEE,
user.name AS USER,
GROUP_CONCAT(book.title) AS BOOK,
loan.date_loan AS LOAN,
loan.date_devolution AS DEVOLUTION,
loan.status AS STATUS
FROM Loan loan
INNER JOIN Employee employee ON employee.id_employee = loan.id_employee
INNER JOIN User user ON user.id_user = loan.id_user
INNER JOIN Book_Loan book_loan ON book_loan.id_loan = loan.id_loan
INNER JOIN Book book ON book.id_book = book_loan.id_book
GROUP BY loan.id_loan