OK, so I'm facing this extremely complicated issue and since I'm not a guru with MySQL I'd definitely need your input on that.
Let's say we've got a database, created using the code below (I'm pasting the creation code - of just the absolutely-necessary tables - to avoid pasting all the tables) :
DROP TABLE IF EXISTS `Jeweller`.`Orders`;
CREATE TABLE `Jeweller`.`Orders` (
`id` int(11) unsigned NOT NULL,
`date` date DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `Jeweller`.`Product_categories`;
CREATE TABLE `Jeweller`.`Product_categories` (
`id` int(11) unsigned NOT NULL,
`name` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `Jeweller`.`Product_orders`;
CREATE TABLE `Jeweller`.`Product_orders` (
`order_id` int(11) unsigned NOT NULL,
`product_id` int(11) unsigned NOT NULL,
`quantity` int(11),
`value` float,
FOREIGN KEY (`order_id`) REFERENCES `Jeweller`.`Orders`(`id`),
FOREIGN KEY (`product_id`) REFERENCES `Jeweller`.`Products`(`id`),
CHECK (`quantity`>0),
CHECK (`value`>0)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `Jeweller`.`Product_returns`;
CREATE TABLE `Jeweller`.`Product_returns` (
`sale_id` int(11) unsigned NOT NULL,
`product_id` int(11) NOT NULL,
`date` date DEFAULT NULL,
`quantity` int(11),
`value` float,
FOREIGN KEY (`sale_id`) REFERENCES `Jeweller`.`Sales`(`id`),
FOREIGN KEY (`product_id`) REFERENCES `Jeweller`.`Products`(`id`),
CHECK (`quantity`>0),
CHECK (`value`>0)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `Jeweller`.`Product_sales`;
CREATE TABLE `Jeweller`.`Product_sales` (
`sale_id` int(11) unsigned NOT NULL,
`product_id` int(11) NOT NULL,
`quantity` int(11),
`value` float,
FOREIGN KEY (`sale_id`) REFERENCES `Jeweller`.`Sales`(`id`),
FOREIGN KEY (`product_id`) REFERENCES `Jeweller`.`Products`(`id`),
CHECK (`quantity`>0),
CHECK (`value`>0)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `Jeweller`.`Products`;
CREATE TABLE `Jeweller`.`Products` (
`id` int(11) unsigned NOT NULL,
`product_category_id` int(11) NOT NULL,
`seller_id` int(11) NOT NULL,
`name` varchar(100) NOT NULL,
`description` text,
PRIMARY KEY (`id`),
FOREIGN KEY (`product_category_id`) REFERENCES `Jeweller`.`Product_categories`(`id`),
FOREIGN KEY (`seller_id`) REFERENCES `Jeweller`.`Sellers`(`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `Jeweller`.`Sales`;
CREATE TABLE `Jeweller`.`Sales` (
`id` int(11) unsigned NOT NULL,
`date` date DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Now, given that we define Profit as :
Sales - Returns - Orders
How would you go about a query to fetch :
Profit by month AND product_category, only for the year 2013.
For testing purposes, here's the full DB Creation code as well as the DB Population code (with some demo data). (SQLFiddle link)
P.S.
The actual code is kinda different (the above is just an example - though a 100% loyal one)
After several attempts I've managed to just filter 2013 sales/orders/etc... I've even managed to get Profit by product (though it took some endless joins, left outer joins, etc... lol)... However this looks much more complicated. Any ideas?
Here's an approximation of your schema...
DROP TABLE IF EXISTS orders;
CREATE TABLE orders
( order_id int(11) unsigned NOT NULL auto_increment
, date date DEFAULT NULL
, PRIMARY KEY (order_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO orders VALUES
(NULL,'2013-01-01'),
(NULL,'2013-01-01'),
(NULL,'2013-02-02'),
(NULL,'2013-02-03'),
(NULL,'2013-03-05'),
(NULL,'2013-06-07');
DROP TABLE IF EXISTS product_orders;
CREATE TABLE product_orders
( order_id int unsigned NOT NULL
, product_id int unsigned NOT NULL
, quantity int NOT NULL DEFAULT 1
, value DECIMAL(5,2)
, PRIMARY KEY(order_id,product_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO product_orders VALUES
(1,101,1,100),
(1,102,1,50),
(2,101,2,200),
(3,101,1,100),
(4,102,2,100),
(4,103,3,150),
(5,104,1,300),
(6,102,1,50),
(6,103,2,100),
(6,104,1,300);
DROP TABLE IF EXISTS product_returns;
CREATE TABLE product_returns
( sale_id int unsigned NOT NULL
, product_id int NOT NULL
, date date DEFAULT NULL
, quantity int NOT NULL DEFAULT 1
, value DECIMAL(5,2)
, PRIMARY KEY(sale_id,product_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO product_returns VALUES
(21,101,'2013-01-04',2,200),
(22,102,'2013-03-06',1,50),
(22,103,'2013-05-08',1,50),
(23,104,'2013-06-09',1,300);
DROP TABLE IF EXISTS product_sales;
CREATE TABLE product_sales
( sale_id int unsigned NOT NULL
, product_id int NOT NULL
, quantity int NOT NULL
, value DECIMAL(5,2)
, PRIMARY KEY(sale_id,product_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO product_sales VALUES
(20,101,1,100),
(20,102,1,50),
(21,101,3,300),
(22,101,1,100),
(22,102,2,100),
(22,103,1,50),
(23,103,2,100),
(23,104,2,600);
DROP TABLE IF EXISTS products;
CREATE TABLE products
( product_id int unsigned NOT NULL AUTO_INCREMENT
, product_category_id int NOT NULL
, name varchar(100) NOT NULL
, description text NULL
, PRIMARY KEY (product_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO products VALUES
(101,1,'donuts','Mmm, donuts'),
(102,2,'buzz Cola','Mmm, donuts'),
(103,2,'duff beer','Can\'t get enough'),
(104,1,'Krusty-O\'s','Yum, yum');
DROP TABLE IF EXISTS sales;
CREATE TABLE sales
( sale_id int NOT NULL
, date date DEFAULT NULL
, PRIMARY KEY (sale_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO sales VALUES
(20,'2013-01-12'),
(21,'2013-02-15'),
(22,'2013-03-17'),
(23,'2013-05-18');
...and a possible query...
SELECT p.product_category_id
, MONTH(date) month
, SUM(value) profit
FROM
( SELECT product_id,value, date
FROM product_sales ps
JOIN sales s
ON s.sale_id = ps.sale_id
UNION ALL
SELECT product_id,value*-1,date FROM product_returns
UNION ALL
SELECT product_id,value*-1,date
FROM product_orders po
JOIN orders o
ON o.order_id = po.order_id
) x
JOIN products p
ON p.product_id = x.product_id
WHERE YEAR(date) = 2013
GROUP
BY p.product_category_id
, MONTH(date);
+---------------------+-------+---------+
| product_category_id | month | profit |
+---------------------+-------+---------+
| 1 | 1 | -400.00 |
| 1 | 2 | 200.00 |
| 1 | 3 | -200.00 |
| 1 | 5 | 600.00 |
| 1 | 6 | -600.00 |
| 2 | 1 | 0.00 |
| 2 | 2 | -250.00 |
| 2 | 3 | 100.00 |
| 2 | 5 | 50.00 |
| 2 | 6 | -150.00 |
+---------------------+-------+---------+
...and an sqlfiddle of same :http://www.sqlfiddle.com/#!2/22a1d/1
In simplest terms, if restructuring is undesirable, then I would do a simple query to determine the value of orders, returns, and sales seperately and then join those together. This could be done using UNION and subqueries as in the following example : SQLFiddle
I've also taken the liberty of swapping the FLOATs for DECIMALs. There is probably room for improvement on indexes and the like but this should put you on a good track for determining the sums. If you look at the subquery you'll see that ORDER and RETURN selects are selecting a negative value as per your requirement.
One potential pitfall is that any records for which the record from Product has been deleted would not be included. This could be avoided by changing the Product joins into LEFT JOINs and handling the NULL value for product_category_id appropriately. Decided to add this into the latest example, though if the rows from Product are NEVER deleted, then INNER JOIN will suffice
SELECT
d.thisMonth,
d.product_category_id,
SUM(d.sumValue)
FROM (
(
-- Get the order value
SELECT
'order' AS valueType,
MONTH(o.date) AS thisMonth,
p.product_category_id,
SUM(-po.value * po.quantity) AS sumValue
FROM Orders o
INNER JOIN Product_orders po
ON po.order_id = o.id
LEFT JOIN Products p
ON p.id = po.product_id
WHERE o.date BETWEEN '2013-01-01' AND '2013-12-31'
GROUP BY
thisMonth,
product_category_id
) UNION ALL (
-- Get the sales value
SELECT
'sale' AS valueType,
MONTH(s.date) AS thisMonth,
p.product_category_id,
SUM(ps.value * ps.quantity) AS sumValue
FROM Sales s
INNER JOIN Product_sales ps
ON ps.sale_id = s.id
INNER JOIN Products p
ON p.id = ps.product_id
WHERE s.date BETWEEN '2013-01-01' AND '2013-12-31'
GROUP BY
thisMonth,
product_category_id
) UNION ALL (
-- Get the return value
SELECT
'return' AS valueType,
p.product_category_id,
MONTH(pr.date) AS thisMonth,
SUM(-pr.value * pr.quantity) AS sumValue
FROM Product_returns pr
INNER JOIN Products p
ON p.id = pr.product_id
WHERE pr.date BETWEEN '2013-01-01' AND '2013-12-31'
GROUP BY
thisMonth,
product_category_id
)
) d
GROUP BY
d.thisMonth,
d.product_category_id;
Related
I have a structure for a university DB wherein I have three tables: room, students, possessions
CREATE TABLE `rooms` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL COLLATE 'utf8_general_ci',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `name` (`name`) USING BTREE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=3
;
CREATE TABLE `students` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`ref_room_id` INT(10) NULL DEFAULT NULL,
`student_name` VARCHAR(50) NOT NULL COLLATE 'utf8_general_ci',
PRIMARY KEY (`id`) USING BTREE,
INDEX `FK_students_room` (`ref_room_id`) USING BTREE,
CONSTRAINT `FK_students_room` FOREIGN KEY (`ref_room_id`) REFERENCES `university`.`rooms` (`id`) ON UPDATE CASCADE ON DELETE SET NULL
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=6
;
CREATE TABLE `possessions` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`ref_student_id` INT(10) NOT NULL,
`name` VARCHAR(50) NOT NULL DEFAULT '' COLLATE 'utf8_general_ci',
PRIMARY KEY (`id`) USING BTREE,
INDEX `FK__students` (`ref_student_id`) USING BTREE,
CONSTRAINT `FK__students` FOREIGN KEY (`ref_student_id`) REFERENCES `university`.`students` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=4
;
INSERT INTO rooms (name) VALUES ('a'),('b');
INSERT INTO students (`ref_room_id`, `student_name`) VALUES
(3,1),
(3,2),
(3,3),
(3,4);
INSERT INTO possessions (ref_student_id, name) VALUES
(7,'a')
(7,'aa'),
(7,'aaa'),
(8,'aaaa'),
(9,'aaaaa'),
(6,'aaaaaa'),
(7,'aaaaaaa');
So, in order to present such a table in MySQL, I created the procedure
CREATE DEFINER=`root`#`localhost` PROCEDURE `get_data`()
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
SELECT r.id AS room_id, r.name, COUNT(s.id) AS student_num, COUNT(p.id) AS possessions_num
FROM rooms r
INNER JOIN students s ON s.ref_room_id = r.id
INNER JOIN possessions p ON p.ref_student_id = s.id
GROUP BY r.name;
END
but what I get is
First of all, it misses the room and secondly instead of 4 students it shows 7...
What am I doing wrong?
Consider the following (and note that my data set may vary slightly from yours...)
DROP TABLE IF EXISTS rooms;
CREATE TABLE `rooms` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL ,
PRIMARY KEY (`id`)
);
DROP TABLE IF EXISTS students;
CREATE TABLE `students` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`ref_room_id` INT(10) NULL DEFAULT NULL,
`student_name` VARCHAR(50) NOT NULL,
PRIMARY KEY (`id`)
);
DROP TABLE IF EXISTS possessions;
CREATE TABLE `possessions` (
`id` INT NOT NULL AUTO_INCREMENT,
`ref_student_id` INT NOT NULL,
`name` VARCHAR(50) NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO rooms VALUES (1,'a'),(2,'b');
INSERT INTO students VALUES
(11,1,1),
(12,1,2),
(13,1,3),
(14,1,4);
INSERT INTO possessions VALUES
(101,11,'apple'),
(102,11,'banana'),
(103,11,'cherry'),
(104,12,'date'),
(105,15,'elderberry'),
(106,13,'fig'),
(107,14,'huckleberry');
SELECT r.*
, COUNT(DISTINCT s.id) total_students
, COUNT(DISTINCT p.id) total_possessions
FROM rooms r
JOIN students s
ON s.ref_room_id = r.id
JOIN possessions p
ON p.ref_student_id = s.id
GROUP
BY r.id;
+----+------+----------------+-------------------+
| id | name | total_students | total_possessions |
+----+------+----------------+-------------------+
| 1 | a | 4 | 6 |
+----+------+----------------+-------------------+
Or...
SELECT r.*
, COUNT(DISTINCT s.id) total_students
, COUNT(DISTINCT p.id) total_possessions
FROM rooms r
LEFT
JOIN students s
ON s.ref_room_id = r.id
LEFT
JOIN possessions p
ON p.ref_student_id = s.id
GROUP
BY r.id;
+----+------+----------------+-------------------+
| id | name | total_students | total_possessions |
+----+------+----------------+-------------------+
| 1 | a | 4 | 6 |
| 2 | b | 0 | 0 |
+----+------+----------------+-------------------+
There are structures:
CREATE TABLE `invoices` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`date` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
INSERT INTO `invoices` VALUES (1,'2018-09-22');
CREATE TABLE `products` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`invoice_id` int(10) unsigned NOT NULL,
`amount` decimal(10,2) unsigned NOT NULL,
`quantity` smallint(5) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
INSERT INTO `products` VALUES (1,1,150.00,2),(2,1,60.00,3),(3,1,50.00,1);
CREATE TABLE `payments` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`invoice_id` int(10) unsigned NOT NULL,
`amount` decimal(10,2) unsigned NOT NULL,
`date` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
INSERT INTO `payments` VALUES (1,1,400.00,'2018-09-23'),(2,1,80.00,'2018-09-23');
I have this query:
select i.id, sum(pr.amount * pr.quantity) as productAmount,
sum(pm.amount) as paymentAmount
from invoices as i
left join products as pr on pr.invoice_id=i.id
left join payments as pm on pm.invoice_id=i.id
group by i.id
and have this result:
+----+---------------+---------------+
| id | productAmount | paymentAmount |
+----+---------------+---------------+
| 1 | 1060.00 | 1440.00 |
+----+---------------+---------------+
1 row in set (0,00 sec)
However, I want to get the following result:
+----+---------------+---------------+
| id | productAmount | paymentAmount |
+----+---------------+---------------+
| 1 | 530.00 | 480.00 |
+----+---------------+---------------+
1 row in set (0,00 sec)
I want sum amount of products and sum amount of payments grouped by invoice.id.
What should be the query in this case?
I do face this kind of queries at times. Due to multiple joins, values from a particular table get duplicated, triplicated etc. To fix this, I normally do a small hack by dividing the sum (on a particular table) by the count of distinct Id(s) from the other table. This negates the effect of multiple duplicates happening.
Try the following query:
select i.id,
(sum(pr.amount * pr.quantity) / IF(count(distinct pm.id) > 0, count(distinct pm.id), 1) as productAmount,
(sum(pm.amount) / IF(count(distinct pr.id) > 0, count(distinct pr.id), 1) as paymentAmount
from invoices as i
left join products as pr on pr.invoice_id=i.id
left join payments as pm on pm.invoice_id=i.id
group by i.id
Use the below sub query to get your expect result
SELECT id,
(select sum(pr.amount * pr.quantity) from products as pr where pr.invoice_id=i.id ) as productAmt,
(select sum(amount) from payments where invoice_id=i.id ) as PaymentAmt
FROM `invoices` i order by id asc
Try this: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=3b496214bf0ffb517dcaa9be7f0b6bb7
select a.id,pramount,sum(amount) as paymentamount from
(select i.id,sum(pr.amount *pr.quantity) as pramount
from invoices as i
join products as pr on pr.invoice_id=i.id
group by i.id)a
join payments pm on a.id=pm.invoice_id
group by a.id
I can imagine a situation where an invoice is created a products is created but no payment, similarly an invoice is created a payment is created but no products
.So you could create sub queries for the products and payments
drop table if exists i,pr,pm;
CREATE TABLE `i` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`date` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
INSERT INTO `i` VALUES (1,'2018-09-22'),(2,'2018-09-22'),(3,'2018-09-22'),(4,'2018-09-22');
CREATE TABLE `pr` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`invoice_id` int(10) unsigned NOT NULL,
`amount` decimal(10,2) unsigned NOT NULL,
`quantity` smallint(5) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
INSERT INTO `pr` VALUES (1,1,150.00,2),(2,1,60.00,3),(3,1,50.00,1),(4,3,50.00,1);
CREATE TABLE `pm` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`invoice_id` int(10) unsigned NOT NULL,
`amount` decimal(10,2) unsigned NOT NULL,
`date` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
INSERT INTO `pm` VALUES (1,1,400.00,'2018-09-23'),(2,1,80.00,'2018-09-23'),(3,4,80.00,'2018-09-23');
select i.id, (select sum(pr.amount * pr.quantity) from pr where pr.invoice_id = i.id) as productAmount,
(select sum(pm.amount) from pm where pm.invoice_id = i.id) as paymentAmount
from i
having productAmount > 0 or paymentAmount > 0;
You may (or may not want the having clause)
+----+---------------+---------------+
| id | productAmount | paymentAmount |
+----+---------------+---------------+
| 1 | 530.00 | 480.00 |
| 3 | 50.00 | NULL |
| 4 | NULL | 80.00 |
+----+---------------+---------------+
3 rows in set (0.00 sec)
Scheme
CREATE TABLE IF NOT EXISTS `content` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`entity_uid` int(11) NOT NULL,
....
PRIMARY KEY (`uid`),
UNIQUE KEY `insert_at` (`insert_at`),
KEY `fk_entity` (`entity_uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `entity` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `entity_comment` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`entity_uid` int(11) NOT NULL,
`user_uid` int(11) NOT NULL,
....
PRIMARY KEY (`uid`),
KEY `fk_entity` (`entity_uid`),
KEY `fk_user` (`user_uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `entity_like` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`entity_uid` int(11) NOT NULL,
`user_uid` int(11) NOT NULL,
....
PRIMARY KEY (`uid`),
KEY `fk_entity` (`entity_uid`),
KEY `fk_user` (`user_uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `entity_share` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`entity_uid` int(11) NOT NULL,
`user_uid` int(11) NOT NULL,
`share_type` int(2) NOT NULL,
....
PRIMARY KEY (`uid`),
KEY `fk_entity` (`entity_uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `entity_view` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`entity_uid` int(11) NOT NULL,
`user_uid` int(11) NOT NULL,
....
PRIMARY KEY (`uid`),
KEY `fk_entity` (`entity_uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `user` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(30) NOT NULL,
....
PRIMARY KEY (`uid`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query 1 - using Left Join [Query took 16.3032 sec]
SELECT c . * , COUNT(DISTINCT ev.uid) AS view_count, COUNT( DISTINCT el.uid ) AS like_count, COUNT( DISTINCT ec.uid ) AS reply_count, COUNT( DISTINCT es.uid ) AS share_count
FROM content AS c
LEFT JOIN entity_view AS ev ON ev.entity_uid = c.entity_uid
LEFT JOIN entity_like AS el ON el.entity_uid = c.entity_uid
LEFT JOIN entity_share AS es ON es.entity_uid = c.entity_uid
LEFT JOIN entity_comment AS ec ON ec.entity_uid = c.entity_uid
GROUP BY c.uid
EDIT - Explain
Query 2 - using Sub query [Query took 0.0069 sec]
SELECT c.*,
(SELECT COUNT(*) FROM entity_view WHERE entity_uid = c.entity_uid) AS view_count ,
(SELECT COUNT(*) FROM entity_like WHERE entity_uid = c.entity_uid) AS like_count ,
(SELECT COUNT(*) FROM entity_comment WHERE entity_uid = c.entity_uid) AS reply_count ,
(SELECT COUNT(*) FROM entity_share WHERE entity_uid = c.entity_uid) AS share_count
FROM content AS c
EDIT - Explain
Result
uid | data of content | view_count | like_count | reply_count | share_count |
-----------------------------------------------------------------------------
1 | ..... | 100 | 10 | 5 | 6 |
-----------------------------------------------------------------------------
2 | ..... | 200 | 20 | 20 | 3 |
-----------------------------------------------------------------------------
3 | ..... | 300 | 10 | 10 | 2 |
-----------------------------------------------------------------------------
Explain
Storage Engine : InnoDB
entity_{action} : Insert occurs when user {action} occurs.(e.g) entity_view is insertion occurs when user sees the content.
Question
How can I optimize more in the above mysql query?
I run the query in two ways and got the results above.
This proved that subquery is much better.
Is there a way to a get better performance than subquery like this table structure? And why is left join so bad?
Here's the basic query:
SELECT
some_columns
FROM
d
JOIN
m ON d.id = m.d_id
JOIN
s ON s.id = m.s_id
JOIN
p ON p.id = s.p_id
WHERE
some_criteria
ORDER BY
d.date DESC
LIMIT 25
Table m can contain multiple s_ids per each d_id. Here's a super simple example:
+--------+--------+------+
| id | d_id | s_id |
+--------+--------+------+
| 317354 | 291220 | 642 |
| 317355 | 291220 | 32 |
+--------+--------+------+
2 rows in set (0.00 sec)
Which we want. But, obviously, it's producing duplicate d records in this particular query.
These tables have lots of columns, and I need to edit these down due to the sensitive nature of the data, but here's the basic structure as it pertains to this query:
| d | CREATE TABLE `d` (
`id` bigint(22) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`),
KEY `date` (`date`)
) ENGINE=InnoDB |
| m | CREATE TABLE `m` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`d_id` bigint(20) NOT NULL,
`s_id` bigint(20) NOT NULL,
`is_king` binary(1) DEFAULT '0',
PRIMARY KEY (`id`),
KEY `d_id` (`d_id`),
KEY `is_king` (`is_king`),
KEY `s_id` (`s_id`)
) ENGINE=InnoDB |
| s | CREATE TABLE `s` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`p_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `p_id` (`p_id`)
) ENGINE=InnoDB |
| p | CREATE TABLE `p` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB |
Now, previously, we had a GROUP BY d.id in place to grab uniques. The data here are now huge, so we can no longer realistically do that. SELECT DISTINCT d.id is even slower.
Any ideas? Everything I come up with creates a problem elsewhere.
Does changing "JOIN m ON d.id = m.d_id" to "LEFT JOIN m ON d.id = m.d_id" accomplish what you're looking for here?
I'm not sure I understand your goal clearly, but "table m contains many rows per each d" immediately has me wondering if you should be using some other type of join to accomplish your ends.
I've been pulling my hair about how to write a particular view within the constraints of MySQL.
The following tables and columns are of importance:
CREATE TABLE `invoices` (
`id` int(10) unsigned NOT NULL AUTO INCREMENT,
PRIMARY KEY (`id`)
)
-- Joins payments to invoices. The sum of all `invoice_currency_value`s is the balance paid towards an invoice.
CREATE TABLE `financial_transactions_invoices` (
`id` int(10) unsigned NOT NULL AUTO INCREMENT,
`invoice` int(10) unsigned NOT NULL,
`invoice_currency_value` decimal(8,2) unsigned NOT NULL,
PRIMARY KEY (`id`)
)
-- Lists items (services) available to purchase.
CREATE TABLE `items` (
`id` int(10) unsigned NOT NULL AUTO INCREMENT,
`value` decimal(8,2) unsigned NOT NULL
PRIMARY KEY (`id`)
)
-- Each instance represents that the `item` has been purchased.
CREATE TABLE `item_instances` (
`id` int(10) unsigned NOT NULL AUTO INCREMENT,
`invoice` int(10) unsigned NOT NULL,
`item` int(10) unsigned NOT NULL,
`invoice_currency_rate` decimal(11,5) unsigned NOT NULL,
PRIMARY KEY (`id`)
)
-- Any number of tax instances can exist for an item instance and indicate this tax has been applied to the associated item instance.
CREATE TABLE `tax_instances` (
`id` int(10) unsigned NOT NULL AUTO INCREMENT,
`item_instance` int(10) unsigned NOT NULL,
`value` decimal(8,2) unsigned NOT NULL,
PRIMARY KEY (`id`)
)
Now, I need a view that lists for each row,
the invoice number
the total value of the invoice
the total tax on the invoice
and the total value of payments made towards the invoice
However, I can't figure out how to get these three separate queries into the same result set of one row per invoice, e.g.
inv_no total_value total_tax payments
1 150 5 120
2 120 10 20
3 10 0 10
4 1000 150 1150
I have written the following query which produces the desired result, but due to the 'no subquery' rule in MySQL views, it is not acceptable.
SELECT `invoice_id`, SUM(`total_value`) AS `total_value`, SUM(`total_tax`) AS `total_tax`,
SUM(`paid_balance`) AS `paid_balance`
FROM
(SELECT `invoices`.`id` AS `invoice_id`, SUM(`items`.`value` * `item_instances`.`invoice_currency_rate`) AS `total_value`,
NULL AS `total_tax`, NULL AS `paid_balance`
FROM `items`
JOIN `item_instances` ON `items`.`id` = `item_instances`.`item`
JOIN `invoices` ON `item_instances`.`invoice` = `invoices`.`id`
GROUP BY `invoices`.`id`
UNION
SELECT `invoices`.`id`, NULL, SUM(`tax_instances`.`value`), NULL
FROM `tax_instances`
JOIN `item_instances` ON `tax_instances`.`item_instance` = `item_instances`.`id`
JOIN `invoices` ON `item_instances`.`invoice` = `invoices`.`id`
GROUP BY `invoices`.`id`
UNION
SELECT `invoices`.`id`, NULL, NULL, SUM(`financial_transactions_invoices`.`invoice_currency_value`)
FROM `financial_transactions_invoices`
JOIN `invoices` ON `financial_transactions_invoices`.`invoice` = `invoices`.`id`
GROUP BY `invoices`.`id`) AS `components`
GROUP by `invoice_id`;
Without tackling the problem in the way I have, I can't think of any other way I can do it within MySQL.
Any ideas? Appreciate any help.
You could create two views. One with the UNION Subquery, and one with the outer query.