Syntax for UPDATE with LEFT JOIN - mysql

How do you insert this query with an update? I've tried all possible combinations and nothing seems to work!
UPDATE Test_table2
SET Pledge_Status = closed
WHERE (
SELECT SUM(pledgers.Pledge_payment_amt) AS pledged
FROM Test_table2 AS recipients
LEFT JOIN Test_table2 AS pledgers
ON recipients.GIFT_NO = pledgers.PLEDGE_GIFT_NO
GROUP BY recipients.GIFT_NO
HAVING recipients.Pledge_Amt >= pledged
ORDER BY recipients.CRSID ASC
);
Schema (all varchar):
ID, Name, Pledge_Amount , Pledge_payment_amt , Gift_No, Pledge_Gift_No, Pledge_Open/Closed
Thank you so very much!

Parallel to this question I suggest that you use WHERE ... IN SELECT. Also, you need to pass the ID of each row from the subselect to the main query, and since you didn't provide a table schema, we can only guess here:
UPDATE Test_table2
SET Pledge_Status = closed
WHERE [your id column] IN (
SELECT [your id column]
FROM Test_table2 AS recipients
LEFT JOIN Test_table2 AS pledgers
ON recipients.GIFT_NO = pledgers.PLEDGE_GIFT_NO
GROUP BY recipients.GIFT_NO
HAVING recipients.Pledge_Amt >= SUM(pledgers.Pledge_payment_amt)
/*ORDER BY recipients.CRSID ASC <-- no need to order anything here */
);

Related

How do I solve error code 1242 with MySQL

In my MySQL database, I have two tables. I am trying to sum up the scores in the two tables. First table named term2 has a particular column named Total_Score, and a column named StudentID. The second table named overall also has a column named Total_Score and a column named StudentID. What I want to do is UPDATE the value of overall.Total_Score by summing up the values in term2.Total_Score with the values in overall.Total_Score, and place the result in overall.Total_Score. Below is the code I am using, but when I have more than one row, I get the error above. Have a look at my code and assist, please. Thank you in advance.
UPDATE overall SET Total_Score = ( SELECT (
ot.Total_Score + op.Total_Score
) AS Total_Score
FROM (
SELECT StudentID, Total_Score
FROM term2
GROUP BY StudentID
) AS ot
INNER JOIN (
SELECT StudentID, Total_Score
FROM overall
GROUP BY StudentID
) AS op ON ( op.StudentID = ot.StudentID ) )
Try the below - you need an update join and your syntax was incorrect
update overall o inner join
(
SELECT op.StudentID,
sum(ot.total_score) as Total_Score_ot,
sum(op.total_score) as Total_Score_op
FROM term2 ot inner join overall op on op.StudentID = ot.StudentID
GROUP BY op.StudentID
) as o1 on o.StudentID = o1.StudentID
SET Total_Score=Total_Score_ot+Total_Score_op
I assume you want to replace the overall value with the calculated value. The key idea is to aggregate before joining the tables together:
update overall o join
(select t.StudentId, sum(t.total_score) as total_score
from term2 t
group by t.StudentId
) t
using (StudentId)
set o.Total_Score = t.Total_Score;
If you want to increment the stored value by the total in term2, then use:
set o.Total_Score = o.Total_Score + t.Total_Score;
If instead you want to replace the stored value for all students, even those with no rows in term2, then use left join:
update overall o left join
(select t.StudentId, sum(t.total_score) as total_score
from term2 t
group by t.StudentId
) t
using (StudentId)
set o.Total_Score = coalesce(t.Total_Score, 0);

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

UPDATE TABLE WITH CASE

