How to optimize the inner query mysql - mysql

insert into abc(id,item_id,item_type,l_id,c_id)
Select '',o.id,'Open',pl.id,'67' from pls pl, opens o where o.id IN
(select id from Open where not exists (select 1 from ps where type = 'Open' and item_id = opens.id)) and o.type = pl.name;
I have huge data..
Help would be appreciated!!!

For the select switching the use of IN to joins:-
SELECT DISTINCT '', o.id, 'Open', pl.id, '67'
FROM pls pl
INNER JOIN opens o ON and o.type = pl.name
INNER JOIN open op ON and o.id = op.id
LEFT OUTER JOIN ps ON ps.type = 'Open' and ps.item_id = opens.id
WHERE ps.item_id IS NULL
I have added DISTINCT in case the id on the open table is not unique. If it is unique then this can be omitted.
The LEFT OUTER JOIN checks for a matching record on the PS table, and if there is on it is returned. If not the columns from that table are returned as NULL. Then in the WHERE clause the non null ones are omitted from the results.
However for efficiency the indexes on the tables are important. Is there an index on type on the opens table? An index on id on the open table? An index covering type and item_id on the ps table?

Try that:
insert into abc(id,item_id,item_type,l_id,c_id)
Select '',o.id,'Open',pl.id,'67' from pls pl
INNER JOIN opens o ON o.type = pl.name
INNER JOIN open ON o.id = open.id
where not exists (select 1 from ps where type = 'Open' and item_id = opens.id)

Related

Database Query not pulling the full (or correct) information

