Populate one table with selected data from two others tables - mysql

I have a table that I am trying to populate with our open tickets and the last followup they received, columns below, this is called "glpi_plugin_ns_followup":
ticket_number[int(11)], datetime_created[datetime], requester[int(11)], title[longtext], description[longtext], followup_datetime[datetime], last_followup[longtext], technician_user[int(11)]
The tables I am pulling from are glpi_tickets and glpi_ticketfollowups
glpi_tickets
id[int(11)], entities_id[int(11)], name[varchar(255)], date[datetime], closedate[datetime], solvedate[datetime], date_mod[datetime], users_id_lastupdater[int(11)], status[int(11)], users_id_recipient[int(11)], requesttypes_id[int(11)], content, urgency[int(11)], impact[int(11)], priority[int(11)], itilcategories_id[int(11)], type[int(11)], solutiontypes_id[int(11)], solution, global_validation[int(11)], slas_id[int(11)], slalevels_id[int(11)], due_date[datetime], begin_waiting_date[datetime], sla_waiting_duration[int(11)], waiting_duration[int(11)], close_delay_stat[int(11)], solve_delay_stat[int(11)], takeintoaccount_delay_stat[int(11)], actiontime[int(11)], is_deleted[tinyint(1)], locations_id[int(11)], validation_percent[int(11)]
glpi_ticketfollowups
id[int(11)], tickets_id[int(11)], date[datetime], users_id[int(11)], content[longtext], is_private[tinyint(11)], requesttypes_id[int(11)]
The common identifier across all three are as follows:
glpi_tickets = id
glpi_ticketfollowups = tickets_id
glpi_plugin_ns_followup = ticket_number
I've managed to insert the first five columns by using the below query:
TRUNCATE glpi_plugin_ns_followup;
INSERT INTO glpi_plugin_ns_followup (ticket_number, datetime_created, requester, title, description)
SELECT Id, date, users_id_recipient, name, content FROM glpi.glpi_tickets
WHERE is_deleted = 0 and status !=6 and status !=5;
However I am struggling to fill in the last 3 columns with the relevant updates, so far I have tried using this code but it doesn't work:
UPDATE glpi_plugin_ns_followup
SET followup_datetime = (SELECT date from glpi_ticketfollowups where glpi_ticketfollowups.tickets_id = glpi_plugin_ns_followup.ticket_number),
last_followup = (SELECT content from glpi_ticketfollowups where glpi_ticketfollowups.tickets_id = glpi_plugin_ns_followup.ticket_number),
technician_user = (SELECT users_id from glpi_ticketfollowups where glpi_ticketfollowups.tickets_id = glpi_plugin_ns_followup.ticket_number)
This gives me the response:
Error Code: 1242. Subquery returns more than 1 row
Which it should as I'm trying to update multiple rows, or the error may be because there are multiple followups per ticket. I have a feeling I may be going at this from entirely the wrong angle. If someone can assist me and put me back on the right track that would be much appreciated.
I can supply sample data if needed but it will take a while to gather and censor it ;-)
EDIT: As per Kamil's suggestions with max() I have managed to get the date fields to work:
UPDATE glpi_plugin_ns_followup
SET followup_datetime = (SELECT max(date) from glpi_ticketfollowups where glpi_ticketfollowups.tickets_id = glpi_plugin_ns_followup.ticket_number GROUP BY tickets_id)
However I cannot get it to work for content or technician_user, if I can pull the newest date/times for the last time a followup was added to a ticket can I modify anything to pull in the content and technician_user as well?
EDIT2: tried adding GROUP BY t.id; at the end of your initial solution but it does not use the newest followup for the tickets.
EDIT3:Have tried the following, it pulls in the correct date/time but the wrong content
TRUNCATE glpi_plugin_ns_followup;
INSERT INTO glpi_plugin_ns_followup (ticket_number, datetime_created, requester, title, description, followup_datetime, last_followup, technician_user)
SELECT
t.id, t.date, t.users_id_recipient, t.name, t.content,
tf.datetime, tf.content, tf.users_id
FROM
glpi.glpi_tickets t
LEFT JOIN (SELECT max(date) as datetime, content, users_id, tickets_id
FROM glpi_ticketfollowups
GROUP BY tickets_id) tf ON
t.id = tf.tickets_id
WHERE
t.is_deleted = 0
and t.status NOT IN (5,6)
GROUP BY t.id;
Thanks
iFr4g

