Getting SQL Server Merge Error - sql

I am trying to update the product history's average rating number by using the merge function
MERGE ProductRankingHistory P
USING Review R ON P.PRHProduct = R.ProductID
WHEN MATCHED THEN
UPDATE
SET P.PRHAverageRating = (SELECT AVG(ReviewRateValue)
FROM Review
WHERE ProductID = P.PRHProduct
AND YEAR(ReviewRateDate) = PRHYear);
And I get this error:
Msg 8672, Level 16, State 1, Line 2
The MERGE statement attempted to UPDATE or DELETE the same row more than once. This happens when a target row matches more than one source row. A MERGE statement cannot UPDATE/DELETE the same row of the target table multiple times. Refine the ON clause to ensure a target row matches at most one source row, or use the GROUP BY clause to group the source rows.

Merge seems unnecessary for this logic. Something like:
UPDATE ProductRankingHistory P
SET PRHAverageRating = (SELECT AVG(r.ReviewRateValue)
FROM Review r
WHERE r.ProductID = P.PRHProduct AND YEAR(r.ReviewRateDate) = P.PRHYear
)
WHERE EXISTS (SELECT 1
FROM Review r
WHERE r.ProductID = P.PRHProduct AND YEAR(r.ReviewRateDate) = P.PRHYear
);
After writing this, I see that you are probably using SQL Server (or a similar database). The following might work:
UPDATE p
SET PRHAverageRating = rp.avg_ReviewRateValue
FROM ProductRankingHistory p JOIN
(SELECT r.ProductID, YEAR(r.ReviewRateDate) as yyyy, AVG(r.ReviewRateValue) as avg_ReviewRateValue
FROM Review r
GROUP BY r.ProductID, YEAR(r.ReviewRateDate)
) rp
ON rp.ProductId = p.PRHProduct AND rp.yyyy = p.prhYear;

Related

MS SQL count values for per row for per id

I'm trying to count incident types per depot. My current query returns the following result set:
I then apply a count query on this set which looks like this:
select middleList.DepotName as depot,
count(middleList.NearMiss) as NearMiss,
count(middleList.Theft) as Theft,
count(middleList.Security) as Security,
count(middleList.LostTimeDisablingInjury) as LostTimeDisablingInjury,
count(middleList.Fire) as Fire,
count(middleList.OccupationalIllness) as OccupationalIllness,
count(middleList.VehicleAccident) as LostTimeDisablingInjury,
count(middleList.Spliiage) as Fire,
count(middleList.PropertyDamage) as OccupationalIllness,
count(middleList.NonConformance) as NonConformance,
count(middleList.Other) as Other
from
(SELECT dbo.IncidentTypes.NearMiss, dbo.IncidentTypes.Theft,
dbo.IncidentTypes.Security, dbo.IncidentTypes.LostTimeDisablingInjury,
dbo.IncidentTypes.Fire, dbo.IncidentTypes.OccupationalIllness,
dbo.IncidentTypes.VehicleAccident, dbo.IncidentTypes.Spliiage,
dbo.IncidentTypes.PropertyDamage, dbo.IncidentTypes.NonConformance,
dbo.IncidentTypes.Other, dbo.Incidents.IncidentTitle, dbo.Depots.DepotName,
dbo.Incidents.IncidentNumber
FROM dbo.Departments INNER JOIN
dbo.Depots ON dbo.Departments.DepotId = dbo.Depots.DepotId
INNER JOIN
dbo.Employees ON dbo.Departments.DepartmentId =
dbo.Employees.DepartmentId INNER JOIN
dbo.IncidentTypes INNER JOIN
dbo.Incidents ON dbo.IncidentTypes.IncidentTypeId =
dbo.Incidents.IncidentTypeId ON dbo.Employees.EmployeeId =
dbo.Incidents.EmployeeId_EmployeeInvolv
Where Incidents.DateCreated = (SELECT MAX (i2.DateCreated) FROM
Incidents as i2 WHERE Incidents.IncidentNumber = i2.IncidentNumber)) as
middleList
group by middleList.DepotName
Which return the following incorrect results:
Please help.
Okay so I found the problem.
It seems like the rows are being counted and not the actual values, thus adding this to the count insured that they were being counted properly:
count(case when middleList.NearMiss <> '0' then 0 end ) as NearMiss
End result being:

