Oke guys, the following has been bugging me all day:
I use the query below to select an overview of products and prices including the latest result-price based on field StartTime from another table (tresults). To do this I thought I would need a subselect in the join.
The problem is that the EXPLAIN function is telling me that MySQL is scanning ALL result rows (225000 rows) not using any index.
Is there some way I can speed this up? Preferably by adding a WHERE statement to have mysql look only at the rows with the corresponding pID's.
select p.pID, brandname, description, p.EAN, RetailPrice, LowestPrice, min(price), min(price)/lowestprice-1 as afwijking
from tproducts p
join (
select Max(tresults.StartTime) AS maxstarttime, tresults.pID
from tresults
-- maybe adding a where clause here?
group by tresults.pID
) p_max on (p_max.pID = p.pID)
join tresults res on (res.starttime = p_max.maxstarttime and p.pID = res.pID and res.websiteID = 1)
join tsupplierproducts sp on (sp.pID = p.pID AND supplierID = 1)
join tbrands b on (b.brandID = p.BrandID)
group by p.pID, brandname, description, p.EAN, RetailPrice, LowestPrice
Indexes are on all columns that are part of joins or where clauses.
Any help would be appreciated. Thanks!
From your SQL I assume that you are listing product based on 1 supplier (supplierID = 1) only.
Best practice is do your known filter at begin of sql to eliminate record, then use inner join to join other without filter table.
select p.pID, brandname, description, p.EAN, RetailPrice, LowestPrice, min(price), min(price)/lowestprice-1 as afwijking
from
(select p.pID, p.BrandID p.EAN, Max(t.StartTime) AS maxstarttime
FROM tproducts p INNER JOIN tresults t on supplierID=1 and p.pID=t.pID
group by tresults.pID
) p
inner join tresults res on (res.websiteID = 1 and p.pID = res.pID and res.starttime = p_max.maxstarttime)
inner join tsupplierproducts sp on (sp.pID = p.pID)
inner join tbrands b on (b.brandID = p.BrandID)
group by p.pID, brandname, description, p.EAN, RetailPrice, LowestPrice
from above code, I eliminate all supplierID != 1 from tproducts before join tresults.
let me know if the above sql help, and what is the EXPLAIN function result
:-)
Related
I would like to do the following and it is not working. I would like to left join the result of a subquery.
select result1.id, result.name from (select cust.id as id, cust.name as name, ss.sold_date as soldDate, pp.product_name as productName from customers cust
left join sales ss on ss.customer_id = cust.id
left join products pp on pp.id = ss.product_id) as result
left join result as result1 on result.id = result1.id
When I do this, it says table 'result' does not exist. How do I left join the result alias?
As per your comment you're trying to join the subquery result with itself.
In this particular case it does not make any sense because you'll just get the same data twice. So, using the subquery once will work
select result1.id, result.name
from (select cust.id as id, cust.name as name, ss.sold_date as soldDate, pp.product_name as productName
from customers cust
left join sales ss on ss.customer_id = cust.id
left join products pp on pp.id = ss.product_id) as result
left join result as result1 on result.id = result1.id
I general, if you need to use same sub-query twice, you may use a CTE (common-table-expression):
with sub_q as (select cust.id as id, cust.name as name, ss.sold_date as soldDate, pp.product_name as productName
from customers cust
left join sales ss on ss.customer_id = cust.id
left join products pp on pp.id = ss.product_id)
select *
from sub_q res
left join sub_q res1
on res.id = res1.id
The CTE (the "with" part of the query above) is like a variable. In "usual" programming languages variable is being used to store values, whereas in query language it's job to store queries
UPD. The OP appeared to be on mysql version prior to 8.0 and the db OP is on doesn't support CTEs
So, here you may end up using views for example
First, a script to creaate a view
create view sub_q as select cust.id as id, cust.name as name, ss.sold_date as soldDate, pp.product_name as productName
from customers cust
left join sales ss on ss.customer_id = cust.id
left join products pp on pp.id = ss.product_id;
Second, run the query
select *
from sub_q res
left join sub_q res1
on res.id = res1.id;
Alternatively you may repeat subquery twice in the select statement
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.
I'm supposed to write a query for this statement:
List the names of customers, and album titles, for cases where the customer has bought the entire album (i.e. all tracks in the album)
I know that I should use division.
Here is my answer but I get some weird syntax errors that I can't resolve.
SELECT
R1.FirstName
,R1.LastName
,R1.Title
FROM (Customer C, Invoice I, InvoiceLine IL, Track T, Album Al) AS R1
WHERE
C.CustomerId=I.CustomerId
AND I.InvoiceId=IL.InvoiceId
AND T.TrackId=IL.TrackId
AND Al.AlbumId=T.AlbumId
AND NOT EXISTS (
SELECT
R2.Title
FROM (Album Al, Track T) AS R2
WHERE
T.AlbumId=Al.AlbumId
AND R2.Title NOT IN (
SELECT R3.Title
FROM (Album Al, Track T) AS R3
WHERE
COUNT(R1.TrackId)=COUNT(R3.TrackId)
)
);
ERROR: misuse of aggregate function COUNT()
You can find the schema for the database here
You cannot alias a table list such as (Album Al, Track T) which is an out-dated syntax for (Album Al CROSS JOIN Track T). You can either alias a table, e.g. Album Al or a subquery, e.g. (SELECT * FROM Album CROSS JOIN Track) AS R2.
So first of all you should get your joins straight. I don't assume that you are being taught those old comma-separated joins, but got them from some old book or Website? Use proper explicit joins instead.
Then you cannot use WHERE COUNT(R1.TrackId) = COUNT(R3.TrackId). COUNT is an aggregate function and aggregation is done after WHERE.
As to the query: It's a good idea to compare track counts. So let's do that step by step.
Query to get the track count per album:
select albumid, count(*)
from track
group by albumid;
Query to get the track count per customer and album:
select i.customerid, t.albumid, count(distinct t.trackid)
from track t
join invoiceline il on il.trackid = t.trackid
join invoice i on i.invoiceid = il.invoiceid
group by i.customerid, t.albumid;
Complete query:
select c.firstname, c.lastname, a.title
from
(
select i.customerid, t.albumid, count(distinct t.trackid) as cnt
from track t
join invoiceline il on il.trackid = t.trackid
join invoice i on i.invoiceid = il.invoiceid
group by i.customerid, t.albumid
) bought
join
(
select albumid, count(*) as cnt
from track
group by albumid
) complete on complete.albumid = bought.albumid and complete.cnt = bought.cnt
join customer c on c.customerid = bought.customerid
join album a on a.albumid = bought.albumid;
Seems you are using count in the wrong place
use having for aggregate function
SELECT R3.Title
FROM (Album Al, Track T) AS R3
HAVING COUNT(R1.TrackId)=COUNT(R3.TrackId))
but be sure of alias because in some database the alias in not available in subquery ..
You should simplify your query. Take a look at this:
SELECT FirstName
, LastName
, Title
FROM (
SELECT C.FirstName
, C.LastName
, A.AlbumID
, A.Title
, COUNT(DISTINCT TrackID) as TracksInvoiced
FROM Customer C
INNER JOIN Invoice I
ON I.CustomerId = C.CustomerId
INNER JOIN InvoiceLine IL
ON I.InvoiceId = IL.InvoiceId
INNER JOIN Track T
ON T.TrackID = I
INNER JOIN Album A
ON A.AlbumID = T.AlbumID
GROUP BY C.FirstName, C.LastName, A.AlbumID, A.Title
) C
INNER JOIN (
SELECT AlbumID
, COUNT(TrackID) as TotalTracks
FROM Track
GROUP BY AlbumID
) A
ON C.AlbumID = A.AlbumID
AND TracksInvoiced = TotalTracks
I used two subselects, the first one counts invoiced tracks per customer and album and joins it with another subselect for each album and amount of tracks on it, only where the two counts are equal.
This one seems to be a little less complicated:
SELECT r.FirstName, r.LastName, r.Title FROM
(
SELECT C.FirstName as FirstName,
C.LastName as LastName,
A.Title as Title,
A.AlbumId as AlbumId,
COUNT(*) as count
FROM Customer C, Invoice I, InvoiceLine IL, Track T, Album A
WHERE C.CustomerId=I.CustomerId
AND I.InvoiceId = IL.InvoiceId
AND T.TrackId = IL.TrackId
AND A.AlbumId = T.AlbumId
GROUP BY C.CustomerId, A.AlbumId
) AS r
WHERE r.count IS IN
(
SELECT COUNT(*) FROM Track T
WHERE T.AlbumId = r.AlbumId
)
Tested the idea on a simpler basis and extended to your example so I don't give a guarantee that you can copy and paste and its working immediately...
I have 3 tables: activites, taks and requirements. I want to return all of the duration of all the tasks for a specific requirement. This is my query:
SELECT r.id as req_id,
r.project_id,
r.name as req_name,
r.cost,r.estimated,
p.name as project_name,
v.name AS `status` ,
t.taskid,
(SELECT SEC_TO_TIME(SUM(TIME_TO_SEC(duration)))
FROM activities a
WHERE a.taskid = t.taskid) AS worked
FROM requirements r
INNER JOIN projects p
ON p.projectid = r.project_id
INNER JOIN `values` v
ON v.id = r.r_status_id
LEFT JOIN tasks t
on t.id_requirement = r.id
WHERE 1 = 1
ORDER BY req_id desc
And this is the result :
As you can see there are 2 same req_id (48) . I want to appear one time and get the sum of the last two rows in worked. How can I manage that ?
this is the activities structure :
this is tasks structure :
and this is the requirement structure :
Include your activities table in the JOIN, GROUP by all requirement columns you need and add a sum. Since you are aggregating tasks, you cannot have taskid in the SELECT clause.
SELECT r.id as req_id,
r.project_id,
r.name as req_name,
r.cost,r.estimated,
p.name as project_name,
v.name AS `status` ,
SEC_TO_TIME(SUM(TIME_TO_SEC(a.duration)))
FROM requirements r
INNER JOIN projects p ON p.projectid = r.project_id
INNER JOIN `values` v ON v.id = r.r_status_id
LEFT JOIN tasks t ON t.id_requirement = r.id
LEFT JOIN activities a ON a.taskid=t.taskid
WHERE 1 = 1
GROUP BY r.id, r.project_id, r.name,r.cost,r.estimated,p.name, v.name
ORDER BY req_id desc
The joins in your query appear to be creating extra rows. I'm sure there is a way to fix the logic directly, possibly by pre-aggregating some results in the from clause.
Your duplicates appear to be complete duplicates (every column is exactly the same). The easy way to fix the problem is to use select distinct. So, just start your query with:
SELECT DISTINCT r.id as req_id, r.project_id, r.name as req_name,
. . .
I suspect that one of your underlying tables has duplicated rows that you are not expecting, but that is another issue.
I have a SQL query that has a subquery that has joins. I would like to rewrite the query without the subquery so that I can create a view. MySQL does not allow SELECT statements where the FROM is a subquery.
Is this possible? I've tried removing the outer select and moving the group by inside the subs query. This partially works but some of the data is incorrect.
select *
from (SELECT r.id, r.dateAdded, r.listingId, r.rating, r.username, r.valid, tbl_data.nameShort, tbl_data.desk, d.model, d.hardware, d.serial, l.appVersion, r.photoUrl, r.comment
FROM tbl_ratings r
JOIN tbl_data on r.listingId = vi_data.id
JOIN tbl_devices d on r.serial = d.serial
JOIN tbl_log l on l.serial = d.serial
ORDER BY d.serial, l.dateAdded DESC) x
group by id
order by dateAdded DESC
Thanks in advance!
Is it as simple as:
SELECT r.id, r.dateAdded, r.listingId, r.rating, r.username, r.valid,
tbl_data.nameShort, tbl_data.desk, d.model, d.hardware,
d.serial, l.appVersion, r.photoUrl, r.comment
FROM tbl_ratings r
JOIN tbl_data on r.listingId = vi_data.id
JOIN tbl_devices d on r.serial = d.serial
JOIN tbl_log l on l.serial = d.serial
GROUP BY r.id
ORDER BY r.dateAdded DESC
Also, you have a reference to "vi_data" that isn't anywhere else in the query
Change your group by clause to be group by r.id. Since you're selecting from a derived table (the subquery), the db can't tell that there's only one "id" field in that derived table - it only sees the column headers as specified in the subquery, which is r.id.