You could combine both of those statements into one and only issue INSERT command. However, this will multiply your rows from glpi_tickets if more than one matching row exists in glpi_ticketfollowups fetched with the condition t.id = tf.tickets_id.
I've added a LEFT JOIN and slightly changed the WHERE clause to save some coding.
INSERT INTO glpi_plugin_ns_followup (ticket_number, datetime_created, requester, title, description, followup_datetime, last_followup, technician_user)
SELECT
t.id, t.date, t.users_id_recipient, t.name, t.content,
tf.date, tf.content, tf.users_id
FROM
glpi.glpi_tickets t
LEFT JOIN glpi_ticketfollowups tf ON
t.id = tf.tickets_id
WHERE
t.is_deleted = 0
and t.status NOT IN (5,6)
If you need only one row per glpi_tickets table in your destination table, then what you are looking for is a way to get only one row from glpi_ticketfollowups per a ticket. This could be achieved by adding a GROUP BY clause and choosing appropriate logic of getting one value, for example: max() or min(). Remember, that issuing an aggregate function over 3 values will not bring you result from one row, but an aggregate result from entire dataset. This could be avoided by doing it the way you started - insert and update by adding a LIMIT clause.
Edit after comments:
Since you need last followup for a every ticket and you are sure that are dates per ticket are unique you could do a self-join calculating maximum date per ticket in glpi_ticketfollowups and then LEFT JOIN that to your glpi_tickets table:
INSERT INTO glpi_plugin_ns_followup (ticket_number, datetime_created, requester, title, description, followup_datetime, last_followup, technician_user)
SELECT
t.id, t.date, t.users_id_recipient, t.name, t.content,
tf.date, tf.content, tf.users_id
FROM
glpi.glpi_tickets t
LEFT JOIN (
SELECT allf.tickets_id, allf.date, allf.content, allf.users_id
FROM glpi_ticketfollowups allf
INNER JOIN (
SELECT tickets_id, MAX(date) AS date
FROM glpi_ticketfollowups
GROUP BY tickets_id
) lastf ON
allf.tickets_id = lastf.tickets_id
AND allf.date = lastf.date
) tf ON
t.id = tf.tickets_id
WHERE
t.is_deleted = 0
and t.status NOT IN (5,6)

Related

SQL: Select records based on comparison of two most recent associated records

