webForumDet fria alternativet

Slå samman dubletter i databas

23 svar · 827 visningar · startad av devotion

devotionMedlem sedan jan. 20013 582 inlägg
#1

Hej på er! :)

Jag har en liten fundering.... ;)

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.

Mvh
Henrik

sql.pngsql2.png
LarsGMedlem sedan dec. 200012 464 inlägg
#2

--
-- aggregera
--

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)

Flyttas från ASP

emissionMedlem sedan dec. 19996 721 inlägg
#3

...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.

devotionMedlem sedan jan. 20013 582 inlägg
#4

Hej!

Det ser ju fint ut.. :)

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'.

Mvh
Henrik

Peter SMedlem sedan dec. 20025 483 inlägg
#5

Testa

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)
emissionMedlem sedan dec. 19996 721 inlägg
#6
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)

Det är ArtNr som kopplar ihop..

devotionMedlem sedan jan. 20013 582 inlägg
#7

Hej! :)

Inga felmeddelande, men:

(0 row(s) affected)

Mvh
Henrik

devotionMedlem sedan jan. 20013 582 inlägg
#8

emission skrev:

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)

Det är ArtNr som kopplar ihop..

Ja, självklart.... :r

Där satt den.... iaf den första delen... :)

Mvh
Henrik

emissionMedlem sedan dec. 19996 721 inlägg
#9

Andra delen

  delete from tblMaterial
    where id not in
          (select max(id)
              from tblMaterialas q
           where tblMaterial.ArtNr= q.ArtNr)
devotionMedlem sedan jan. 20013 582 inlägg
#10

Då blir det så här om allt slås samman:

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)

Tack LarsG, emission och Peter S :birp

Mvh
Henrik

emissionMedlem sedan dec. 19996 721 inlägg
#11

..och glöm inte transaktionen.

devotionMedlem sedan jan. 20013 582 inlägg
#12

emission skrev:

..och glöm inte transaktionen.

Det där får du nog förklara.. :(

EDIT!

Hade missat ett inlägg ovan...

Jag har aldrig jobbat med transaktioner, så du får gärna förklara/berätta hur jag ska göra...

Tänkte dock att en sådan dubbelfråga kan skapa problem...

Mvh
Henrik

emissionMedlem sedan dec. 19996 721 inlägg
#13

Jo, som jag skrev tidigare.

"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
devotionMedlem sedan jan. 20013 582 inlägg
#14

Så här?

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

Mvh
Henrik

emissionMedlem sedan dec. 19996 721 inlägg
#15

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...

devotionMedlem sedan jan. 20013 582 inlägg
#16

:)
God morgon!

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... ;) )

Angående den andra metoden, ska jag leta lite! :)

Mvh
Henrik

devotionMedlem sedan jan. 20013 582 inlägg
#17

Hej igen! :)

Kan man kombinera det hela med att "logga" det som utförs?

Kan man köra en transaktion i en asp-sida?

Är det säkert att köra transaktionen?

Mvh
Henrik

emissionMedlem sedan dec. 19996 721 inlägg
#18

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

Säkert? Hur menar du?

devotionMedlem sedan jan. 20013 582 inlägg
#19

Hej! :)
Med säkert menar jag, att jag inte får korrupt data... Dvs vågar jag köra transaktionen? :)

Hur skrivar man in raden i tabellen då? Eller hur jag skriver vet jag, men vad ska jag skriva? :)

Mvh
Henrik

emissionMedlem sedan dec. 19996 721 inlägg
#20

devotion skrev:

Hej! :)
Med säkert menar jag, att jag inte får korrupt data... Dvs vågar jag köra transaktionen? :)

Jupp.

devotion skrev:

Hur skrivar man in raden i tabellen då? Eller hur jag skriver vet jag, men vad ska jag skriva? :)

Det får du banne mig hitta på själv :)

260 ms totalt · 3 externa anrop · v20260731065814-full.30151723
134 ms — hämta forumlista (db)
120 ms — hämta statistik (db)
137 ms — hämta tråd, inlägg och bilagor (db)