How can I get the sum of a column ? - mysql

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.

Related

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.

Duplicated rows

SQL Query:
SELECT
T.*,
U.nick AS author_nick,
P.id AS post_id,
P.name AS post_name,
P.author AS post_author_id,
P.date AS post_date,
U2.nick AS post_author
FROM
zero_topics T
LEFT JOIN
zero_posts P
ON
T.id = P.topic_id
LEFT JOIN
zero_players U
ON
T.author = U.uuid
LEFT JOIN
zero_players U2
ON
P.author = U2.uuid
ORDER BY
CASE
WHEN P.date is null THEN T.date
ELSE P.date
END DESC
Output:
Topics:
Posts:
Question: Why i have duplicated topic id 22? i have in mysql two topics (id 22 and 23) and two posts(id 24 and 25). I want to see topic with last post only.
If a join produces multiple results and you want only at most one result, you have to rewrite the join and/or filtering criteria to provide that result. If you want only the latest result of all the results, it's doable and reasonably easy once you use it a few times.
select a.Data, b.Data
from Table1 a
left join Table2 b
on b.JoinValue = a.JoinValue
and b.DateField =(
select Max( DateField )
from Table2
where JoinValue = b.JoinValue );
The correlated subquery pulls out the one date that is the highest (most recent) value of all the joinable candidates. That then becomes the row that takes part in the join -- or, of course, nothing if there are no candidates at all. This is a pattern I use quite a lot.

mySQL Sub Select needed

I have three tables, libraryitems, copies and loans.
A libraryitem hasMany copies, and a copy hasMany loans.
I'm trying to get the latest loan entry for a copy only; The query below returns all loans for a given copy.
SELECT
libraryitems.title,
copies.id,
copies.qruuid,
loans.id AS loanid,
loans.status,
loans.byname,
loans.byemail,
loans.createdAt
FROM copies
INNER JOIN libraryitems ON copies.libraryitemid = libraryitems.id AND libraryitems.deletedAt IS NULL
LEFT OUTER JOIN loans ON copies.id = loans.copyid
WHERE copies.libraryitemid = 1
ORDER BY copies.id ASC, loans.createdAt DESC
I know there needs to be a sub select of some description in here, but struggling to get the correct syntax. How do I only return the latest, i.e MAX(loans.createdAt) row for each distinct copy? Just using group by copies.id returns the earliest, rather than latest entry.
Image example below:
in the subquery , getting maximum created time for a loan i.e. latest entry and joining back with loans to get other details.
SELECT
T.title,
T.id,
T.qruuid,
loans.id AS loanid,
loans.status,
loans.byname,
loans.byemail,
loans.createdAt
FROM
(
SELECT C.id, C.qruuid, L.title, MAX(LN.createdAt) as maxCreatedTime
FROM Copies C
INNER JOIN libraryitems L ON C.libraryitemid = L.id
AND L.deletedAt IS NULL
LEFT OUTER JOIN loans LN ON C.id = LN.copyid
GROUP BY C.id, C.qruuid, L.title) T
JOIN loans ON T.id = loans.copyid
AND T.maxCreatedTime = loans.createdAt
A self left join on loans table will give you latest loan of a copy, you may join the query to the other tables to fetch the desired output.
select * from loans A
left outer join loans B
on A.copyid = B.copyid and A.createdAt < B.createdAt
where B.createdAt is null;
This is your query with one simple modification -- table aliases to make it clearer.
SELECT li.title, c.id, c.qruuid,
l.id AS loanid, l.status, l.byname, l.byemail, l.createdAt
FROM copies c INNER JOIN
libraryitems li
ON c.libraryitemid = li.id AND
li.deletedAt IS NULL LEFT JOIN
loans l
ON c.id = l.copyid
WHERE c.libraryitemid = 1
ORDER BY c.id ASC, l.createdAt DESC ;
With this as a beginning let's think about what you need. You want the load with the latest createdAt date for each c.id. You can get this information with a subquery:
select l.copyid, max(createdAt)
from loans
group by l.copyId
Now, you just need to join this information back in:
SELECT li.title, c.id, c.qruuid,
l.id AS loanid, l.status, l.byname, l.byemail, l.createdAt
FROM copies c INNER JOIN
libraryitems li
ON c.libraryitemid = li.id AND
li.deletedAt IS NULL LEFT JOIN
loans l
ON c.id = l.copyid LEFT JOIN
(SELECT l.copyid, max(l.createdAt) as maxca
FROM loans
GROUP BY l.copyid
) lmax
ON l.copyId = lmax.copyId and l.createdAt = lmax.maxca
WHERE c.libraryitemid = 1
ORDER BY c.id ASC, l.createdAt DESC ;
This should give you the most recent record. And, the use of left join should keep all copies, even those that have never been leant.

Sum of unique rows of joined table

I have 3 tables. practices, adjustments and claims.
I need to select all practices with additional column adjustments.amount + count(claims.id)
Here is my query.
SELECT
practices.*,
(
SUM(adjustments.amount) + count(claims.id)
) as accurred_fees
INNER JOIN adjustments ON adjustments.practice_id = practices.id
INNER JOIN claims ON claims.practice_id = practices.id
GROUP BY practices.id
In my database I have 1 practice, 20 claims and 1 adjustment with amount equal to 2. SUM(eligible_fee_adjustments.amount) in query always returns 40, I guess it's 2 * count(claims.id), I don't know why it behaves like that. Could you please help me to find solution. Thanks in advance
You have a problem of joining along two different dimensions, so the rows are multiplying. You can solve this by pre-aggregating the data. The following will fix this particular problem:
SELECT p.*,
(SUM(a.amount) + c.cnt) as accurred_fees
FROM practices p INNER JOIN
adjustments a
ON a.practice_id = p.id INNER JOIN
(SELECT practice_id, count(*) as cnt
FROM claims c
GROUP BY practice_id
) c
ON c.practice_id = p.id
GROUP BY p.id;
You should probably preaggregate the adjustments as well:
SELECT p.*,
(a.amount + c.cnt) as accurred_fees
FROM practices p INNER JOIN
(SELECT practice_id, SUM(amount) as amount
FROM adjustments a
GROUP BY practice_id
) a
ON a.practice_id = p.id INNER JOIN
(SELECT practice_id, count(*) as cnt
FROM claims c
GROUP BY practice_id
) c
ON c.practice_id = p.id
GROUP BY p.id;
And you might want to consider LEFT OUTER JOIN rather than INNER JOIN so you get all practices, even those that might be missing adjustments or claims.

MySQL select in join clause scanning too many rows

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
:-)