I have these tables:
DROP TABLE IF EXISTS books;
CREATE TABLE `books` (
`bookId` mediumint(8) UNSIGNED NOT NULL,
`title` varchar(10) NOT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `books`
ADD PRIMARY KEY (`bookId`),
DROP TABLE IF EXISTS movements;
CREATE TABLE `movements` (
`movementId` mediumint(8) UNSIGNED NOT NULL,
`movementTypeId` tinyint(3) UNSIGNED NOT NULL,
`deletedFlag` tinyint(3) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `movements`
ADD PRIMARY KEY (`movementId`),
ADD KEY `movementId` (`movementTypeId`,`deletedFlag`) USING BTREE;
DROP TABLE IF EXISTS movements_types;
CREATE TABLE `movements_types` (
`movementTypeId` mediumint(8) UNSIGNED NOT NULL,
`title` varchar(200) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `movements_types`
ADD PRIMARY KEY (`movementTypeId`);
DROP TABLE IF EXISTS movements_books;
CREATE TABLE `movements_books` (
`movementId` mediumint(8) UNSIGNED NOT NULL,
`bookId` mediumint(8) UNSIGNED NOT NULL,
`bookSize` tinyint(3) UNSIGNED NOT NULL,
`quantity` smallint(5) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `books` (`bookId`, `title`) VALUES
(1, 'Harry Potter'),
(2, 'Mysql Join'),
(3, 'Comedy');
INSERT INTO `movements` (`movementId`, `movementTypeId`, `deletedFlag`) VALUES
(7, 1, 0),
(8, 2, 0),
(9, 2, 0);
INSERT INTO `movements_types` (`movementTypeId`, `title`) VALUES
(1, "Bought"),
(2, "Sold");
INSERT INTO `movements_books` (`movementId`, `bookId`, `bookSize`, `quantity`) VALUES
(7, 1, 1, 3),
(7, 1, 2, 3),
(7, 2, 1, 3),
(7, 2, 2, 3),
(8, 1, 1, 2),
(8, 1, 1, 2),
(9, 2, 1, 3);
bookSize is just an integer indicating the size of the book.
movements_books actually has 111824 records, movements has 4534.
deletedFlag is 1 for a deleted movement (I prefer to keep them flagged) and 0 otherwise.
I need help creating indexes and I need these results:
1) sizes sold/bought/available for each book (bookId, bookSize, booksSold, booksBought, booksBought minus booksSold)
2) books sold/bought/available for each book of all size (bookId, booksSold, booksBought, booksBought minus booksSold)
To get the one of the statistics I tried:
SELECT
books.title
movements_books_grouped.bookId,
SUM(IF(movements.movementTypeId=1, movements_books_grouped.quantity, NULL)) AS booksBought,
SUM(IF(movements.movementTypeId=2, movements_books_grouped.quantity, NULL)) AS booksSold,
(
SUM(IF(movements.movementTypeId=1, movements_books_grouped.quantity, 0))-
SUM(IF(movements.movementTypeId=2, movements_books_grouped.quantity, 0))
) AS booksAvailability,
FROM
(
SELECT bookId,quantity,movementId FROM movements_books GROUP BY bookId
) AS movements_books_grouped
JOIN movements ON movements.movementId=movements_books_grouped.movementId
JOIN books ON books.bookId=movements_books_grouped.bookId
GROUP BY movements_books_grouped.bookId
ORDER BY book.title
but it's very slow.
Edit: I needed to add another table to the example because that's the one that makes really slow the last query. I need to join to this table because I need to order the result by title.
Here's a query that can use indexes:
SELECT m.movementId
, m.movementTypeId
, m.deletedFlag
, mb.bookId
, mb.bookSize
, t.title
FROM movements m
JOIN movements_types t
ON t.movementTypeId = m.movementTypeId
JOIN movements_books mb
ON mb.movementid = m.movementid
WHERE m.deletedFlag=0;
An index on (m.movementTypeId, m.deletedFlag) may prove beneficial, but best to suck it and see.
Related
I am working on comment system, I have to count all replies of a single comment on several levels.
Like this:
Parent
->child
-> child
Parent
-> child
-> child
->child
My Sql is :
CREATE TABLE IF NOT EXISTS `comment` (
`id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'This is primary key of the table',
`parent_id` bigint(11) NOT NULL,
`content` text NOT NULL,
PRIMARY KEY (`comment_id`),
KEY `user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=unicode_ci AUTO_INCREMENT=8 ;
INSERT INTO `comments` (`id`, parent_id`, `content`) VALUES
(1, 0, 'Parent'),
(2, 1, 'child'),
(3, 2, 'child'),
(4, 3, 'child'),
(5, 1, 'child2'),
(6, 0, 'Parent2'),
(7, 6,'child of parent2');
Try below query:
select count(*)
from comments c0
join comments c1 on c0.id = c1.parentid
-- in case if child comment doesn't have any children, we still need to keep it
left join comments c2 on c1.id = c2.parentid
where c0.id = 1 --particular id for which we want to count children
i have got the below query which references couple of views 'goldedRunQueries' and 'currentGoldMarkings'. My issue seems to be from the view that is referred in the subquery - currentGoldMarkings. While execution, MySQL first materializes this subquery and then implements the where clauses of 'queryCode' and 'runId', which therefore results in execution time of more than hour as the view refers tables that has got millions of rows of data. My question is how do I enforce those two where conditions on the subquery before it materializes.
SELECT goldedRunQueries.queryCode, goldedRunQueries.runId
FROM goldedRunQueries
LEFT OUTER JOIN
( SELECT measuredRunId, queryCode, COUNT(resultId) as c
FROM currentGoldMarkings
GROUP BY measuredRunId, queryCode
) AS accuracy ON accuracy.measuredRunId = goldedRunQueries.runId
AND accuracy.queryCode = goldedRunQueries.queryCode
WHERE goldedRunQueries.queryCode IN ('CH001', 'CH002', 'CH003')
and goldedRunQueries.runid = 5000
ORDER BY goldedRunQueries.runId DESC, goldedRunQueries.queryCode;
Here are the two views. Both of these also get used in a standalone mode and so integrating any clauses into them is not possible.
CREATE VIEW currentGoldMarkings
AS
SELECT result.resultId, result.runId AS measuredRunId, result.documentId,
result.queryCode, result.queryValue AS measuredValue,
gold.queryValue AS goldValue,
CASE result.queryValue WHEN gold.queryValue THEN 1 ELSE 0 END AS correct
FROM results AS result
INNER JOIN gold ON gold.documentId = result.documentId
AND gold.queryCode = result.queryCode
WHERE gold.isCurrent = 1
CREATE VIEW goldedRunQueries
AS
SELECT runId, queryCode
FROM runQueries
WHERE EXISTS
( SELECT 1 AS Expr1
FROM runs
WHERE (runId = runQueries.runId)
AND (isManual = 0)
)
AND EXISTS
( SELECT 1 AS Expr1
FROM results
WHERE (runId = runQueries.runId)
AND (queryCode = runQueries.queryCode)
AND EXISTS
( SELECT 1 AS Expr1
FROM gold
WHERE (documentId = results.documentId)
AND (queryCode = results.queryCode)
)
)
Note: The above query reflects only a part of my actual query. There are 3 other left outer joins which are similar in nature to the above subquery which makes the problem far more worse.
EDIT: As suggested, here is the structure and some sample data for the tables
CREATE TABLE `results`(
`resultId` int auto_increment NOT NULL,
`runId` int NOT NULL,
`documentId` int NOT NULL,
`queryCode` char(5) NOT NULL,
`queryValue` char(1) NOT NULL,
`comment` varchar(255) NULL,
CONSTRAINT `PK_results` PRIMARY KEY
(
`resultId`
)
);
insert into results values (100, 242300, 'AC001', 'I', NULL)
insert into results values (100, 242300, 'AC001', 'S', NULL)
insert into results values (150, 242301, 'AC005', 'I', 'abc')
insert into results values (100, 242300, 'AC001', 'I', NULL)
insert into results values (109, 242301, 'PQ001', 'S', 'zzz')
insert into results values (400, 242400, 'DD006', 'I', NULL)
CREATE TABLE `gold`(
`goldId` int auto_increment NOT NULL,
`runDate` datetime NOT NULL,
`documentId` int NOT NULL,
`queryCode` char(5) NOT NULL,
`queryValue` char(1) NOT NULL,
`comment` varchar(255) NULL,
`isCurrent` tinyint(1) NOT NULL DEFAULT 0,
CONSTRAINT `PK_gold` PRIMARY KEY
(
`goldId`
)
);
insert into gold values ('2015-02-20 00:00:00', 138904, 'CH001', 'N', NULL, 1)
insert into gold values ('2015-05-20 00:00:00', 138904, 'CH001', 'N', 'aaa', 1)
insert into gold values ('2016-02-20 00:00:00', 138905, 'CH002', 'N', NULL, 0)
insert into gold values ('2015-12-12 00:00:00', 138804, 'CH001', 'N', 'zzzz', 1)
CREATE TABLE `runQueries`(
`runId` int NOT NULL,
`queryCode` char(5) NOT NULL,
CONSTRAINT `PK_runQueries` PRIMARY KEY
(
`runId`,
`queryCode`
)
);
insert into runQueries values (100, 'AC001')
insert into runQueries values (109, 'PQ001')
insert into runQueries values (400, 'DD006')
CREATE TABLE `runs`(
`runId` int auto_increment NOT NULL,
`runName` varchar(63) NOT NULL,
`isManual` tinyint(1) NOT NULL,
`runDate` datetime NOT NULL,
`comment` varchar(1023) NULL,
`folderName` varchar(63) NULL,
`documentSetId` int NOT NULL,
`pipelineVersion` varchar(50) NULL,
`isArchived` tinyint(1) NOT NULL DEFAULT 0,
`pipeline` varchar(50) NULL,
CONSTRAINT `PK_runs` PRIMARY KEY
(
`runId`
)
);
insert into runs values ('test1', 0, '2015-08-04 06:30:46.000000', 'zzzz', '2015-08-04_103046', 2, '2015-08-03', 0, NULL)
insert into runs values ('test2', 1, '2015-12-04 12:30:46.000000', 'zzzz', '2015-08-04_103046', 2, '2015-08-03', 0, NULL)
insert into runs values ('test3', 1, '2015-06-24 10:56:46.000000', 'zzzz', '2015-08-04_103046', 2, '2015-08-03', 0, NULL)
insert into runs values ('test4', 1, '2016-05-04 11:30:46.000000', 'zzzz', '2015-08-04_103046', 2, '2015-08-03', 0, NULL)
First, let's try to improve the performance via indexes:
results: INDEX(runId, queryCode) -- in either order
gold: INDEX(documentId, query_code, isCurrent) -- in that order
After that, update the CREATE TABLEs in the question and add the output of:
EXPLAIN EXTENDED SELECT ...;
SHOW WARNINGS;
What version are you running? You effectively have FROM ( SELECT ... ) JOIN ( SELECT ... ). Before 5.6, neither subquery had an index; with 5.6, an index is generated on the fly.
It is a shame that the query is built that way, since you know which one to use: and goldedRunQueries.runid = 5000.
Bottom Line: add the indexes; upgrade to 5.6 or 5.7; if that is not enough, then rethink the use of VIEWs.
Column B of a relation has the following list of values in the five rows of the table:
3, NULL, 2, 3, 5
Which of the following is the correct value of AVG(DISTINCT B)?
I have tried creating a relational table here with the specified rows and then executed AVG(DISTINCT A): http://ideone.com/3ItE01
CREATE TABLE A(a int(8), b int(8), c int(8), d int(8), e int(8));
INSERT INTO A VALUES (3, NULL, 2, 3, 5);
INSERT INTO A VALUES (3, NULL, 2, 3, 5);
INSERT INTO A VALUES (3, NULL, 2, 3, 5);
INSERT INTO A VALUES (3, NULL, 2, 3, 5);
INSERT INTO A VALUES (3, NULL, 2, 3, 5);
But my SQL Query of "AVG(DISTINCT A)" is not valid. I am new to SQL and looking for documentation. Any ideas?
Create table like this
CREATE TABLE `test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`value` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1;
Insert values with query
insert into `test`(`id`,`value`) values (1,'3'),(2,'2'),(3,'3'),(4,NULL),(5,'5');
now use query
SELECT AVG( DISTINCT test.value)FROM test
to calculate the average you will get the result 3.3333333333333335
I have a problem with my database and I hope you can help me with this.
I have tables like this:
categories
--------------------
|id | name | parent|
____________________
categories_x_products (m:n)
---------------------
|id | ctg_id | p_id |
---------------------
products
------------
| id, name |
------------
And my question is: "how to get all my & subcategories product count?"
For example:
categories:
id = 1, name = computers, parent = 0 (1 product)
id = 2, name = notebooks, parent = 1 (2 products)
and I want to get
computers : 3
notebooks : 2
I try this but does not working
select a.name, count(b.id)
FROM categories a
LEFT JOIN categories x ON x.id=a.parent
LEFT JOIN category_x_product b ON b.ctg_id=a.id
group by a.id
Thank you for answers.
Here is some example data:
-- Adminer 4.2.3 MySQL dump
SET NAMES utf8;
SET time_zone = '+00:00';
SET foreign_key_checks = 0;
SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
DROP TABLE IF EXISTS `categories`;
CREATE TABLE `categories` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`parent` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `categories` (`id`, `name`, `parent`) VALUES
(1, 'computers', 0),
(2, 'notebooks', 1),
(3, 'lenovo', 2);
DROP TABLE IF EXISTS `products`;
CREATE TABLE `products` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `products` (`id`, `name`) VALUES
(1, 'lenovo thinkpad 001'),
(2, 'lenovo thinkpad 002'),
(3, 'lenovo thinkpad 003'),
(4, 'lenovo thinkpad 004'),
(5, 'lenovo thinkpad 005'),
(6, 'random comp.');
DROP TABLE IF EXISTS `product_x_category`;
CREATE TABLE `product_x_category` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`product_id` int(11) DEFAULT NULL,
`category_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `product_x_category` (`id`, `product_id`, `category_id`) VALUES
(1, 1, 3),
(2, 2, 3),
(3, 3, 3),
(4, 4, 3),
(5, 5, 3),
(6, 6, 1);
-- 2016-02-23 08:16:30
I've tried to run your SQL Query (Create table and insert value).
And I run this query
SELECT a.name, COUNT( b.id )
FROM categories a
LEFT JOIN product_x_category b ON b.category_id = a.id
GROUP BY a.id
And it returns this
This is your product_x_Category table (where category_id --> 3 = lenovo and category_id --> 1 = computer)
I think the result is what you want, isn't it?
Following is a dump of the tables and data needed to answer understand the system:-
The system consists of tutors and classes.
The data in the table All_Tag_Relations stores tag relations for each tutor registered and each class created by a tutor. The tag relations are used for searching classes.
CREATE TABLE IF NOT EXISTS `Tags` (
`id_tag` int(10) unsigned NOT NULL auto_increment,
`tag` varchar(255) default NULL,
PRIMARY KEY (`id_tag`),
UNIQUE KEY `tag` (`tag`),
KEY `id_tag` (`id_tag`),
KEY `tag_2` (`tag`),
KEY `tag_3` (`tag`),
KEY `tag_4` (`tag`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `Tags` (`id_tag`, `tag`) VALUES
(1, 'Sandeepan'),
(2, 'Nath'),
(3, 'first'),
(4, 'class'),
(5, 'new'),
(6, 'Bob'),
(7, 'Cratchit');
CREATE TABLE IF NOT EXISTS `All_Tag_Relations` (
`id_tag` int(10) unsigned NOT NULL default '0',
`id_tutor` int(10) default NULL,
`id_wc` int(10) unsigned default NULL,
KEY `All_Tag_Relations_FKIndex1` (`id_tag`),
KEY `id_wc` (`id_wc`),
KEY `id_tag` (`id_tag`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `All_Tag_Relations` (`id_tag`, `id_tutor`, `id_wc`) VALUES
(1, 1, NULL),
(2, 1, NULL),
(3, 1, 1),
(4, 1, 1),
(6, 2, NULL),
(7, 2, NULL),
(5, 2, 2),
(4, 2, 2),
(8, 1, 3),
(9, 1, 3);
Following is my query:-
This query searches for "first class" (tag for first = 3 and for class = 4, in Tags table) and returns all those classes such that both the terms first and class are present in the class name.
SELECT wtagrels.id_wc,SUM(DISTINCT( wtagrels.id_tag =3)) AS
key_1_total_matches,
SUM(DISTINCT( wtagrels.id_tag =4)) AS
key_2_total_matches
FROM all_tag_relations AS wtagrels
WHERE ( wtagrels.id_tag =3
OR wtagrels.id_tag =4 )
GROUP BY wtagrels.id_wc
HAVING key_1_total_matches = 1
AND key_2_total_matches = 1
LIMIT 0, 20
And it returns the class with id_wc = 1.
But, I want the search to show all those classes such that all the search terms are present in the class name or its tutor name
So that searching "Sandeepan class" (wtagrels.id_tag = 1,4) or "Sandeepan Nath" also returns the class with id_wc=1. And Searching. Searching "Bob First" should not return any classes.
Please modify the above query or suggest a new query, if possible using MyIsam - fulltext search, but somehow help me get the result.
I think this query would help you:
SET #tag1 = 1, #tag2 = 4; -- Setting some user variables to see where the ids go. (you can put the values in the query)
SELECT wtagrels.id_wc,
SUM(DISTINCT( wtagrels.id_tag =#tag1 OR wtagrels.id_tutor =#tag1)) AS key_1_total_matches,
SUM(DISTINCT( wtagrels.id_tag =#tag2 OR wtagrels.id_tutor =#tag2)) AS key_2_total_matches
FROM all_tag_relations AS wtagrels
WHERE ( wtagrels.id_tag =#tag1 OR wtagrels.id_tag =#tag2 )
GROUP BY wtagrels.id_wc
HAVING key_1_total_matches = 1 AND key_2_total_matches = 1
LIMIT 0, 20
It returns id_wc = 1.
For (6, 3) the query returns nothing.