UPDATE statement with JOIN in SQL Server Not Working as Expected

I'm attempting to update the LAST_INSPECTION_FW field for all records in the VEHICLES_FW table with the last JOB_DATE_FW for records with the REASON_CODE_FW = 35. However, what's happening is that once the below code is executed, it's not taking into consideration the WHERE clause. This causes all of the records to update when it should just be updating those with the REASON_CODE_FW = 35.
Is there a way to restructure this code to get it working correctly? Please help, thanks!
UPDATE VEHICLES_FW
SET VEHICLES_FW.LAST_INSPECTION_FW = JOB_HEADERS_FW.FIELD2MAX
FROM VEHICLES_FW
INNER JOIN (SELECT VEHICLE_ID_FW, MAX(JOB_DATE_FW) AS FIELD2MAX
FROM JOB_HEADERS_FW
GROUP BY VEHICLE_ID_FW) AS JOB_HEADERS_FW
ON VEHICLES_FW.VEHICLE_ID_FW = JOB_HEADERS_FW.VEHICLE_ID_FW
INNER JOIN JOB_DETAILS_FW
ON JOB_NUMBER_FW = JOB_NUMBER_FW
WHERE REASON_CODE_FW = '35'
Common Table Expressions are your friend here. SQL Server's strange UPDATE ... FROM syntax is not. EG
with JOB_HEADERS_FW_BY_VEHICLE_ID as
(
SELECT VEHICLE_ID_FW, MAX(JOB_DATE_FW) AS FIELD2MAX
FROM JOB_HEADERS_FW
GROUP BY VEHICLE_ID_FW
), q as
(
Select VEHICLES_FW.LAST_INSPECTION_FW, JOB_HEADERS_FW_BY_VEHICLE_ID.FIELD2MAX NEW_LAST_INSPECTION_FW
FROM VEHICLES_FW
INNER JOIN JOB_HEADERS_FW_BY_VEHICLE_ID
ON VEHICLES_FW.VEHICLE_ID_FW = JOB_HEADERS_FW_BY_VEHICLE_ID.VEHICLE_ID_FW
INNER JOIN JOB_DETAILS_FW
ON JOB_NUMBER_FW = JOB_NUMBER_FW
WHERE REASON_CODE_FW = '35'
)
UPDATE q set LAST_INSPECTION_FW = NEW_LAST_INSPECTION_FW
I suspect this does what you want:
update v
set last_inspection_fw = (
select max(j.job_date_fw)
from job_headers_fw j
inner join job_details_fw jd on jd.job_number_fw = j.job_number_fw
where j.vehicle_id_fw = v.vehicle_id_fw and jd.reason_code_fw = 35
)
from vehicles_fw v

SQL: Error when using MERGE

