MySQL Query to fetch Distinct Rows with latest status - mysql

I have 3 tables, namely - areas, works and jobs.
areas works jobs
----- ----- -----
area_id work_id area_id (FK)
area_name task work_id (FK)
area_type app_area status
updated_at
I'm trying to select the total list of areas cross joined with works such that I have all the permutations for areas vs works, then have the LATEST status of that combination, if it exists. I want distinct rows for each area_id-work_id combination.
I put together the below query statement but some rows have statuses displayed as NULL when they actually exist. My guess is there's something wrong with my inner SELECT statement but try as I may, I could not get it to work, any idea what's wrong with my statement?
SELECT area_name, works.task, jobs.status
FROM areas
CROSS JOIN works ON works.work_id = works.work_id
LEFT JOIN jobs ON jobs.status = (SELECT jobs.status FROM jobs ORDER BY jobs.updated_at DESC LIMIT 1) AND
(jobs.work_id = works.work_id AND jobs.area_id = areas.area_id)
WHERE works.app_area = 'zone' AND areas.area_type = 'zone'
ORDER BY areas.area_id, works.work_id, jobs.updated_at;

Your logic for the last status should be using the date not the status. The logic looks like this:
SELECT a.area_name, w.task, j.status
FROM areas a CROSS JOIN
works w LEFT JOIN
jobs j
ON j.work_id = w.work_id AND j.area_id = a.area_id AND
j.updated_at = (SELECT MAX(j2.updated_at)
FROM jobs j2
WHERE j2.work_id = w.work_id AND j2.area_id = a.area_id
)
WHERE w.app_area = 'zone' AND a.area_type = 'zone'
ORDER BY a.area_id, w.work_id, j.updated_at;
This also fixes some other problems, such as having an ON clause with CROSS JOIN.

If you want to solve it by your own query then please replace this line in the left join sub query
SELECT j.status FROM jobs j ORDER BY jobs.updated_at DESC LIMIT 1

Using Gordon Solution, I think this is another way you can do it. you'll have to test to see which way works faster for you.
SELECT a.area_name, w.task, (SELECT MAX(j2.updated_at)
FROM jobs j2
WHERE j2.work_id = w.work_id AND j2.area_id = a.area_id
) status
FROM areas a CROSS JOIN
works w LEFT JOIN
jobs j
ON j.work_id = w.work_id AND j.area_id = a.area_id
WHERE w.app_area = 'zone' AND a.area_type = 'zone'
ORDER BY a.area_id, w.work_id, j.updated_at;

Related

MySQL group by twice and COUNT

Some sql query gives me the following result:
As you can see, it already has GROUP BY.
So what I need? I need to group it again (by treatment_name) and count rows for each group. See more details on screenshot.
Here is full query:
SELECT
treatment_summaries.*
FROM `treatment_summaries`
INNER JOIN
`treatments`
ON
`treatments`.`treatment_summary_id` = `treatment_summaries`.`id`
AND
(treatment <> '' and treatment is not null)
INNER JOIN
`treatment_reviews`
ON
`treatment_reviews`.`treatment_id` = `treatments`.`id`
INNER JOIN
`conditions_treatment_reviews`
ON
`conditions_treatment_reviews`.`treatment_review_id` = `treatment_reviews`.`id`
INNER JOIN
`conditions` ON `conditions`.`id` = `conditions_treatment_reviews`.`condition_id`
INNER JOIN `conditions_treatment_summaries` `conditions_treatment_summaries_join`
ON
`conditions_treatment_summaries_join`.`treatment_summary_id` = `treatment_summaries`.`id`
INNER JOIN `conditions` `conditions_treatment_summaries`
ON `conditions_treatment_summaries`.`id` = `conditions_treatment_summaries_join`.`condition_id`
WHERE
`conditions`.`id` = 9
AND `conditions`.`id` IN (9)
AND (latest_review_id is not null)
GROUP BY
treatment_reviews.id
ORDER BY
treatment_summaries.reviews_count desc
LIMIT 20 OFFSET 0
Maybe there is another issue, cause GROUP BY should not leave same lines (for given column), but anyway you can wrap it like this:
SELECT * FROM ( YOUR_SQL_SELECT_WITH_EVERYTHING ) GROUP BY id
So the result you get will behave as another table and you can do all operations like GROUP BY again.

