web-dev-qa-db-fra.com

Comment éviter que l'instruction UPDATE ne verrouille la table entière lors de la mise à jour d'un grand nombre d'enregistrements

Je suis assez nouveau pour les serrures et les allusions.

J'ai une table avec de très fréquentes opérations SELECT et INSERT. La table contient 11 millions d’enregistrements.

J'ai ajouté une nouvelle colonne et j'ai besoin de copier les données d'une colonne existante de la même table dans la nouvelle colonne.

Je prévois d'utiliser l'indicateur ROWLOCK pour éviter la remontée des verrous au niveau des tableaux et le blocage de toutes les autres opérations sur la table. Par exemple:

UPDATE 
    SomeTable WITH (ROWLOCK)
SET
    NewColumn = OldColumn

Des questions:

  1. Est-ce qu'une NOLOCK au lieu de ROWLOCK? Remarque: une fois les enregistrements insérés dans la table, la valeur de OldColumn ne change pas. Par conséquent, NOLOCK ne provoque pas de lectures modifiées.
  2. NOLOCK a-t-il même un sens dans ce cas, car SQL Server devrait malgré tout obtenir des verrous de mise à jour pour UPDATE.
  3. Y a-t-il un meilleur moyen d'y parvenir?

Je sais que les allusions sont à éviter et que SQL Server fait généralement des choix plus judicieux, mais je ne souhaite pas que la table soit verrouillée pendant cette mise à jour.

7
HappyTown

Essayez et mettez à jour en lots.

DECLARE @Batch INT = 1000
DECLARE @Rowcount INT = @Batch


WHILE @Rowcount > 0
    BEGIN
        ;WITH CTE AS 
        (
            SELECT TOP (@Batch) NewColumn,OldColumn 
            FROM SomeTable 
            WHERE NewColumn <> OldColumn
                  OR (NewColumn IS NULL AND OldColumn IS NOT NULL)
        )
        UPDATE cte
            SET NewColumn = OldColumn;
        SET @Rowcount = @@ROWCOUNT
    END
3
pacreely

J'ai adopté l'approche de @ pacreely (voir sa réponse à cette question) consistant à mettre à jour par lots et créé une variation update...top. J'ai ajouté le conseil (rowlock) dire au serveur SQL de garder les verrous au niveau des lignes.

Voir update ... top pour plus de détails. Notez également que vous ne pouvez pas utiliser order by lorsque vous utilisez top dans une instruction update, insert, merge, delete afin que les lignes référencées ne soient organisées dans aucun ordre.

declare @BatchSize  int = 1000
declare @RowCount   int = @BatchSize

while @RowCount > 0
begin
    update top (@BatchSize) SomeTable with (rowlock)
    set NewColumn = OldColumn
    where 
        NewColumn <> OldColumn      or
        (
            NewColumn is null       and
            OldColumn is not null
        )
    select @RowCount = @@rowcount
end
2
HappyTown

Cette question a une réponse sur le site des administrateurs de base de données sur StackExchange ici: https://dba.stackexchange.com/questions/127158/how-to-get-sql-insert-and-or-update-to-not- lock-whole-table-on-ms-sql-server

1
STLDev