Let's say we have a person table and survey table. survey is a set of attributes collected from a person at some point in time. Let's say survey has columns address and marriage_status
How do I select all persons whose address or marriage status has changed in the last survey?
Here's how I would write it if MySQL were able to magically interpret my intention:
SELECT *
FROM person
JOIN
(SELECT *
FROM survey
GROUP BY survey.person_id
ORDER BY survey.timestamp DESC
LIMIT 2 EACH) -- of course this part doesn't actually work. Trying to get last 2 records per person
surveys
ON surveys.person_id = person.id
WHERE surveys[0].address != surveys[1].address
OR surveys[0].marriage_status != surveys[1].marriage_status;
OR
SELECT *
FROM person
JOIN
(SELECT MOST RECENT survey FOR EACH person) latest_survey
ON latest_survey.person_id = person.id
JOIN
(SELECT SECOND MOST RECENT survey FOR EACH person) previous_survey
ON previous_survey.person_id = person.id
WHERE latest_survey.address != previous_survey.address
OR latest_survey.marriage_status != previous_survey.marriage_status;
This seems like a relatively straightforward query, but it's driving me crazy. I suspect I have tunnel vision and I'm not approaching this the right way.
EDIT: I am on MySQL v5. Based on the first couple answers, it seems like this might be the time to migrate to v8 (among other reasons)
So here's how I ended up doing it. It's a little long, but I think it's pretty straightforward? This felt amazing to get working.
(Note that underscores are used as prefixes in table aliases to help keep track of subquery depth)
SELECT person.*
FROM person
JOIN (
-- Join full survey data against each 'most recent' survey timestamp
SELECT s1.*
FROM survey s1
JOIN (
-- get most recent timestamp for each person
SELECT _s1.person_id, MAX(_s1.timestamp) timestamp
FROM survey _s1
GROUP BY person_id
) latest_surveys
ON latest_surveys.person_id = s1.person_id and latest_surveys.timestamp = s1.timestamp
) latest
ON latest.person_id = person.id
JOIN (
-- Join full survey data against each 'SECOND most recent' survey timestamp
select s2.*
from survey s2
JOIN (
-- to get SECOND most recent survey timestamp, do similar query, but exclude latest timestamp
SELECT _s2.person_id, MAX(_s2.timestamp) timestamp
FROM survey _s2
JOIN (
-- get most recent timestamp for each person (again)
SELECT __s2.person_id, MAX(__s2.timestamp) timestamp
FROM survey __s2
GROUP BY person_id
) _latest_surveys
-- Note the *NOT* equal here
ON _latest_surveys.person_id = _s2.person_id and _latest_surveys.timestamp != _s2.timestamp
GROUP BY _s2.person_id
) previous_surveys
ON previous_surveys.person_id = s2.person_id and previous_surveys.timestamp = s2.timestamp
) previous
ON previous.person_id = person.id
WHERE latest.address != previous.address
OR latest.marriage_status != previous.marriage_status;
Analytic functions make your question much more tractable. If you are not yet using MySQL 8+, then now would be a good time to upgrade. Assuming you are using MySQL 8+, we can try:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY p.id ORDER BY s.timestamp DESC) rn
FROM person p
INNER JOIN survey s ON p.id = s.person_id
)
SELECT id
FROM cte
GROUP BY id
HAVING
MAX(CASE WHEN rn = 1 THEN address END) <> MAX(CASE WHEN rn = 2 THEN address END) OR
MAX(CASE WHEN rn = 1 THEN marriage_status END) <> MAX(CASE WHEN rn = 2 THEN marriage_status END);
The above query uses a pivot trick to isolate the latest, and second latest, addresses and marriage statuses for each person. It retains person id values for those whose latest and second latest addresses or marriage statuses are not identical.
This might be how you can achieve that:
SELECT *
FROM person
JOIN (
SELECT *,
MAX(survey_date) latest_survey,
SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(survey_date ORDER BY person_id, survey_date ASC),',',-2),',',1) previous_survey,
SUBSTRING_INDEX(GROUP_CONCAT(address ORDER BY person_id, survey_date ASC),',',-1) curadd,
SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(address ORDER BY person_id, survey_date ASC),',',-2),',',1) prevadd,
SUBSTRING_INDEX(GROUP_CONCAT(marriage_status ORDER BY person_id, survey_date ASC),',',-1) curms,
SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(marriage_status ORDER BY person_id, survey_date ASC),',',-2),',',1) prevms
FROM survey GROUP BY person_id
HAVING curadd != prevadd OR curms != prevms) A
ON person.id=A.person_id;
Using GROUP_CONCAT and SUBSTRING_INDEX to combine the data value then separate it again and using those to compare at the end. I know there are a bunch of ways to achieve without all these, like your second example is something that I think can be done but when I think about it, it's going to be a very long query. This query however, since you're not using MySQL 8+ is much shorter but the performance of this query is a concern especially on a large table.
It is not given, but I hope you have at least MySQL 8 or similar to have ability to use Common Table Expression. It can simplify the complex query.
The trick part is getting survey records #1 and #2 for each user. I will do it this way: see cte1 and cte2 definition
WITH
cte1 AS (
SELECT MAX(x1.id) AS id, x1.person_id
FROM survey x1
GROUP BY x1.person_id),
cte2 AS (
SELECT MAX(x2.id) AS id, x2.person_id
FROM survey x2
JOIN cte1 ON cte1.person_id = x2.person_id
AND cte1.id > x2.id
GROUP BY x2.person_id)
SELECT
p.*,
s1.address, s2.address address2,
s1.marriage_status, s2.marriage_status marriage_status2
FROM person AS p
JOIN (
cte1 JOIN survey s1 ON s1.id = cte1.id
) ON cte1.person_id = p.id
JOIN (
cte2 JOIN survey s2 ON s2.id = cte2.id
) ON cte2.person_id = p.id
WHERE
(s1.address <> s2.address)
OR (s1.marriage_status <> s2.marriage_status)
https://www.db-fiddle.com/f/hLwdHiZin4MkdUZ4aBz67H/2
Update: Thanks to Ian, I replaced MIN to MAX to get recent records

MYSQL query to get project details and last MAX() action details from log