MySQL join multiple tables and limit the output from "ON"

when i write a MySQL query, there occur a problem. here is my query
SELECT
SUM(view_product_count_details.view_product_count) AS count_sum,
product_details.product_name,
product_details.product_url,
product_details.product_price,
product_image_details.product_image_name,
main_category_details.main_category_url,
sub_category_details.sub_category_url
FROM
view_product_count_details
JOIN
product_details ON view_product_count_details.product_id_fk = product_details.product_id
JOIN
product_image_details ON product_image_details.product_id_fk = view_product_count_details.product_id_fk
JOIN
main_category_details ON product_details.product_main_cat_id = main_category_details.main_category_id
JOIN
sub_category_details ON product_details.product_sub_cat_id_fk = sub_category_details.sub_category_id
WHERE
view_product_count_details.view_product_status = 'active'
GROUP BY view_product_count_details.product_id_fk
ORDER BY count_sum DESC
LIMIT 4
Here I have multiple images for one product.the images are in table "product_image_details". this query returns count as the number of images, where I need the count of product viewed by people which is stored in table "view_product_count_details". when I just pick the count, i got the count as it is. but when i join the table "product_image details", result become wrong. Is there any way to do it in single query?
Please help me... Thanks in advance.... :)
You can do it by having an inline query. I am not sure how this will perform when you have more data.
SELECT table1.*,product_image_details.product_image_name FROM
(
SELECT
SUM(view_product_count_details.view_product_count) AS count_sum,
product_details.product_id,
product_details.product_name,
product_details.product_url,
product_details.product_price,
main_category_details.main_category_url,
sub_category_details.sub_category_url
FROM
view_product_count_details
JOIN
product_details ON view_product_count_details.product_id_fk = product_details.product_id
JOIN
product_image_details ON product_image_details.product_id_fk = view_product_count_details.product_id_fk
JOIN
main_category_details ON product_details.product_main_cat_id = main_category_details.main_category_id
JOIN
sub_category_details ON product_details.product_sub_cat_id_fk = sub_category_details.sub_category_id
WHERE
view_product_count_details.view_product_status = 'active'
GROUP BY view_product_count_details.product_id_fk
ORDER BY count_sum DESC
LIMIT 4
) table1
JOIN
product_image_details ON product_image_details.product_id_fk = table1.product_id
LIMIT 4

MAX() Function not working as expected

I've created sqlfiddle to try and get my head around this http://sqlfiddle.com/#!2/21e72/1
In the query, I have put a max() on the compiled_date column but the recommendation column is still coming through incorrect - I'm assuming that a select statement will need to be inserted on line 3 somehow?
I've tried the examples provided by the commenters below but I think I just need to understand this from a basic query to begin with.
As others have pointed out, the issue is that some of the select columns are neither aggregated nor used in the group by clause. Most DBMSs won't allow this at all, but MySQL is a little relaxed on some of the standards...
So, you need to first find the max(compiled_date) for each case, then find the recommendation that goes with it.
select r.case_number, r.compiled_date, r.recommendation
from reporting r
join (
SELECT case_number, max(compiled_date) as lastDate
from reporting
group by case_number
) s on r.case_number=s.case_number
and r.compiled_date=s.lastDate
Thank you for providing sqlFiddle. But only reporting data is given. we highly appreciate if you give us sample data of whole tables.
Anyway, Could you try this?
SELECT
`case`.number,
staff.staff_name AS ``case` owner`,
client.client_name,
`case`.address,
x.mx_date,
report.recommendation
FROM
`case` INNER JOIN (
SELECT case_number, MAX(compiled_date) as mx_date
FROM report
GROUP BY case_number
) x ON x.case_number = `case`.number
INNER JOIN report ON x.case_number = report.case_number AND report.compiled_date = x.mx_date
INNER JOIN client ON `case`.client_number = client.client_number
INNER JOIN staff ON `case`.staff_number = staff.staff_number
WHERE
`case`.active = 1
AND staff.staff_name = 'bob'
ORDER BY
`case`.number ASC;
Check below query:
SELECT c.number, s.staff_name AS `case owner`, cl.client_name,
c.address, MAX(r.compiled_date), r.recommendation
FROM case c
INNER JOIN (SELECT r.case_number, r.compiled_date, r.recommendation
FROM report r ORDER BY r.case_number, r.compiled_date DESC
) r ON r.case_number = c.number
INNER JOIN client cl ON c.client_number = cl.client_number
INNER JOIN staff s ON c.staff_number = s.staff_number
WHERE c.active = 1 AND s.staff_name = 'bob'
GROUP BY c.number
ORDER BY c.number ASC
SELECT
case.number,
staff.staff_name AS `case owner`,
client.client_name,
case.address,
(select MAX(compiled_date)from report where case_number=case.number),
report.recommendation
FROM
case
INNER JOIN report ON report.case_number = case.number
INNER JOIN client ON case.client_number = client.client_number
INNER JOIN staff ON case.staff_number = staff.staff_number
WHERE
case.active = 1 AND
staff.staff_name = 'bob'
GROUP BY
case.number
ORDER BY
case.number ASC
try this