I'm trying to update or insert one table based on the entries present in another table. But when I try to use MERGE to accomplish this. I'm getting below error message.
Error: The MERGE statement attempted to UPDATE or DELETE the same row more than once. This happens when a target row matches more than one source row. A MERGE statement cannot UPDATE/DELETE the same row of the target table multiple times. Refine the ON clause to ensure a target row matches at most one source row, or use the GROUP BY clause to group the source rows.
Do I have to include more conditions in ON clause?
Code:
IF EXISTS (SELECT ShoppingCartNo FROM tbPOValidation WHERE ShoppingCartNo <> '')
BEGIN
MERGE tbPOValidation AS t
USING tbPOValidationTemp AS s
ON (CONVERT(VARCHAR(30),t.ShoppingCartNo)+CONVERT(VARCHAR(30),t.lineitemNo)+CONVERT(VARCHAR(30),t.PONo)) =
(CONVERT(VARCHAR(30),s.ShoppingCartNo)+CONVERT(VARCHAR(30),s.lineitemNo)+CONVERT(VARCHAR(30),s.PONo))
--When records are matched, update
--the records if there is any change
WHEN MATCHED AND (CONVERT(VARCHAR(30),t.ShoppingCartNo)+CONVERT(VARCHAR(30),t.lineitemNo)+CONVERT(VARCHAR(30),t.PONo)) =
(CONVERT(VARCHAR(30),s.ShoppingCartNo)+CONVERT(VARCHAR(30),s.lineitemNo)+CONVERT(VARCHAR(30),s.PONo))
THEN
UPDATE SET t.SupplierName = s.SupplierName, t.DUNS = s.DUNS, t.PONo = s.PONo, t.LineItemNo = s.LineItemNo, t.PurchDocItemDesc = s.PurchDocItemDesc,
t.POIssueDate = s.POIssueDate, t.DeliveryDate = s.DeliveryDate, t.PurchDocType = s.PurchDocType, t.MtrNo = s.MtrNo, t.Location = s.Location,
t.PayTerms = s.PayTerms, t.BlanketNo = s.BlanketNo, t.BlanketLineItemNo = s.BlanketLineItemNo, t.ShoppingCartNo = s.ShoppingCartNo,
t.SHCItmNo = s.SHCItmNo, t.ItemPricing = s.ItemPricing, t.ItmPrcCurrency = s.ItmPrcCurrency, t.Per = s.Per, t.POValue = s.POValue,
t.POValueCurrency = s.POValueCurrency, t.Qty = s.Qty, t.UOM = s.UOM, t.MFGName = s.MFGName, t.MFGPartNO = s.MFGPartNO, t.Description = s.Description,
t.Remarks = s.Remarks, t.Accept = s.Accept, t.AcceptedBy = s.Duns, t.AcceptedOn = GETDATE(), t.RejectionReason = s.RejectionReason
--When no records are matched, insert
--the incoming records from s
--table to t table
WHEN NOT MATCHED BY TARGET THEN
INSERT (SupplierName, DUNS, PONo, LineItemNo, PurchDocItemDesc, POIssueDate, DeliveryDate, PurchDocType, MtrNo,
Location, PayTerms, BlanketNo, BlanketLineItemNo, ShoppingCartNo, SHCItmNo, ItemPricing, ItmPrcCurrency, Per, POValue, POValueCurrency,
Qty, UOM, MFGName, MFGPartNO, Description, Remarks, Accept, AcceptedBy, AcceptedOn, RejectionReason)
VALUES (s.SupplierName, s.DUNS, s.PONo, s.LineItemNo, s.PurchDocItemDesc, s.POIssueDate, s.DeliveryDate, s.PurchDocType, s.MtrNo,
s.Location, s.PayTerms, s.BlanketNo, s.BlanketLineItemNo, s.ShoppingCartNo, s.SHCItmNo, s.ItemPricing, s.ItmPrcCurrency, s.Per, s.POValue,
s.POValueCurrency,s.Qty, s.UOM, s.MFGName, s.MFGPartNO, s.Description, s.Remarks, s.Accept, s.AcceptedBy, s.AcceptedOn, s.RejectionReason);
END
It is likely that the concatenation of fields is not unique i.e 10 11 12 would be equal to 101 11 2. Put special characters between the fields:
ON (CONVERT(VARCHAR(30),t.ShoppingCartNo)+'~' +CONVERT(VARCHAR(30),t.lineitemNo)+'~'+CONVERT(VARCHAR(30),t.PONo)) =
(CONVERT(VARCHAR(30),s.ShoppingCartNo)+'~' +CONVERT(VARCHAR(30),s.lineitemNo)+'~' +CONVERT(VARCHAR(30),s.PONo))

My UPDATE statement with WHERE EXISTS does not limit to the SELECT statement results

I have data in a temporary table and I am checking for duplicates in two other tables. I want to set a flag on the temp table (SET DupFlag = TRUE) for all duplicates found. My SELECT statement works perfectly, returning only the 48 duplicates that I entered to test with. But when I add UPDATE and WHERE EXISTS, every record in idTempImport2 is set to TRUE instead of just the 48 records returned from the SELECT statement. Is my syntax wrong? It looks like this:
UPDATE idTempImport2 as tmp2 SET DupFlag = TRUE
WHERE EXISTS
(SELECT * FROM idTempImport2 tmp2
LEFT JOIN (SELECT im.idDate, im.UserID, im.ActionID, im.IsHC, idn.Epic1, idn.Epic2
FROM idMain AS im
INNER JOIN idNotes AS idn ON im.aID = idn.MainID
WHERE idDate BETWEEN "2017-01-02" AND "2017-01-31") AS qry
ON qry.idDate = tmp2.idDate AND qry.UserID = tmp2.UserID AND qry.ActionID = tmp2.ActionID AND qry.Epic1 = clng(tmp2.Epic1) AND qry.Epic2 = clng(tmp2.Epic2)
WHERE qry.Epic1 <> NULL);
I think the ultimate issue is that you want a correlated subquery. As written, the subquery has no connection to the outer query, so it is likely that at least one row meets the condition. So, everything gets updated.
I think you intend:
UPDATE idTempImport2 as tmp2
SET DupFlag = TRUE
WHERE EXISTS (SELECT im.idDate, im.UserID, im.ActionID, im.IsHC, idn.Epic1, idn.Epic2
FROM idMain AS im INNER JOIN
idNotes AS idn
ON im.aID = idn.MainID
WHERE idDate BETWEEN "2017-01-02" AND "2017-01-31" AND
im.idDate = tmp2.idDate AND im.UserID = tmp2.UserID AND
im.ActionID = tmp2.ActionID AND
?.Epic1 = clng(tmp2.Epic1) AND ?.Epic2 = clng(tmp2.Epic2)
);

