sql >> Databasteknik >  >> RDS >> Sqlserver

Uppdatera om annorlunda/ändrats

Under frågekompilering och exekvering tar SQL Server sig inte tid att ta reda på om en UPDATE-sats faktiskt kommer att ändra några värden eller inte. Den utför bara skrivningarna som förväntat, även om det är onödigt.

I scenariot som

update table1 set col1 = 'hello'

du kanske tror att SQL inte kommer att göra någonting, men det gör det – det kommer att utföra alla nödvändiga skrivningar som om du faktiskt hade ändrat värdet. Detta inträffar för både den fysiska tabellen (eller klustrade index) såväl som alla icke-klustrade index som definieras i den kolumnen. Detta orsakar skrivningar till de fysiska tabellerna/indexen, omräkning av index och transaktionsloggskrivningar. När du arbetar med stora datamängder finns det enorma prestandafördelar med att bara uppdatera rader som kommer att få en förändring.

Om vi ​​vill undvika överkostnaderna med dessa skrivningar när det inte är nödvändigt måste vi hitta ett sätt att kontrollera behovet av att uppdateras. Ett sätt att kontrollera behovet av att uppdatera skulle vara att lägga till något som "där kol <> 'hej'.

update table1 set col1 = 'hello' where col1 <> 'hello'

Men detta skulle inte fungera bra i vissa fall, till exempel om du uppdaterade flera kolumner i en tabell med många rader och endast en liten delmängd av dessa rader faktiskt skulle få sina värden ändrade. Detta beror på behovet av att sedan filtrera på alla dessa kolumner, och icke-likvärdiga predikat kan i allmänhet inte använda indexsökningar, och overheaden för tabell- och indexskrivningar och transaktionsloggposter som nämnts ovan.

Men det finns ett mycket bättre alternativ med en kombination av en EXISTS-sats med en EXCEPT-sats. Tanken är att jämföra värdena i målraden med värdena i den matchande källraden för att avgöra om en uppdatering faktiskt behövs. Titta på den modifierade frågan nedan och undersök det ytterligare frågefiltret som börjar med EXISTS. Notera hur inuti EXISTS-satsen SELECT-satserna inte har någon FROM-sats. Den delen är särskilt viktig eftersom detta bara lägger till en extra konstant skanning och en filteroperation i frågeplanen (kostnaden för båda är trivial). Så det du slutar med är en mycket lätt metod för att avgöra om en UPPDATERING ens behövs i första hand, för att undvika onödig skrivoverhead.

update table1 set col1 = 'hello'
/* AVOID NET ZERO CHANGES */
where exists 
    (
    /* DESTINATION */
    select table1.col1
    except
    /* SOURCE */
    select col1 = 'hello'
    )

Detta ser alltför komplicerat ut jämfört med att leta efter uppdateringar i en enkel WHERE-sats för den enkla scenerio i den ursprungliga frågan när du uppdaterar ett värde för alla rader i en tabell med ett bokstavligt värde. Den här tekniken fungerar dock mycket bra om du uppdaterar flera kolumner i en tabell, och källan till din uppdatering är en annan fråga och du vill minimera skrivningar och transaktionsloggposter. Det fungerar också bättre än att testa alla fält med <>.

Ett mer komplett exempel kan vara

update table1
   set col1 = 'hello',
       col2 = 'hello',
       col3 = 'hello'
/* Only update rows from CustomerId 100, 101, 102 & 103 */
where table1.CustomerId IN (100, 101, 102, 103)
/* AVOID NET ZERO CHANGES */
  and exists 
    (
    /* DESTINATION */
    select table1.col1
           table1.col2
           table1.col3
    except
    /* SOURCE */
    select z.col1,
           z.col2,
           z.col3
      from #anytemptableorsubquery z
     where z.CustomerId = table1.CustomerId
    )


  1. Massinsätta eller uppdatera med Hibernate?

  2. Anropar RESTful Web Services från PostgreSQL procedur/funktion

  3. Får fel i konsolen:Det gick inte att ladda resurs:net::ERR_CONNECTION_RESET

  4. SQL Fiddle-utgångsfel