Schema:
CREATE TABLE IF NOT EXISTS `user` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`deleted` TIMESTAMP NOT NULL,
`email` VARCHAR(254) NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS `userVersion` (
`userId` BIGINT UNSIGNED,
`effective` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`name` VARCHAR(100) NOT NULL,
PRIMARY KEY (`userId`, `effective`, `created`),
FOREIGN KEY (`userId`) REFERENCES `user`(`id`)
);
The query I'm trying to perform:
SELECT u.id
FROM `user` u
INNER JOIN userVersion uv
ON u.id = uv.userId
AND uv.effective = (
SELECT MAX(uv1.effective)
FROM userVersion uv1
WHERE uv1.userId = u.id
AND uv1.effective <= NOW())
AND uv.created = (
SELECT MAX(uv2.created)
FROM userVersion uv2
WHERE uv2.userId = u.id
AND uv2.effective = uv1.effective
AND uv2.created <= NOW())
I'm getting an unknown column error of uv1.effective (situated right before the last line). I believe this query works for other databases (e.g. Oracle) but doesn't seem to work with MySQL. How could I change this query to get the same behavior?
PS: The created column is supposed to represent when the row was inserted in the database while effective is supposed to represent when that row should start being used (this allows me to add changes in the present that will work in the future).
I am trying to run below query on MYsql, The query is taking too long to run.
I need to extract for each supplier:
total_purchases
total_sales
If supplier has an opening balance different from null, use it in where clause in subqueries, else use '2020-01-01'
Here is the query I am using:
SELECT
sup.name AS supplier_name,
sup.id AS supplier_id,
sup.opening_balance_date,
sup.opening_balance AS opening_balance,
(
SELECT
IFNULL(SUM(pop.quantity * pop.cost),0) AS total_purchases
FROM purchase_order_products pop
JOIN purchase_order po ON
pop.purchase_order_id = po.id
WHERE
DATE(po.created_at) >= IFNULL(sup.opening_balance_date, '2020-01-01')
AND DATE(po.created_at) < '2020-03-01'
AND po.status = 'approved'
AND po.supplier_id = sup.id
) as total_purchases,
(
SELECT
IFNULL(sum(soi.total_cost),0) AS total_sales
FROM
sales_order_item soi
JOIN sales_order so use index (date_status_completed) ON
so.id = soi.sales_order_id
WHERE
soi.total_cost > 0
AND soi.supplier_id = sup.id
AND so.order_status = 'complete'
AND so.completed_returned = 0
AND so.desired_delivery_date >= IFNULL(sup.opening_balance_date, '2020-01-01')
) AS total_sales
FROM supplier sup
WHERE sup.is_active = 1
group by sup.id
ORDER BY sup.name;
If I run the query without the below subquery, it tooks 0.5 sec which is accepted.
(
SELECT
IFNULL(sum(soi.total_cost),0) AS total_sales
FROM
sales_order_item soi
JOIN sales_order so use index (date_status_completed) ON
so.id = soi.sales_order_id
WHERE
soi.total_cost > 0
AND soi.supplier_id = sup.id
AND so.order_status = 'complete'
AND so.completed_returned = 0
AND so.desired_delivery_date >= IFNULL(sup.opening_balance_date, '2020-01-01')
) AS total_sales
I think the problem is here: soi.supplier_id = sup.id
here is the explain
and here the database
Create Query:
--
-- Table structure for table `purchase_order`
--
CREATE TABLE `purchase_order` (
`id` int(11) NOT NULL,
`supplier_id` int(11) NOT NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`status` varchar(150) DEFAULT 'Pending',
`total` float NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Table structure for table `purchase_order_products`
--
CREATE TABLE `purchase_order_products` (
`id` int(11) NOT NULL,
`purchase_order_id` int(11) NOT NULL,
`product_id` int(11) NOT NULL,
`quantity` int(50) NOT NULL,
`cost` decimal(15,3) NOT NULL,
`reporting_quantity` int(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Table structure for table `sales_order`
--
CREATE TABLE `sales_order` (
`id` varchar(20) NOT NULL,
`order_status` varchar(50) DEFAULT NULL,
`desired_delivery_date` date DEFAULT NULL,
`completed_returned` int(1) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Table structure for table `sales_order_item`
--
CREATE TABLE `sales_order_item` (
`id` varchar(20) NOT NULL,
`sales_order_id` varchar(20) NOT NULL,
`total_cost` float NOT NULL DEFAULT '0',
`supplier_id` int(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Table structure for table `supplier`
--
CREATE TABLE `supplier` (
`id` int(11) NOT NULL,
`name` varchar(400) NOT NULL,
`opening_balance` decimal(10,2) NOT NULL DEFAULT '0.00',
`opening_balance_date` date DEFAULT NULL,
`is_active` tinyint(4) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Indexes for dumped tables
--
--
-- Indexes for table `credit_note`
--
-- Indexes for table `purchase_order`
--
ALTER TABLE `purchase_order`
ADD PRIMARY KEY (`id`),
ADD KEY `id` (`id`),
ADD KEY `supplier_id` (`supplier_id`),
ADD KEY `created_at` (`created_at`,`status`),
ADD KEY `supplier_id_2` (`supplier_id`,`created_at`),
ADD KEY `id_date_status` (`supplier_id`,`created_at`,`status`) USING BTREE;
--
-- Indexes for table `purchase_order_products`
--
ALTER TABLE `purchase_order_products`
ADD PRIMARY KEY (`id`),
ADD KEY `purchase_order_id` (`purchase_order_id`),
ADD KEY `product_id` (`product_id`),
ADD KEY `cost` (`cost`,`reporting_quantity`);
--
-- Indexes for table `sales_order`
--
ALTER TABLE `sales_order`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `idx_id` (`id`),
ADD KEY `idx_desired_delivery_date` (`desired_delivery_date`),
ADD KEY `date_status_completed` (`order_status`,`desired_delivery_date`,`completed_returned`) USING BTREE,
ADD KEY `completed_returned` (`completed_returned`),
ADD KEY `order_status` (`order_status`),
ADD KEY `order_status_2` (`order_status`,`completed_returned`);
--
-- Indexes for table `sales_order_item`
--
ALTER TABLE `sales_order_item`
ADD PRIMARY KEY (`id`),
ADD KEY `sales_order_id` (`sales_order_id`),
ADD KEY `reporting_supplier` (`supplier_id`) USING BTREE,
ADD KEY `total_cost` (`total_cost`),
ADD KEY `supplier_cost` (`supplier_id`,`total_cost`) USING BTREE,
ADD KEY `supplier_id` (`supplier_id`);
--
-- Indexes for table `supplier`
--
ALTER TABLE `supplier`
ADD PRIMARY KEY (`id`),
ADD KEY `id` (`id`),
ADD KEY `is_active` (`is_active`);
--
-- AUTO_INCREMENT for table `purchase_order`
--
ALTER TABLE `purchase_order`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=26763;
--
-- AUTO_INCREMENT for table `purchase_order_products`
--
ALTER TABLE `purchase_order_products`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=41884;
--
-- AUTO_INCREMENT for table `supplier`
--
ALTER TABLE `supplier`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=182;
A PRIMARY KEY is a unique key:
ADD PRIMARY KEY (`id`),
ADD KEY `id` (`id`), -- Toss this; it is redundant
Do not mix INT and VARCHAR:
so.id = soi.sales_order_id
Don't use DATE() when you don't need to:
DATE(po.created_at) < '2020-03-01'
-->
po.created_at < '2020-03-01'
When you have INDEX(a,b), you don't also need INDEX(a).
Some of the above may be severely impacting preformance.
Here are some composite indexes that may help:
sup: (is_active, id)
so: (completed_returned, order_status, desired_delivery_date, id)
soi: (supplier_id, total_cost, sales_order_id)
po: (supplier_id, status, created_at, id)
There's not enough information to answer this, but it looks like the USE INDEX clause you're using in the second subquery may be forcing MySQL to scan more rows than necessary. Try removing use index (date_status_completed).
A revision to the query tested on mysql version 8.0.19, assuming the purchase_order and sales_order_item tables to be sparse against each other but both (should have a FOREIGN KEY) reference the supplier table (but not necessarily any of the same records on supplier).
You can trim down the number of necessary scans by pushing down either one of purchase_order or sales_order_item into a single EXISTS subquery and then converting the other into LEFT OUTER JOINs.
The functionality of SUM() obviates the need for a null-coalesce operation for purchase_order_product.
Because sales_order.completed_returned is a BOOLEAN INT, we can combine it into the (1-so.completed_returned) to convert the total_cost of 'returned' items into harmless zeroes in the aggregation; GREATEST() likewise obviates the need for the total_cost >0 filter criteria.
Additionally, if every purchase_order supplier is also a sales_order_supplier then you can replace LEFT OUTER JOIN with INNER JOINs for a proper output.
Finally, as #RickJames noted, don't bother using DATE() here either, since the conditions already imply it.
SELECT
sup.name AS supplier_name,
sup.id AS supplier_id,
MIN(sup.opening_balance_date) AS opening_balance_date,
MIN(sup.opening_balance) AS opening_balance,
SUM( pop.quantity * pop.cost ) as total_purchases,
SUM( COALESCE( GREATEST( soi.total_cost, 0) * (1-so.completed_returned),0) ) AS total_sales
FROM (( supplier sup, purchase_order_products pop )
LEFT OUTER JOIN sales_order_item soi ON soi.supplier_id = sup.id )
LEFT OUTER JOIN sales_order so ON
so.id = soi.sales_order_id AND
so.order_status = 'complete' AND
so.desired_delivery_date >= COALESCE(sup.opening_balance_date, '2020-01-01')
WHERE sup.is_active = 1
AND EXISTS (
SELECT 1
FROM purchase_order po
WHERE
po.created_at >= COALESCE(sup.opening_balance_date, '2020-01-01')
AND po.created_at < '2020-03-01'
AND po.status = 'approved'
AND po.supplier_id = sup.id
AND pop.purchase_order_id <=> po.id
)
group by sup.id, sup.name
ORDER BY sup.name;
EXPLAIN FORMAT=TREE
-> Sort: <temporary>.name
-> Table scan on <temporary>
-> Aggregate using temporary table
-> Nested loop inner join (cost=1.75 rows=1)
-> Nested loop inner join (cost=1.40 rows=1)
-> Nested loop left join (cost=1.05 rows=1)
-> Nested loop left join (cost=0.70 rows=1)
-> Index lookup on sup using is_active (is_active=1) (cost=0.35 rows=1)
-> Index lookup on soi using reporting_supplier (supplier_id=sup.id) (cost=0.35 rows=1)
-> Filter: ((so.order_status = \'complete\') and (so.desired_delivery_date >= coalesce(sup.opening_balance_date,\'2020-01-01\'))) (cost=0.35 rows=1)
-> Single-row index lookup on so using PRIMARY (id=soi.sales_order_id) (cost=0.35 rows=1)
-> Filter: ((po.created_at >= coalesce(sup.opening_balance_date,\'2020-01-01\')) and (po.created_at < TIMESTAMP\'2020-03-01 00:00:00\') and (po. = \'approved\')) (cost=0.35 rows=1)
-> Index lookup on po using supplier_id (supplier_id=sup.id) (cost=0.35 rows=1)
-> Index lookup on pop using purchase_order_id (purchase_order_id=po.id), with index condition: (pop.purchase_order_id <=> po.id) (cost=0.35 rows=1)
See if this might work better in your use case - it's hard to know exactly how a re-work will play out without knowing the contents of all the tables involved.
A cursory look at the EXPLAIN plan indicates that further optimization might be possible by employing hints, i.e. SEMIJOIN and BNL/NO_NBL optimizations for this query in particular.
I have the following simple query that works just fine when there is one keyword to match:
SELECT gc.id, gc.name
FROM gift_card AS gc
JOIN keyword ON gc.id = keyword.gc_id
WHERE keyword = 'mini'
GROUP BY gc.id
ORDER BY id DESC
What I want to do is find the id's that match at least two of the keywords I provide. I thought just adding a simple AND would work but I get blank results.
SELECT gc.id, gc.name
FROM gift_card AS gc
JOIN keyword ON gc.id = keyword.gc_id
WHERE keyword = 'mini'
AND keyword = '2012'
GROUP BY gc.id
ORDER BY id DESC
Obviously SQL is not my strong suit so I am looking for some help one what I am doing wrong here.
Here are my table structures:
CREATE TABLE `gift_card` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=52 DEFAULT CHARSET=utf8;
CREATE TABLE `keyword` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`gc_id` int(11) NOT NULL,
`keyword` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`),
UNIQUE KEY `dupes_UNIQUE` (`gc_id`,`keyword`)
) ENGINE=InnoDB AUTO_INCREMENT=477 DEFAULT CHARSET=utf8;
No, and does not work. A column cannot have two different values in one row.
Instead, or . . . and a bit more logic:
SELECT gc.id, gc.name
FROM gift_card gc JOIN
keyword k
ON gc.id = k.gc_id
WHERE k.keyword IN ('mini', '2012')
GROUP BY gc.id
HAVING COUNT(*) = 2 -- both match
ORDER BY id DESC;
It is a good idea to qualify all column names in a query that has more than one table reference.
I have 2 tables. One that contains stock information with a timestamp, price, ticker_id.
CREATE TABLE `data_realtime` (
`id` mediumint(9) unsigned NOT NULL AUTO_INCREMENT,
`timestamp` int(10) NOT NULL,
`ticker_id` smallint(5) unsigned NOT NULL,
`price` decimal(7,2) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ticker_timestamp` (`ticker_id`,`timestamp`) USING BTREE,
CONSTRAINT `data_realtime_ibfk_2` FOREIGN KEY (`ticker_id`) REFERENCES
`tickers` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1585421 DEFAULT CHARSET=latin1
And the other contains ticker info with ticker_id and name. (However I don't think this is relevant here)
CREATE TABLE `tickers` (
`id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`ticker` text NOT NULL,
`ticker_name` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ticker` (`ticker`(5))
) ENGINE=InnoDB AUTO_INCREMENT=9416 DEFAULT CHARSET=latin1
I run a query that gets the latest rows for each ticker (using ticker_id).
SELECT ticker_id, max(timestamp) max_timestamp FROM `data_realtime` GROUP BY ticker_id
I want to then get the corresponding stock price, 3 minutes ago.
I do this by joining the initial result (latest prices) with all rows newer than 3 mins; then ordering by them by the timestamp; then grouping them by ticker_id.
This is my query:
SELECT * FROM (
SELECT
data_latest.*, data_3m.timestamp timestamp_3m, data_3m.price price_3m
FROM (
SELECT B.* FROM (
SELECT
ticker_id, max(timestamp) max_timestamp
FROM
`data_realtime`
GROUP BY ticker_id)
A
LEFT JOIN
data_realtime B
ON A.ticker_id=B.ticker_id and A.max_timestamp=B.timestamp)
data_latest
LEFT JOIN
data_realtime data_3m
ON
data_latest.timestamp <= (data_3m.timestamp + (60*3) )
AND
data_latest.timestamp>data_3m.timestamp
AND
data_latest.ticker_id=data_3m.ticker_id
ORDER BY
data_3m.timestamp ASC)
data_3m_sorted
GROUP BY ticker_id
As you can see from the Create Table, there exists a primary key on 'id' and a combined unique key on 'timestamp' and 'ticker'.
The query takes a little over 2 secs to run with about 1.5M rows in the table. Is there anything I can do to improve the query performance.
Will adding single indexes on ticker_id and timestamp column help? If so, why?
Thanks for the help.
read mysql post but couldnt find the solution.
Any pointers would be appreciated.
I have 2 tables A,B
Table A has folderid mapped to multiple feedid (1 to many )
Table B has feedid and data associated with it.
The following query gives Using index; Using temporary; Using filesort
SELECT A.feed_id,B.feed_id,B.entry_id
FROM feed_folder A
LEFT JOIN feed_data B on A.feed_id=B.feed_id
WHERE A.folder_id=29
AND B.entry_id <= 123
AND B.entry_created <= '2012-11-01 21:38:54'
ORDER by B.entry_created desc limit 0,20;
Any ideas as to how the temporary filesort can be avoided.
Following is the table structure
CREATE TABLE `feed_folder` (
`folder_id` bigint(20) unsigned NOT NULL,
`feed_id` bigint(20) unsigned NOT NULL,
UNIQUE KEY `folder_id` (`folder_id`,`feed_id`),
KEY `feed_id` (`feed_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `feed_data` (
`entry_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`feed_id` bigint(20) unsigned NOT NULL,
`entry_created` datetime NOT NULL,
`entry_object` text,
`entry_permalink` varchar(150) NOT NULL,
`entry_orig_created` datetime NOT NULL,
PRIMARY KEY (`entry_id`),
UNIQUE KEY `feed_id_2` (`feed_id`,`entry_permalink`),
KEY `entry_created` (`entry_created`),
KEY `feed_id` (`feed_id`,`entry_created`),
KEY `feed_id_entry_id` (`feed_id`,`entry_id`),
KEY `entry_permalink` (`entry_permalink`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
Try this query:
SELECT * FROM(SELECT
A.feed_id,B.feed_id,B.entry_id
FROM feed_folder as A
INNER JOIN feed_data as B on A.feed_id=B.feed_id
WHERE A.folder_id=29
AND B.entry_id <= 123
AND B.entry_created <= '2012-11-01 21:38:54')joinAndSortTable
ORDER by B.entry_created desc limit 0,20;
The problem is in alias, you are forgot to give as A and as B
i'm also remind you about LEFT JOIN and RIGHT JOIN, INNER JOIN...
To join all records what you selected, Using INNER JOIN is a workaround.
Because INNER JOIN can join all columns with full accessed.
GOOD LUCK