I have two tables user_master and login_history. In User_master I want to update the status column as A(absent) or P(Present) if user has logged in in current date from login history.the code I am trying but it updates all the rows. All I want is if the user has logged in , it should match both the tables and update user_master status column as P or A. Hope My question is clear. Help would be really appreciated. here is my MySQL query
UPDATE User_master a
INNER JOIN
(
SELECT DISTINCT user_name FROM login_history WHERE DATE(`login_time`)=CURRENT_DATE()
) b
SET a.`user_status` = CASE
WHEN a.`user_name`=B.`user_name` THEN 'P'
WHEN a.`user_name`!=B.`user_name` THEN 'A'
END
Hmmm, I am thinking LEFT JOIN:
UPDATE User_master m
LEFT JOIN Login_History lh
ON m.user_name = lh.user_name AND
DATE(lh.login_time) = CURRENT_DATE()
SET m.user_status = (CASE WHEN lh.user_name IS NULL THEN 'A' ELSE 'P' END);
It occurs to me that there might be more than one login on a given date. The result is additional updates on the same row. You can prevent this by doing:
UPDATE User_master m LEFT JOIN
(SELECT lh.user_name, 'P' as user_status
FROM Login_History lh
WHERE lh.login_time >= CURRENT_DATE() AND
lh.login_tie < DATE_ADD(CURRENT_DATE(), INTERVAL 1 DAY)
GROUP BY lh.user_name
) lh
ON m.user_name = lh.user_name
SET m.user_status = COALESCE(lh.user_status, 'A');
Notice that I changed the date arithmetic as well. This version should make better use of an index.
Might be easier with two queries:
Set everyone absent (update ... set user_status='A')
Set the present people to P with a select set.
Like so:
update user_master set user_status='A';
update user_master set user_status='P'
where user_name in (select distinct user_name from login_history...);
A join is somewhat quicker, but this is a pretty clean, understandable approach.

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.

MySQL update value from the same table with count

What I want to do is to set every patient its unique patient code which starts with 1 and it's not based on row id. Id only specifies order. Something like this:
patient_id patient_code
2 1
3 2
4 3
This is my query:
UPDATE patients p1
SET p1.patient_code = (
SELECT COUNT( * )
FROM patients p2
WHERE p2.patient_id <= p1.patient_id
)
But it is throwing error:
#1093 - You can't specify target table 'p1' for update in FROM clause
I found this thread: Mysql error 1093 - Can't specify target table for update in FROM clause.But I don't know how to apply approved answer this to work with subquery WHERE which is necessary for COUNT.
UPDATE
patients AS p
JOIN
( SELECT
p1.patient_id
, COUNT(*) AS cnt
FROM
patients AS p1
JOIN
patients AS p2
ON p2.patient_id <= p1.patient_id
GROUP BY
p1.patient_id
) AS g
ON g.patient_id = p.patient_id
SET
p.patient_code = g.cnt ;
I found working solution, but this is just workaround:
SET #code=0;
UPDATE patients SET patient_code = (SELECT #code:=#code+1 AS code)
Try this,
UPDATE patients p1 INNER JOIN
(
SELECT COUNT(*) as count,patient_id
FROM patients
group by patient_id
)p2
SET p1.patient_code=p2.count
WHERE p2.patient_id <= p1.patient_id
SQL_LIVE_DEMO
Thanks to Mari's answer I found a solution to my similar problem. But I wanted to add a bit of an explanation which for me at first wasn't too clear from his answer.
What I wanted to do would have been as simple as the following:
UPDATE my_comments AS c
SET c.comment_responses = (
SELECT COUNT(c1.*) FROM my_comments AS c1
WHERE c.uid = c.parent_uid
);
Thanks to Mari I then found the solution on how to achieve this without running into the error You can't specify target table 'p1' for update in FROM clause:
UPDATE my_comments AS c
INNER JOIN (
SELECT c1.parent_uid, COUNT(*) AS cnt
FROM my_comments AS c1
WHERE c1.parent_uid <> 0
GROUP BY c1.parent_uid
) AS c2
SET c.comment_responses = c2.cnt
WHERE c2.parent_uid = c.uid;
My problems before getting to this solution were 2:
the parent_uid field doesn't always contain an id of a parent which is why I added the WHERE statement in the inner join
I didn't quite understand why I would need the GROUP BY until I executed the SELECT statement on it's own and the answer is: because COUNT groups the result and really counts everything. In order to prevent this behavior the GROUP BY is needed. In my case I didn't have to group it by uid though but the parent_uid to get the correct count. If I grouped it by uid the COUNT would always be 1 but the parent_uid existed multiple times in the result. I suggest you check the SELECT statement on it's own to check if it's the result you expect before you execute the full UPDATE statement.