sql >> Databáze >  >> RDS >> Sqlserver

Jak získat další číslo v pořadí

Pokud neudržujete pultový stůl, máte dvě možnosti. V rámci transakce nejprve vyberte MAX(seq_id) pomocí jedné z následujících tabulkových nápověd:

  1. WITH(TABLOCKX, HOLDLOCK)
  2. WITH(ROWLOCK, XLOCK, HOLDLOCK)

TABLOCKX + HOLDLOCK je trochu přehnané. Blokuje běžné výběrové příkazy, které lze považovat za těžké i když je transakce malá.

A ROWLOCK, XLOCK, HOLDLOCK nápověda k tabulce je pravděpodobně lepší nápad (ale:přečtěte si alternativu s protitabulkou dále). Výhodou je, že neblokuje běžné příkazy select, tj. když se příkazy select neobjeví v SERIALIZABLE transakce, nebo když příkazy select neposkytují stejné rady pro tabulku. Pomocí ROWLOCK, XLOCK, HOLDLOCK bude stále blokovat příkazy insert.

Samozřejmě si musíte být jisti, že žádná jiná část vašeho programu nezvolí MAX(seq_id) bez těchto tabulkových rad (nebo mimo SERIALIZABLE transakce) a poté tuto hodnotu použijte k vložení řádků.

Všimněte si, že v závislosti na počtu řádků, které jsou uzamčeny tímto způsobem, je možné, že SQL Server eskaluje zámek na zámek tabulky. Další informace o eskalaci uzamčení zde .

Procedura vkládání pomocí WITH(ROWLOCK, XLOCK, HOLDLOCK) bude vypadat následovně:

DECLARE @target_model INT=3;
DECLARE @part VARCHAR(128)='Spine';
BEGIN TRY
    BEGIN TRANSACTION;
    DECLARE @max_seq INT=(SELECT MAX(seq) FROM dbo.table_seq WITH(ROWLOCK,XLOCK,HOLDLOCK) WHERE [email protected]_model);
    IF @max_seq IS NULL SET @max_seq=0;
    INSERT INTO dbo.table_seq(part,seq,model)VALUES(@part,@max_seq+1,@target_model);
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION;
END CATCH

Alternativní a pravděpodobně lepší nápad je mít počítadlo stůl a poskytněte tyto rady k tabulce na pultovém stole. Tato tabulka by vypadala takto:

CREATE TABLE dbo.counter_seq(model INT PRIMARY KEY, seq_id INT);

Potom byste změnili postup vkládání následovně:

DECLARE @target_model INT=3;
DECLARE @part VARCHAR(128)='Spine';
BEGIN TRY
    BEGIN TRANSACTION;
    DECLARE @new_seq INT=(SELECT seq FROM dbo.counter_seq WITH(ROWLOCK,XLOCK,HOLDLOCK) WHERE [email protected]_model);
    IF @new_seq IS NULL 
        BEGIN SET @new_seq=1; INSERT INTO dbo.counter_seq(model,seq)VALUES(@target_model,@new_seq); END
    ELSE
        BEGIN SET @new_seq+=1; UPDATE dbo.counter_seq SET [email protected]_seq WHERE [email protected]_model; END
    INSERT INTO dbo.table_seq(part,seq,model)VALUES(@part,@new_seq,@target_model);
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION;
END CATCH

Výhodou je, že se používá méně řádkových zámků (tj. jeden na model v dbo.counter_seq ) a eskalace zámku nemůže uzamknout celý dbo.table_seq tabulky a tím blokuje příkazy select.

To vše můžete vyzkoušet a sami vidět účinky umístěním WAITFOR DELAY '00:01:00' po výběru sekvence z counter_seq a pohráváte si s tabulkami na druhé kartě SSMS.

PS1:Pomocí ROW_NUMBER() OVER (PARTITION BY model ORDER BY ID) není dobrý způsob. Pokud se odstraní/přidají řádky nebo se změní ID, změní se pořadí (vezměte v úvahu ID faktur, která by se nikdy neměla změnit). Také z hlediska výkonu je nutnost určovat čísla všech předchozích řádků při načítání jednoho řádku špatný nápad.

PS2:Nikdy bych nepoužil vnější zdroje k zajištění zamykání, když SQL Server již poskytuje zamykání prostřednictvím úrovní izolace nebo jemných tabulkových rad.



  1. Jak jsou tabulky innodb uzamčeny, když je zpracován trigger ON INSERT?

  2. Jak odstranit duplikáty ze seznamu odděleného mezerou pomocí Oracle regexp_replace?

  3. Zdá se, že MySQL Query nesplňuje očekávání

  4. Jak vložit příchozí e-mailovou zprávu do databáze mySQL?