Simplify update-statement

I have the fallowing update-statement:
update tmp set
tmp.Anzahl=(select sum(a.BNANZ) from PAYMASTER_Journal a where a.BNARTID=tmp.ArtikelAutoID),
tmp.Betrag=(select sum(a.BNBETR) from PAYMASTER_Journal a where a.BNARTID=tmp.ArtikelAutoID),
tmp.Rabatt=(select sum(a.BNRMRBETR) from PAYMASTER_Journal a where a.BNARTID=tmp.ArtikelAutoID)
from ##tmp1 tmp
On this way, for each record in ##tmp1, there are 3 subqueries executed. ##tmp1 contains 10'000 records -> totaly 30'000 subqueries.
Because each subquery selects the same records from PAYMASTER_Journal, I am searching for a way, to update ##tmp1 with executing only one subquery per record in ##tmp1.
I hope, someone can help me about this.
Using LEFT JOIN try this
update tmp set
tmp.Anzahl=BNANZ_accumulated,
tmp.Betrag=BNBETR_accumulated,
tmp.Rabatt=BNRMRBETR_accumulated
from ##tmp1 tmp
LEFT JOIN ( SELECT BNARTID,
SUM(BNANZ) AS BNANZ_accumulated,
SUM(BNBETR) AS BNBETR_accumulated,
SUM(BNRMRBETR) AS BNRMRBETR_accumulated
FROM PAYMASTER_Journal
WHERE (ARTIKELAUSWAHL=0x30 AND BLBONSTO=0x30 AND BLZESTO=0x30 AND
STORNO=0x30 AND
BDDAT BETWEEN '20120301' AND '20130821' AND
AdressID='d68e4d8f-e60e-4482-9730-76076948df43' AND
BNFIL=5 AND
ISNULL(Preisliste, 'VK-Preisliste') = 'VK-Preisliste' AND
BNARTID=tmp.ArtikelAutoID)
GROUP BY BNARTID) a ON a.BNARTID=tmp.ArtikelAutoID
this will leave you NULL when there is no rows in PAYMASTER_Journal for a given ##tmp1.ArtikelAutoID
if you don't want to touch them, change the LEFT JOIN to INNER JOIN
I'd go with MERGE statement:
merge ##tmp1
using (select
BNARTID
,sum(BNANZ) as BNANZ
,sum(BNBETR) as BNBETR
,sum(BNRMRBETR) as BNRMRBETR
from
PAYMASTER_Journal
group by
BNARTID
) as upd
on tmp.ArtikelAutoID = upd.BNARTID
when matched then update set Anzahl=BNANZ, Betrag=BNBETR, Rabatt=BNRMRBETR;
You can do it with a single sub-query as follows.
UPDATE tmp T
INNER JOIN
(SELECT a.BNARTID
,sum(a.BNANZ) as BANAZ_SUM
,sum(a.BNBETR) as BNBETR_SUM
,sum(a.BNRMRBETR) as BNRMRBETR_SUM
FROM PAYMASTER_Journal a
WHERE a.BNARTID = tmp.ArtikelAutoID
GROUP BY
a.BNARTID
) SQ
ON SQ.BNARTID = tmp.ArtikelAutoID
SET tmp.Anzahl = SQ.BANAZ_SUM
,tmp.Betrag = SQ.BNBETR_SUM
,tmp.Rabatt = SQ.BNRMRBETR_SUM