How can I write a MYSQL query to get project details and the entire last row of the activity log? I want a list of all the projects, with the data from each project's most recent row from the action log, all of it ordered by the most recent action log date DESC. Sorry, I know that this is a common query and the answer must be very easy. But I can't find the solution. I searched with every possible word combination. I found examples that need only one field such as MAX(id) from the joined table. I found solutions with COALESCE but can't seem to make them work. My problem is that I need many fields from the 'parent' table row PL_PROJECTS as well as many fields from the joined table PL_LOG row, not to mention people's names from the same table joined twice.
Everything I try either gives me all the rows of the PL_LOG, repeating rows from PL_PROJECTS. Or, I get just one row from PL_LOG for just one project if I put a LIMIT in the sub query. Here's my query that doesn't work:
SELECT
PJ.pj_id, PJ.pj_title, PJ.pj_location, PJ.pj_desc, PJ.pj_request, PJ.pj_date_start, PP1.pp_name AS supervisor_name, PP2.pp_name AS customer_name, ST.st_desc, logDate, logDesc
FROM PL_PROJECTS PJ
INNER JOIN PL_PEOPLE PP1 ON PJ.pj_spst_member = PP1.pp_id
INNER JOIN PL_PEOPLE PP2 ON PJ.pj_pp_id = PP2.pp_id
INNER JOIN PL_STATUS ST ON PJ.pj_status = ST.st_id
LEFT OUTER JOIN (
SELECT MAX(lg_pj_id) MaxLogID, lg_date AS logDate, lg_desc AS logDesc, lg_pj_id
FROM PL_LOG PL
ORDER BY lg_id DESC
)
LR ON LR.lg_pj_id = PJ.pj_id
GROUP BY PJ.pj_id
ORDER BY logDate DESC
LIMIT 9999999
I think you problem is, that your subselect only generates one row as you are using max() while you need one row per project (lg_pj_id i think).
You only need to rewrite the subselect to generate one row per project with the informations from the recent activity. Do you have an activity_ID in your action log? Because it looks like
lg_pj_id is the project_ID. The meaning of lg_desc is also unknown (or is that the action_log_id ?). Try to group by project_ID in you subselect and depending on your needs either select the max values from the corresponding rows or select the row with the maximum values per group (project_ID)
Thanks for the suggestion of GROUP BY to get one row per project. I tried changing the sub-query like so:
SELECT MAX(lg_id) AS MaxLogID, lg_desc, lg_pj_id
FROM PL_LOG PL
GROUP BY lg_pj_id
Now, I get one row from the log, but it gives me the max id, but not the lg_desc from the same row! If I try the sub-query by itself:
SELECT lg_id, lg_pj_id, lg_date, lg_desc
FROM `PL_LOG`
WHERE lg_pj_id = 33
ORDER BY lg_date DESC
I get these rows. You can see the max row, 68 has a description "30 minute skype call."
68,33,2018-06-10 00:00:00","30 minute skype call."
61,33,"2018-06-02 00:00:00","Sent email to try to elicit a response."
52,33,"2018-05-10 00:00:00","sent follow up email"
47,33,"2018-03-26 00:00:00","sent initial email"
46,33,"2018-03-26 00:00:00","sent initial email"
But when I try to get just that row, using GROUP BY, it gives me the max lg_id, but the first lg_desc. I need the data all from the max(lg_id) row:
SELECT MAX(lg_id) AS MaxLogID, lg_pj_id, lg_date, lg_desc
FROM PL_LOG
WHERE lg_pj_id = 33
GROUP BY lg_pj_id
ORDER BY MaxLogID DESC
Returns:
68, 33, "2018-03-26 00:00:00", "sent initial email"
Try this as mentioned in my comment:
SELECT
PJ.pj_id, PJ.pj_title, PJ.pj_location, PJ.pj_desc, PJ.pj_request,
PJ.pj_date_start, PP1.pp_name AS supervisor_name, PP2.pp_name AS
customer_name, ST.st_desc, logDate, logDesc
FROM PL_PROJECTS PJ
INNER JOIN PL_PEOPLE PP1 ON PJ.pj_spst_member = PP1.pp_id
INNER JOIN PL_PEOPLE PP2 ON PJ.pj_pp_id = PP2.pp_id
INNER JOIN PL_STATUS ST ON PJ.pj_status = ST.st_id
LEFT JOIN (SELECT lg_id, lg_date AS logDate, lg_desc AS logDesc, lg_pj_id
FROM PL_LOG AS PL
WHERE PL.lg_id=(SELECT MAX(lg_id) FROM PL_LOG AS PL_2
WHERE PL_LOG.lg_pj_id = PL_2.lg_pj_id )
LR ON LR.lg_pj_id = PJ.pj_id
GROUP BY PJ.pj_id
ORDER BY logDate DESC
LIMIT 9999999

Display zero in group by sql for a particular period

I am trying to run the following query to obtain the sales for each type of job for a particular period. However for certain months where there are no jobs of a particular job type performed no 0 is displayed in sales.
How can i display the zeros in such a condition.
Here is the sql query-
select Year(postedOn), month(postedOn), jobType, sum(price)
from tbl_jobs
group by jobType, year(postedOn), month(postedOn)
order by jobType, year(postedOn), month(postedOn)
Typically, this is where your all-purpose calendar or numbers table comes in to anchor the query with a consistent sequential set:
SELECT job_summary.*
FROM Calendar
CROSS JOIN (
-- you may not have though about this part of the problem, though
-- what about years/months with missing job types?
SELECT distinct jobType FROM tbl_jobs
) AS job_types
LEFT JOIN (
select Year(postedOn) AS year,month(postedOn) as month,jobType ,sum(price)
from tbl_jobs
group by jobType, year(postedOn), month(postedOn)
) job_summary
ON job_summary.jobType = job_types.jobType
AND job_summary.year = Calendar.year
AND job_summary.month = Calendar.month
WHERE Calendar.day = 1 -- Assuming your calendar is every day
AND calendar.date BETWEEN some_range_goes_here -- you don't want all time, right?
order by job_types.jobType, Calendar.year, Calendar.month

SQL Query - Determine who has only one gift record

I would like to know how I can write a SQL Script so a within a group of individuals initially selected:
SELECT [RECORDS].[CONSTITUENT_ID]
,[RECORDS].[FIRST_NAME]
,[RECORDS].[LAST_NAME]
,[DATEADDED]
,[DTE]
,[Amount]
,[REF]
,[TYPE]
FROM [re7].[dbo].[GIFT]
INNER JOIN [re7].[dbo].[RECORDS]
ON GIFT.CONSTIT_ID LIKE RECORDS.ID
WHERE ([DTE] BETWEEN '2/7/2015' AND '2/8/2015')
ORDER BY [DATEADDED] DESC
select only individuals who are "First Time Donors" (or someone who only has one gift in [re7].[dbo].[GIFT].
[RECORDS] is a table of all the constituents.
[GIFT] is a table of all recorded Gifts.
The output of the above Query, is just a table with:
CONSTITUENT_ID, FIRST_NAME, LAST_NAME, DATEADDED, DTE, Amount, REF, TYPE
I pretty much want to see the same output format, but I would like the query to select only CONSTITUENT_ID who only have 1 GIFT (by their Record ID) in [re7].[dbo].[GIFT].
I apologize for the lack of data to show. I wish I could describe better....
SELECT [RECORDS].[CONSTITUENT_ID]
,[RECORDS].[FIRST_NAME]
,[RECORDS].[LAST_NAME]
,[DATEADDED]
,[DTE]
,[Amount]
,[REF]
,[TYPE]
FROM [re7].[dbo].[GIFT]
INNER JOIN [re7].[dbo].[RECORDS]
ON GIFT.CONSTIT_ID LIKE RECORDS.ID
WHERE ([DTE] BETWEEN '2/7/2015' AND '2/8/2015')
AND GIFT.CONSTIT_ID IN (
SELECT CONSTIT_ID FROM re7.dbo.Gift GROUP BY CONSTIT_ID HAVING COUNT(*) = 1
) /* another option is to add a subquery to the query you already had */
ORDER BY [DATEADDED] DESC
This solution simply selects all the constituents who have made only one donation and then joins to that, thereby limiting the result set.
SELECT
r.[CONSTITUENT_ID]
,r.[FIRST_NAME]
,r.[LAST_NAME]
,[DATEADDED]
,[DTE]
,[Amount]
,[REF]
,[TYPE]
FROM
(select [CONSTIT_ID] from [re7].[dbo].[GIFT] group by [CONSTIT_ID] having count([CONSTIT_ID]) = 1) g1
inner join [re7].[dbo].[GIFT] g
on g.[CONSTIT_ID] = g1.[CONSTIT_ID]
INNER JOIN [re7].[dbo].[RECORDS] r
ON g.CONSTIT_ID LIKE r.RECORDS.ID
WHERE ([DTE] BETWEEN '2/7/2015' AND '2/8/2015')
ORDER BY [DATEADDED] 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.