I have been working on a multi-table query (something I haven't had much experience in) and at first I thought it was working perfectly fine until I noticed that half of the results had null values. I have put the query and table structures below so any help would be appreciated!
SELECT
i.name, i.material, i.price, a.str_mod, a.def_mod,
a.dex_mod, a.spd_mod, i.level_req
FROM `character` as c
LEFT JOIN item_owned as o ON c.uid = o.oid
LEFT JOIN items as i ON o.iid = i.id
LEFT JOIN armour as a ON i.id = a.aid
WHERE uid = :id AND o.equipped = 1 AND i.type = 'Armour'
Above is the query I have been running and below is the table structures
Found the solution thanks to Malfunct on discord... The query had a column typo so should have been
SELECT
i.name, i.material, i.price, a.str_mod, a.def_mod, a.dex_mod, a.spd_mod, i.level_req
FROM `character` as c
JOIN item_owned as o ON c.uid = o.oid
JOIN items as i ON o.iid = i.id
JOIN armour as a ON i.id = a.aid WHERE uid = 1
AND o.equipped = 1
AND i.type = 'Armour'

Improve MySql query left outer joins with subquery

We are maintaining a history of Content. We want to get the updated entry of each content, with create Time and update Time should be of the first entry of the Content. The query contains multiple selects and where clauses with so many left joins. The dataset is very huge, thereby query is taking more than 60 seconds to execute. Kindly help in improving the same. Query:
select * from (select * from (
SELECT c.*, initCMS.initcreatetime, initCMS.initupdatetime, user.name as partnerName, r.name as rightsName, r1.name as copyRightsName, a.name as agelimitName, ct.type as contenttypename, cat.name as categoryname, lang.name as languagename FROM ContentCMS c
left join ContentCategoryType ct on ct.id = c.contentType
left join User user on c.contentPartnerId = user.id
left join Category cat on cat.id = c.categoryId
left join Language lang on lang.id = c.languageCode
left join CopyRights r on c.rights = r.id
left join CopyRights r1 on c.copyrights = r1.id
left join Age a on c.ageLimit = a.id
left outer join (
SELECT contentId, createTime as initcreatetime, updateTime as initupdatetime from ContentCMS cms where cms.deleted='0'
) as initCMS on initCMS.contentId = c.contentId WHERE c.deleted='0' order by c.id DESC
) as temp group by contentId) as c where c.editedBy='0'
Any help would be highly appreciated. Thank you.
Just a partial eval and suggestion because your query seems non properly formed
This left join seems unuseful
FROM ContentCMS c
......
left join (
SELECT contentId
, createTime as initcreatetime
, updateTime as initupdatetime
from ContentCMS cms
where cms.deleted='0'
) as initCMS on initCMS.contentId = c.contentId
same table
the order by (without limit) in a subquery in join is unuseful because join ordered values or unordered value produce the same result
the group by contentId is strange beacuse there aren't aggregation function and the sue of group by without aggregation function is deprecated is sql
and in the most recente version for mysql is not allowed (by deafult) if you need distinct value or just a rows for each contentId you should use distinct or retrive the value in a not casual manner (the use of group by without aggregation function retrive casual value for not aggregated column .
for a partial eval your query should be refactored as
SELECT c.*
, c.initcreatetime
, c.initupdatetime
, user.name as partnerName
, r.name as rightsName
, r1.name as copyRightsName
, a.name as agelimitName
, ct.type as contenttypename
, cat.name as categoryname
, lang.name as languagename
FROM ContentCMS c
left join ContentCategoryType ct on ct.id = c.contentType
left join User user on c.contentPartnerId = user.id
left join Category cat on cat.id = c.categoryId
left join Language lang on lang.id = c.languageCode
left join CopyRights r on c.rights = r.id
left join CopyRights r1 on c.copyrights = r1.id
WHERE c.deleted='0'
) as temp
for the rest you should expiclitally select the column you effectively need add proper aggregation function for the others
Also the nested subquery just for improperly reduce the rows don't help performance ... you should also re-eval you data modelling and design.

MySql crazy join thorugh a grouping table

I have a database structure with the following setup:
po: id, stockNumber, factoryId, other columns
order: id, stockNumber, factoryId, other columns
stock_number: id, stockNumber, groupId
factory: id, name, groupId
The important part here is the stock_number/factory tables. The groupId column is just an integer and if two or more rows in the table have the same value then their stock numbers/factory are considered the same. Typically this is used for different sizes of the same product.
What I'd like to do is write a query that will join "order" to "po" through the group of stock_number and factory so I can find orders with no matching po. Also the factory has to match the same way.
I have this query if I have a specific stock number/factory in mind but I'd like to update it to query the whole orders table for me:
SELECT id
FROM order
WHERE
styleNumber IN (SELECT a.stockNumber FROM stock_number a INNER JOIN stock_number b ON a.groupId = b.groupId or a.id = b.id WHERE b.stockNumber = '123')
AND factoryId IN (SELECT a.submitter_id FROM submitter a INNER JOIN submitter b ON a.groupId = b.groupId OR a.submitter_id = b.submitter_id WHERE b.SUBMITTER_ID = 'alpha');
EDIT: I came up with this query which I think might be on the right track. It only joins in the stock number so it doesn't do factory yet. Can anyone confirm if I'm going in the correct direction:
SELECT *
FROM order o
LEFT JOIN stock_number s_o ON o.stockNumber = s_o.stockNumber
LEFT JOIN stock_number s_p ON s_o.groupId = s_p.groupId
LEFT JOIN po p ON s_p.stockNumber = p.stockNumber
WHERE p.id IS NULL;
Just join all the tables.
select o.id
FROM order AS o
JOIN stock_number AS sn ON sn.stockNumber = o.stockNumber
JOIN submitter AS su ON ON o.factoryId = su.submitter_id
You could use an anti-join pattern. In this example, it looks complicated because of the two relationship tables. But a query something like this:
SELECT o.id
, o.stockNumber
, o.factoryId
FROM `order` o
LEFT
JOIN `stock_number` s
ON s.stockNumber = o.stockNumber
LEFT
JOIN `factory` f
ON f.id = o.factoryId
AND f.groupId = s.groupId
LEFT
JOIN `po` p
ON p.stockNumber = s.stockNumber
AND p.factoryId = f.id
WHERE p.id IS NULL
The anti-join pattern is easier to visualize with a simpler example. Say you had the order table (as in your example), and an order_line table, with rows related to the order table by the order_id column.
order_line: id, order_id, othercolumns
To get order along with matching order_line rows:
SELECT o.id AS order_id
, l.id AS line_id
FROM `order` o
JOIN `order_line` l
ON l.order_id = o.id
To include rows from order that don't have any matching rows in order_line, we can use an outer join. We add the LEFT keyword:
SELECT o.id AS order_id
, l.id AS line_id
FROM `order` o
LEFT
JOIN `order_line` l
ON l.order_id = o.id
That gets all rows from order, including rows that don't have a matching row in order_line. The trick now is to exclude all the rows that have a matching row. For any rows that didn't have a match, the columns from order_line will be NULL. So we can add a test in the WHERE clause, to exclude rows that had a match.
SELECT o.id AS order_id
, l.id AS line_id
FROM `order` o
LEFT
JOIN `order_line` l
ON l.order_id = o.id
WHERE l.order_id IS NULL
That gets us rows from order that don't have a matching row in order_line.
We can use this same pattern in a more complicated query. We use outer join operations, and rows from order that don't have a matching row in po will have NULL values for the columns from po.

Deciding between two methods to check if a row exists (more subqueries VS more left joins)

I want to fetch one product information using joins from different tables. I have three additional tables (review,thread,award) and I'd like to check whether records exist relating to this specific product. If they exist, return a non-null value, otherwise null. There is a possibility that more of this type of checking will be added to the query in the future.
Which query would you prefer performance wise to test if records exists?
Using exists with multiple subqueries:
$sql = "SELECT p.product_id,p.name,m.model,m.model_id,b.brand,me.merchant,
EXISTS(SELECT 1 FROM review WHERE product_id = :id) AS has_review,
EXISTS(SELECT 1 FROM thread WHERE product_id = :id) AS has_thread,
EXISTS(SELECT 1 FROM award WHERE product_id = :id) AS has_award
FROM product p
INNER JOIN model m ON m.model_id = p.model_id
INNER JOIN brand b ON b.brand_id = m.brand_id
INNER JOIN merchant me ON me.merchant_id = m.merchant_id
WHERE p.product_id = :id
LIMIT 1";
$dbh->prepare($sql);
Using multiple left joins:
$sql = "SELECT p.product_id,p.name,m.model,m.model_id,b.brand,me.merchant,
(t.product_id is not null) AS has_thread,
(r.product_id is not null) AS has_review,
(a.product_id is not null) AS has_award
FROM product p
INNER JOIN model m ON m.model_id = p.model_id
INNER JOIN brand b ON b.brand_id = m.brand_id
INNER JOIN merchant me ON me.merchant_id = m.merchant_id
LEFT JOIN review r ON re.product_id = p.product_id
LEFT JOIN thread t ON t.product_id = p.product_id
LEFT JOIN award a ON a.product_id = a.product_id
WHERE p.product_id = :id
LIMIT 1";
The first is much preferable.
For performance, for either version, you want indexes on review(product_id), thread(product_id), and award(product_id).
Why is using EXISTS better? When no matching rows exist in the three tables, then the two versions should be equivalent (minus the typo in the last on clause on the second query). However, when rows do exist, then the second version will create cartesian products of those rows, throwing off both the results and performance.
Note: I would be inclined to write the EXISTS clause using correlated subqueries, so the parameter is only referenced once:
EXISTS (SELECT 1 FROM review r WHERE r.product_id = p.product_id) AS has_review,
EXISTS (SELECT 1 FROM thread t WHERE t.product_id = p.product_id) AS has_thread,
EXISTS (SELECT 1 FROM award a WHERE a.product_id = p.product_id) AS has_award,

Why is this query so slow and what can i do about it

I have the following SELECT UPDATE statement from MySQL
UPDATE table_Learning l
INNER JOIN (select ULN, id from table_users group by ULN having count(ULN) =1) u
ON l.ULN = u.ULN
set l.user_id=u.id
WHERE l.user_id is null
The problem is, it is so slow that it times out, and basically does not work.
I am sure it is to do with the line:
INNER JOIN (select ULN, id from table_users group by ULN having count(ULN) =1) u
and specifically because there is both a GROUP BY and a HAVING clause in this inner select, and from what I have read, because INNER JOINS are very slow with MySQL.
My overall aim is to:
Populate the userID's that are null in table_learning
To do so using the userID's in table_users
To Join on the field named ULN in both tables
To only populate the fields where the ULN is unique in table_users eg if more than one user has this ULN, then do not populate the user_id in table_learning
This is your query:
UPDATE table_Learning l INNER JOIN
(select ULN, id
from table_users
group by ULN
having count(ULN) = 1
) u
ON l.ULN = u.ULN
set l.user_id=u.id
WHERE l.user_id is null;
In MySQL, the subquery is going to be expensive. An index on table_learning(user_id) might help a bit. But filtering inside the subquery could also help:
UPDATE table_Learning l INNER JOIN
(select ULN, id
from table_users
where exists (select 1
from table_learning tl
where tl.ULN = u.uln and tl.user_id is null
)
group by ULN
having count(ULN) = 1
) u
ON l.ULN = u.ULN
set l.user_id=u.id
WHERE l.user_id is null;
For this, you want a composite index on table_learning(ULN, user_id).