Jag skulle vilja göra ett script som slår samman dubletter av artiklar i en databastabell.
Dvs finns en artikel med samma id på 2 ställen med olika antal ska en ny post skapas med det summerade antalet från de två ursprungsposterna. De två ursprungsposterna skall raderas.
Jag bifogar en bild på hur det ser ut i databasen innan en sammanslagning.
Resultatet skall bli som i den andra bilden...
Kan väl tillägga att jag använder SQL-server och att scriptet ska köras på "hela tabellen" schemalagt.
update tab as q
set antal = (select sum(antal)
from tab
where tab.artikelid = q.artikelid)
where artikelid in
(select artikelid
from tab
group by artikelid
having count(*) > 1)
--
-- ta bort duplikat
--
delete from tab
where id not in
(select max(id)
from tab as q
where tab.artikelid = q.artikelid)
...och en sådan dubbelfråga vill du köra atomiskt, dvs. antingen körs ingen eller så körs båda (duktigt korrupt data blir resultatet annars). Med andra ord bör du kapsla in frågan i en transaktion.
Jag försökte dock att ändra till mina tabellnamn och kolumner och kanske gjort fel...
update tblMaterial as q
set Amount = (select sum(Amount)
from tblMaterial
where tblMaterial.Id = q.Id)
where Id in
(select Id
from tblMaterial
group by Id
having count(*) > 1)
Ger
Msg 156, Level 15, State 1, Line 1
Felaktig syntax nära nyckelordet 'as'.
Msg 156, Level 15, State 1, Line 5
Felaktig syntax nära nyckelordet 'where'.
update tblMaterial
set Amount = (select sum(Amount)
from tblMaterial
where tblMaterial.Id = q.Id)
from tblMaterial as q
where Id in
(select Id
from tblMaterial
group by Id
having count(*) > 1)
update q set Amount = (select sum(Amount)
from tblMaterial
where tblMaterial.ArtNr = q.ArtNr)
from tblMaterial as q
where ArtNr in
(select ArtNr
from tblMaterial
group by ArtNr
having count(*) > 1)
update q set Amount = (select sum(Amount)
from tblMaterial
where tblMaterial.ArtNr = q.ArtNr)
from tblMaterial as q
where ArtNr in
(select ArtNr
from tblMaterial
group by ArtNr
having count(*) > 1)
update q set Amount = (select sum(Amount)
from tblMaterial
where tblMaterial.ArtNr = q.ArtNr)
from tblMaterial as q
where ArtNr in
(select ArtNr
from tblMaterial
group by ArtNr
having count(*) > 1)
delete from tblMaterial
where id not in
(select max(id)
from tblMaterial as q
where tblMaterial.ArtNr= q.ArtNr)
"en sådan dubbelfråga vill du köra atomiskt, dvs. antingen körs ingen eller så körs båda (duktigt korrupt data blir resultatet annars). Med andra ord bör du kapsla in frågan i en transaktion."
Om fråga 1 lyckas och fråga 2 misslyckas så kommer du att få en tråkig helg.
BEGIN TRAN
--Gör hemska saker i databasen
IF (@@ERROR<>0)
BEGIN
--Något gick visst snett. Jag ångrar alltihop.
ROLLBACK TRAN
END
ELSE
BEGIN
--Wehee! Det gick ju som smort. Jag ser till att ändringarna utförs.
COMMIT TRAN
END
BEGIN TRAN
update q set Amount = (select sum(Amount)
from tblMaterial
where tblMaterial.ArtNr = q.ArtNr)
from tblMaterial as q
where ArtNr in
(select ArtNr
from tblMaterial
group by ArtNr
having count(*) > 1)
delete from tblMaterial
where id not in
(select max(id)
from tblMaterial as q
where tblMaterial.ArtNr= q.ArtNr)
IF (@@ERROR<>0)
BEGIN
ROLLBACK TRAN
END
ELSE
BEGIN
COMMIT TRAN
END
Helt rätt tänkt Henrik! Tyvärr inte rätt (inte konstigt med min slappa beskrivning)
Saken är den att @@ERROR nollställs vid varje fråga så man måste kontrollera värdet direkt efter varje fråga, eller mellanlagra den i en variabel.
Ex:
DECLARE @updaterr int, @deleteerr int
BEGIN TRAN
update q set Amount = (select sum(Amount)
from tblMaterial
where tblMaterial.ArtNr = q.ArtNr)
from tblMaterial as q
where ArtNr in
(select ArtNr
from tblMaterial
group by ArtNr
having count(*) > 1)
SELECT @updaterr=@@ERROR
--Här skulle man kunna hoppa över resten, och direkt köra en rollback, eftersom nästa fråga ändå inte ska köras.
delete from tblMaterial
where id not in
(select max(id)
from tblMaterial as q
where tblMaterial.ArtNr= q.ArtNr)
SELECT @deleteerr=@@ERROR
IF (@updaterr<>0 or @deleteerr<>0)
BEGIN
ROLLBACK TRAN
END
ELSE
BEGIN
COMMIT TRAN
END
Kom precis på att du väl kör SQL Server 2005? Då finns en efterlängtad TRY-CATCH-metod att ta till också, men den har jag knappt provat så jag överlåter informationsspridningen till Google...
Hehe... Detta är lite överkurs för mig... Men, men, man får väl lära sig något nytt!
Då ska det se ut så här:
DECLARE @updaterr int, @deleteerr int
BEGIN TRAN
update q set Amount = (select sum(Amount)
from tblMaterial
where tblMaterial.ArtNr = q.ArtNr)
from tblMaterial as q
where ArtNr in
(select ArtNr
from tblMaterial
group by ArtNr
having count(*) > 1)
SELECT @updaterr=@@ERROR
delete from tblMaterial
where id not in
(select max(id)
from tblMaterial as q
where tblMaterial.ArtNr= q.ArtNr)
SELECT @deleteerr=@@ERROR
IF (@updaterr<>0 or @deleteerr<>0)
BEGIN
ROLLBACK TRAN
END
ELSE
BEGIN
COMMIT TRAN
END
Är detta säkert?
Min tanke är at schemalägga detta för att just plocka bort dubletter 1 gång per dygn... (städa upp efter användarna... ;) )
Du kan definitivt logga det som utförs. Skriv in rad i en tabell, helt enkelt. Gör det dock inte inne i samma transaktion, eftersom en rollback skulle ta bort loggningen också.
Ja, du kan köra en transaktion på en asp-sida. Antingen genom att skriva som ovan, med BEGIN TRANS etc., eller genom att köra
on error resume next
objConn.BeginTrans
objConn.Execute("INSERT ......
if err.Number<>0 or If objConn.errors.count>0 then
objConn.RollbackTrans
else
objConn.CommitTrans
end if
on error goto 0