MySQL linked table get the last row insered with table.value = X - mysql

I current have a table project(id) and a table project_step (id, project,step(integer)).
1 project is linked to 1 or many project_step.
I want to get a list of project where the last entered project_step (project_step.id DESC) has a step = X.
I need to do this in a sub query but i don't know how to write it.

This query will return all column from both tables. The purpose of the extra subquery is to separately get the latest ID for every project on table project_step.
SELECT a.*, b.*
FROM project a
INNER JOIN project_step b
ON a.ID = b.project
INNER JOIN
(
SELECT project, MAX(ID) ID
FROM project_step
GROUP BY project
) c ON b.project = c.project AND
b.ID = c.ID
WHERE b.step = ?

Try something like this.
SELECT project.* from project_step
JOIN project ON project.id = project_step.project
GROUP BY project
HAVING SUBSTRING_INDEX(GROUP_CONCAT(step ORDER BY step DESC SEPARATOR ' '), ' ', 1) = X

Related

Joining a second table and selecting the first entry

I have been having some troubles getting my head around achieving the below.
I have an 'applications' table and a 'application_logs' table. I am attempting to select all the applications where the 'type' is equal to 'test' and then join the 'application_logs' table and retrieve only the first log entry for the application.
One of the queries I tried and understood most was: (whilst this didn't fail it looked like an endless loop and completed the query.
SELECT applications.id FROM applications JOIN application_logs ON application_logs.application_id =
(
SELECT application_logs.id FROM application_logs
WHERE application_logs.application_id = applications.id
ORDER BY id DESC
LIMIT 1
)
WHERE type = 'test';
There were some other queries (using CROSS APPLY/distinct) I attempted but they didn't make sense to me and didn't look like they were trying to achieve the same thing. I appreciate all the help :)
There are many ways to achieve this in standard SQL. A lateral join (CROSS APPLY) is the first to come to mind, but MySQL doesn't support it. Another would be FETCH FIRST ROWS WITH TIES to get all latest application logs, but MySQL doesn't support it (and its counterpart LIMIT doesn't have a ties clause either). If you are only interested in the application ID alone (as shown in your query) one could even combine this with an INTERSECT operation, but MySQL doesn't support it.
After all you want to find out the maximum log ID per application ID. As of MySQL 8 you can do this on-the-fly:
select *
from applications a
left join
(
select
application_logs.*,
max(id) over (partition by application_id) as max_application_id
from application_logs
) al on al.application_id = a.id and al.application_id = al.max_application_id
where a.type = 'test';
In earlier vesions you would do this in separate steps. One way would be this:
select *
from applications a
left join application_logs al
on al.application_id = alm.application_id
and (al.application_id, al.id) in
(
select application_id, max(id)
from application_logs
group by application_id
)
where a.type = 'test';
Another is your own query, where you only got confused with the IDs:
SELECT *
FROM applications a
JOIN application_logs al ON al.id =
(
SELECT almax.id
FROM application_logs almax
WHERE almax.application_id = a.id
ORDER BY almax.id DESC
LIMIT 1
)
WHERE a.type = 'test';
try this
SELECT
a.*, c.*
FROM
applications a
INNER JOIN (
SELECT
al.application_id AS application_id,
MAX(al.id) AS al_id
FROM
application_logs al
GROUP BY
al.application_id
) c ON a.id = c.application_id
WHERE
a.type = 'test';
To get the first entry from application_logs table with respect to application_id. You need to use Row_Number Over (partition by order by ).
SELECT *
FROM applications A
LEFT JOIN (
SELECT Id AS applications.id
FROM (
SELECT ROW_NUMBER() OVER (Partition By applications.id ORDER BY application_logs.id) as R, application_logs.id, applications.id
FROM application_logs
) AS S
WHERE R = 1
) AS L ON L.applications.id = A.applications.id
Try this -
SELECT a.id, ay.*
FROM applications AS a
INNER JOIN (
SELECT al.application_id, min(al.id) as Min_Id
FROM application_logs AS al
GROUP BY al.application_id
) AS ax ON ax.application_id = a.id
INNER JOIN application_logs AS ay ON ay.id = ax.id
WHERE a.type = 'test';
Your query suggests that "first" means the largest id. (Colloquially, I would expect "first" to mean the smallest id or earliest chronologically.)
I usually recommend a correlated subquery for the filtering. It is worth testing if this is faster than other methods:
select . . .
from applications a join
application_logs al
on al.application_id = a.id
where a.type = 'test' and
al.id = (select max(al2.id)
from application_logs al2
where al2.application_id = al.application_id
);
The optimal indexes for performance are:
application(type, id)
application_logs(application_id, id)

Getting Newest Record from a table using mysql

This has to be a no brainer, but I am stumped. I'm used to using aggregate 'FIRST' in MsAccess, but MySql has no such thing.
Here is a simple table. I want to return the most recent record based on the date,
for each unique 'group ID'. I need the three records in yellow.
I was asked to add my full query. I tried one of the suggestions using the JOIN feature replacing 't' with the temp table name, but it failed to work. "Can't reopen table 't'"
The code is below. I know it's ugly, but it does return the correct data set.
I cleaned up the code a bit and added the JOIN code. Error: "Can't reopen table 't'"
enter code here
DROP TABLE IF EXISTS `tmpMaxLookupResults`;
create temporary table tmpMaxLookupResults
as
SELECT
REPORTS.dtmReportCompleted,
RESULTS.lngMainReport_ID, RESULTS.lngLocationGroupSub_ID
FROM
(tbl_010_040_ProcedureVsTest_Sub as ProcVsSub
INNER JOIN tbl_010_050_CheckLog_RESULTS as RESULTS
ON (ProcVsSub.lngLocationGroupSub_ID = RESULTS.lngLocationGroupSub_ID)
AND (ProcVsSub.lngProcedure_ID = RESULTS.lngProcedure_ID)
AND (ProcVsSub.lngItemizedTestList_ID = RESULTS.lngItemizedTestList_ID)
AND (ProcVsSub.strPasscodeAdmin = RESULTS.strPasscodeAdmin)
AND (ProcVsSub.strCFICode = RESULTS.strCFICode))
INNER JOIN
tbl_000_010_MAIN_REPORT_INFO as REPORTS ON (RESULTS.lngPCC_ID =
REPORTS.lngPCC_ID)
AND (RESULTS.lngProcedure_ID = REPORTS.lngProcedure_ID)
AND (RESULTS.lngMainReport_ID = REPORTS.idMainReport_ID)
AND (RESULTS.strPasscodeAdmin = REPORTS.strPasscodeAdmin)
AND (RESULTS.strCFICode = REPORTS.strCFICode)
WHERE
(((RESULTS.lngProcedure_ID) = 143)
AND ((RESULTS.dtmExpireDate) IS NOT NULL)
AND ((RESULTS.strCFICode) = 'ems'))
GROUP BY RESULTS.lngMainReport_ID, RESULTS.lngLocationGroupSub_ID
ORDER BY (REPORTS.dtmReportCompleted) DESC;
SELECT t.*
FROM tmpMaxLookupResults AS t
JOIN (
SELECT lngLocationGroupSub_ID,
MAX(dtmReportCompleted) AS max_date_completed
FROM tmpMaxLookupResults
GROUP BY lngLocationGroupSub_ID ) AS dt
ON dt.lngLocationGroupSub_ID = t.lngLocationGroupSub_ID AND
dt.max_date_completed = t.dtmReportCompleted
enter code here
Try this
SELECT
tn.*
FROM
tableName tn
RIGHT OUTER JOIN
(
SELECT
groupId, MAX(date_completed) as max_date_completed
FROM
tableName
GROUP BY
groupId
) AS gt
ON
(gt.max_date_completed = nt.date_completed AND gt.groupId = nt.groupId)
You can use the following SQL.
select * from table1 order by date_completed desc Limit 1;
Use Order By
SELECT *
FROM table_name
ORDER BY your_date_column_name
DESC
LIMIT 1
In a Derived Table, get the maximum date_completed value for every group_id.
Join this result-set back to the main table, in order to get the complete row corresponding to maximum date_completed value for every group_id
Try the following query:
SELECT t.*
FROM your_table_name AS t
JOIN (
SELECT group_id,
MAX(date_completed) AS max_date_completed
FROM your_table_name
GROUP BY group_id
) AS dt
ON dt.group_id = t.group_id AND
dt.max_date_completed = t.date_completed

MySQL Query to fetch Distinct Rows with latest status

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;

Selecting from two tables and the latest entry

I keep running on an issue in calling from MySQL and getting what I need. I have two tables called projects and entries, what I am trying to do is get the latest time stamped entry. The SQL query is as follows:
SELECT
projects.ProjectLogo, projects.ProjectLink, projects.ProjectDescription,
entries.EntryNum, entries.Votes, entries.Views, entries.Update
FROM
projects
LEFT JOIN entries
ON projects.ProjectID = entries.ProjectID
AND projects.Media = 'image'
AND projects.Type = 'fan-art'
GROUP BY
projects.ProjectID
ORDER BY
entries.Update DESC
The issue is that I get the results but not the latest entry, I have used MAX(entries.Update) but it does not work. Any suggestions? Why does it not work?
You can use a subquery to get the latest Update for every ProjectID on table entries. The result of the subquery is then join back on the two join statements providied that it will match on two columns: ProjectID and Update.
SELECT projects.ProjectLogo,
projects.ProjectLink,
projects.ProjectDescription,
entries.EntryNum,
entries.Votes,
entries.Views,
entries.Update
FROM projects
INNER JOIN entries
ON projects.ProjectID = entries.ProjectID
INNER JOIN
(
SELECT a.ProjectID, MAX(a.Update) max_val
FROM entries a
GROUP BY a.ProjectID
) b ON b.ProjectID = entries.ProjectID AND
b.max_val = entries.Update
WHERE projects.Media = 'image' AND
projects.Type = 'fan-art'
ORDER BY entries.Update DESC
Try using a subquery to get the latest entry:
select projectId, max(`update`) as lastUpdate
from entries
group by projectId
And now, use this subquery to get what you need:
select ...
from
projects as p
inner join entries as e on p.projectId=e.projectId
inner join (
select projectId, max(`update`) as lastUpdate
from entries
group by projectId) as me on e.`update`=me.lastUpdate
Hope this helps you
You have to restrict the join to only the record (row) with the latest entry date.
if the pk in entries is chronologically increasing, you can use it.
Select p.ProjectLogo, p.ProjectLink, p.ProjectDescription,
e.EntryNum, e.Votes, e.Views, e.Update
From projects p
Left Join entries e
On e.EntryId =
(Select(Max(entryId) from entries
where ProjectID = p.ProjectID)
Where p.Media = 'image'
And p.Type = 'fan-art'
Group By p.ProjectID
Order By e.Update Desc
Otherwise, you need a double nested subquery
Select p.ProjectLogo, p.ProjectLink, p.ProjectDescription,
e.EntryNum, e.Votes, e.Views, e.Update
From projects p
Left Join entries e
On e.EntryId =
(Select entryId from entries
where ProjectID = p.ProjectID
And update =
(Select max(update) From entries
Where ProjectID = p.ProjectID))
where p.Media = 'image'
And p.Type = 'fan-art'
Group By p.ProjectID
Order By e.Update Desc

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.