MySQL Inner Join with where clause sorting and limit, subquery?

Everything in the following query results in one line for each invBlueprintTypes row with the correct information. But I'm trying to add something to it. See below the codeblock.
Select
blueprintType.typeID,
blueprintType.typeName Blueprint,
productType.typeID,
productType.typeName Item,
productType.portionSize,
blueprintType.basePrice * 0.9 As bpoPrice,
productGroup.groupName ItemGroup,
productCategory.categoryName ItemCategory,
blueprints.productionTime,
blueprints.techLevel,
blueprints.researchProductivityTime,
blueprints.researchMaterialTime,
blueprints.researchCopyTime,
blueprints.researchTechTime,
blueprints.productivityModifier,
blueprints.materialModifier,
blueprints.wasteFactor,
blueprints.maxProductionLimit,
blueprints.blueprintTypeID
From
invBlueprintTypes As blueprints
Inner Join invTypes As blueprintType On blueprints.blueprintTypeID = blueprintType.typeID
Inner Join invTypes As productType On blueprints.productTypeID = productType.typeID
Inner Join invGroups As productGroup On productType.groupID = productGroup.groupID
Inner Join invCategories As productCategory On productGroup.categoryID = productCategory.categoryID
Where
blueprints.techLevel = 1 And
blueprintType.published = 1 And
productType.marketGroupID Is Not Null And
blueprintType.basePrice > 0
So what I need to get in here is the following table with the columns below it so I can use the values timestamp and sort the entire result by profitHour
tablename: invBlueprintTypesPrices
columns: blueprintTypeID, timestamp, profitHour
I need this information with the following select in mind. Using a select to show my intention of the JOIN/in-query select or whatever that can do this.
SELECT * FROM invBlueprintTypesPrices
WHERE blueprintTypeID = blueprintType.typeID
ORDER BY timestamp DESC LIMIT 1
And I need the main row from table invBlueprintTypes to still show even if there is no result from the invBlueprintTypesPrices. The LIMIT 1 is because I want the newest row possible, but deleting the older data is not a option since history is needed.
If I've understood correctly I think I need a subquery select, but how to do that? I've tired adding the exact query that is above with a AS blueprintPrices after the query's closing ), but did not work with a error with the
WHERE blueprintTypeID = blueprintType.typeID
part being the focus of the error. I have no idea why. Anyone who can solve this?
You'll need to use a LEFT JOIN to check for NULL values in invBlueprintTypesPrices. To mimic the LIMIT 1 per TypeId, you can use the MAX() or to truly make sure you only return a single record, use a row number -- this depends on whether you can have multiple max time stamps for each type id. Assuming not, then this should be close:
Select
...
From
invBlueprintTypes As blueprints
Inner Join invTypes As blueprintType On blueprints.blueprintTypeID = blueprintType.typeID
Inner Join invTypes As productType On blueprints.productTypeID = productType.typeID
Inner Join invGroups As productGroup On productType.groupID = productGroup.groupID
Inner Join invCategories As productCategory On productGroup.categoryID = productCategory.categoryID
Left Join (
SELECT MAX(TimeStamp) MaxTime, TypeId
FROM invBlueprintTypesPrices
GROUP BY TypeId
) blueprintTypePrice On blueprints.blueprintTypeID = blueprintTypePrice.typeID
Left Join invBlueprintTypesPrices blueprintTypePrices On
blueprintTypePrice.TypeId = blueprintTypePrices.TypeId AND
blueprintTypePrice.MaxTime = blueprintTypePrices.TimeStamp
Where
blueprints.techLevel = 1 And
blueprintType.published = 1 And
productType.marketGroupID Is Not Null And
blueprintType.basePrice > 0
Order By
blueprintTypePrices.profitHour
Assuming you might have the same max time stamp with 2 different records, replace the 2 left joins above with something similar to this getting the row number:
Left Join (
SELECT #rn:=IF(#prevTypeId=TypeId,#rn+1,1) rn,
TimeStamp,
TypeId,
profitHour,
#prevTypeId:=TypeId
FROM (SELECT *
FROM invBlueprintTypesPrices
ORDER BY TypeId, TimeStamp DESC) t
JOIN (SELECT #rn:=0) t2
) blueprintTypePrices On blueprints.blueprintTypeID = blueprintTypePrices.typeID AND blueprintTypePrices.rn=1
You don't say where you are putting the subquery. If in the select clause, then you have a problem because you are returning more than one value.
You can't put this into the from clause directly, because you have a correlated subquery (not allowed).
Instead, you can put it in like this:
from . . .
(select *
from invBLueprintTypesPrices ibptp
where ibtp.timestamp = (select ibptp2.timestamp
from invBLueprintTypesPrices ibptp2
where ibptp.blueprintTypeId = ibptp2.blueprintTypeId
order by timestamp desc
limit 1
)
) ibptp
on ibptp.blueprintTypeId = blueprintType.TypeID
This identifies the most recent records for all the blueprintTypeids in the subquery. It then joins in the one that matches.

Converting subquery to joins for performance

I have taken over a big project, and as the database is becoming large, some of the code stopped working,
Here is the query to find those rendering_requests who's last rending_log is pending, sometimes there are log entries which have no status change and recorded as noaction we dont need to count them. That is what I understood from the query.
SELECT
COUNT(rr.rendering_id) AS recordCount
FROM
rendering_request rr, rendering_log rl
WHERE
rl.rendering_id = rr.rendering_id
AND rl.status = 'pending' AND
rl.log_id = (
SELECT rl1.log_id
FROM rendering_log rl1
WHERE
rl.rendering_id = rl1.rendering_id AND
rl1.status = 'pending'
AND rl1.log_id = (
SELECT rl2.log_id
FROM rendering_log rl2
WHERE rl1.rendering_id = rl2.rendering_id AND rl2.status!='noaction'
ORDER BY rl2.log_id DESC LIMIT 1
)
ORDER BY rl1.log_id DESC
LIMIT 1
)
for example
rendering_id=1 is having multiple logs
status=noaction
status=noaction
status=pending
and
rendering_id=2 is having multiple logs
status=noaction
status=assigned
status=noaction
status=pending
when we run this query it should display count=1 as only the rendering_id=1 is our desired record.
Right now this query has stopped working, and it hangs the mysql server
Not 100% sure I have got this right, but something like this. Think you still need to use a couple of subselects but (depending on the version of MySQL) doing it this way with JOINs should be a lot faster
SELECT COUNT(rr.rendering_id) AS recordCount
FROM rendering_request rr
INNER JOIN rendering_log rl
ON rl.rendering_id = rr.rendering_id
INNER JOIN (SELECT rendering_id, MAX(log_id) FROM rendering_log WHERE status = 'pending' GROUP BY rendering_id) rl1
ON rl1.rendering_id = rl.rendering_id
AND rl1.log_id = rl.log_id
INNER JOIN (SELECT rendering_id, MAX(log_id) FROM rendering_log WHERE status!='noaction' GROUP BY rendering_id) rl2
ON rl2.rendering_id = rl1.rendering_id
AND rl2.log_id = rl1.log_id
WHERE rl.status = 'pending'