číslování seznamu pořadí/rok (zámky, transakce, SQL)   zodpovězená otázka

ASP.NET WebForms, SQL, ADO.NET, Databáze

Chtěl bych se zeptat zda existuje varianta nastavení primárního klíče (automatického generování na úrovni databáze )ve tvaru pořadí/rok ( pořadí vždy od 1 na každý rok ). Nebo je to nutné udělat pomocí procedury v APS.NET , která bude údaj při insertu nového záznamu zapisovat do databáze ( údaj by ev. nebyl použit jako klíč ) . ev. nějaký typ na tuto proceduru.

Děkuji

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Rozhodně bych nedoporučoval dělat z tohoto údaje klíč, pro klí použijte INT nebo UNIQUEIDENTIFIER.

Pro vložení záznamu si udělejte proceduru v databázi a obalte to transakcí s isolation levelem SERIALIZABLE, aby vám tam nenastala kolize, kdyby se náhodou přidávaly dva záznamy najednou. Kolize při dvou současných přidáních by bez transakce nastala v případě, že si řekněme jedno vlákno zjistí, jaké pořadí má budoucí záznam mít, druhé si to ve stejnou chvíli zjistí také, dostanou tedy stejné hodnoty, a pak to obě dvě vlákna vloží. Dalo by se to vyřešit nějakým omezením na sloupec, ale transakce jsou lepší. Nejsem si jistý, jestli v následující ukázce kódu nemám nějakou chybu, píšu ji z hlavy. Pro transakci by možná stačil režim REPEATABLE READ, ale nejsem si 100% jistý, SERIALIZABLE by měl fungovat určitě, zajistí, že během jedné transakce nezmění jiná transakce tabulku, se kterou ta první pracuje.

Nějak takto:

CREATE PROCEDURE [PridatZaznam] (
  @Param1 INT,
  ...
) AS BEGIN

  SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
  
  BEGIN TRAN

  -- vytáhnout počet záznamů v tomto roce
  DECLARE @Poradi INT
  SELECT @Poradi = COALESCE(COUNT(*) + 1, 1) FROM [Zaznamy] WHERE YEAR([Datum]) = YEAR(GETDATE())

  -- vložit záznam
  INSERT INTO [Zaznamy] ([Poradi], [Param1], ...) VALUES (@Poradi, @Param1, ...)

  COMMIT

END
nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Dobrý den,¨

děkuji Vám - vyzkouším ( s tímto postupem se musím seznámit ).

Myslel jsem, že se to pokusím udělat nějak v asp.net ( dva sloupce - při vložení nového záznamu ( např. pomocí gridu )do jednoho přidat aktuální rok a do druhého pomocí procedury pořadí - zjistit si poslední záznam + 1 a nějak ošetřit ten přechod na nový rok ( zase začít od 1 ) např. vždy porovnat aktuální rok s rokem posledního záznamu a v případě neshody zadat 1. Je pravda, že by přidání záznamu mohl udělat druhý uživatel ve stejný čas ( ale je asi málo pravděpodobné že by to bylo najednou - nebo ne ? ).Pak ev. údaje zobrazit dohromady ( pořadí rok )

Myslíte, že by to takhle šlo ? ( ev. mi nasměroval na tvar té procedury ?

Nechci Vás s tím zdržovat.

Ještě jednou díky za Vaše rady.

P.B.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

To, že přidání dvou záznamů najednou je nepraděpodobné, sice vypadá na první pohled, nicméně ke kolizím dochází překvapivě často, náhoda je blbec a Murphyho zákon schválnosti fakt funguje.

Zobrazení údajů dohromady je pak už jednoduché:

SELECT CAST(YEAR([Datum]) AS CHAR(4)) + '-' + [Poradi], * FROM [Tabulka]
nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Izolace REPEATABLE READ v tomhle případě nepomůže. Už z jeho názvu plyne, že dovoluje opakované čtení. Tedy je možné, aby druhá transakce upravila data, která první v rámci transakce přečetla.

Jinými slovy pokud transakce A i B najednou přečtou hodnotu @Poradi, příkaz INSERT INTO projde a vloží se dvakrát ta samá hodnota.

Rozdíl u SERIALIZABLE bude ten, že při vkládání (obě transakce stejně zjistí stejné číslo) se navzájem zablokují - obě se totiž snaží zapsat do dat, které v rámci druhé transakce již byly čteny a to SERIALIZABLE nedovoluje. Transakce se zaseknou a vyčká se do rozpoznání deadlocku, což způsobí zaseknutí aplikace a vytvoření jedné chyby (dokončí se pouze jedna transakce) po vypršení timeoutu. Což také není vhodné řešení.

nahlásit spamnahlásit spam 2 / 2 odpovědětodpovědět

Dobrý den,

tak jaké řešení navrhujete ?

Díky

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Máte 2 možnosti.

1) Řešení konkurenčního přístupu na úrovni C# / Visual Basicu

2) Složitější řešení na úrovni DB - pomoc,í updatu zamykací tabulky, což znamená vytvořit tabulku s jedním sloupcem a řádkem a uvnitř procedury na uložení pak příkazem update tutu hodnotu změnit. Tím provedete okamžitý zámek a ostatní transakce se nezablokují do deadlocku, jen počkají do potvrzení transakce. Jako zamykací update stačí například:

UPDATE [novaZamykaciTabulka] SET [Sloupec]=[Sloupec]
nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Další možností je zablokovat při běžném izolačním levelu přímo tabulku čtením tzv. table hintem TABLOCX, tedy:

SELECT @Poradi = COALESCE(COUNT(*) + 1, 1) FROM [Zaznamy] WITH (TABLOCKX) WHERE YEAR([Datum]) = YEAR(GETDATE())

Ten zablokuje celou tabulku a druhá transakce jej nemůže přečíst dokud se nedokončí první transakce. Tím pádem druhá transakce získá novější číslo a nevznikne duplicita.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Možná se zeptám znovu hloupě ale pořád mi není jasné když vytvořím proceduru v ASP.net stránce ( vb.net ) která až v okamžiku vlastního ukládání vygeneruje toto číslo a uloží ho spolu s novým záznamem , jak může vůbec nastat kolize ( zapsání duplicitního čísla v případě, že to bude na úrovni db povoleno ) - vždyť by se procedura dalšího uživatele musela spustit cca současně ) - je to statisticky vůbec reálné u cca 40 uživatelů ( občasných - jedná se o evidenci pošty )? Chápu že procedura, kde se přepočítávají údaje a čeká se i na odezvu uživatele musí používat transakce ale tohle ( jedná se o přečtení údaje z databáze - vygenerování nového a zápis jak dlouho to může trvat aby se do toho někdo další trefil -není to jako první pořadí ve Sportce) ?

Díky za odpověď

PB

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Divil byste se, ale opravdu k tomu dochází překvapivě často. Na jednom webu, kam nechodilo víc než 50 návštěvníků denně, jsem měl počítadlo udělané jednoduchým způsobem - při každém přístupu na stránku to načetlo soubor pocitadlo.txt, přečetlo to z něj to jedno čslo, zvýšilo o jedničku a soubor zase zapsalo. Průměrně jednou za 14 dní s tím byl problém a počítadlo se vynulovalo.

Pro vás by možná stačilo něco jednoduššího, ale já už z principu nemám rád řešení "pro 40 lidí to stačí", protože málokterá aplikace se v budoucnu nezvrhne v něco, co se začne používat v daleko větší míře. Za rok nebo dva budou aplikaci používat stovky lidí a problém bude na světě.

A i kdyby ne, tak tohle fórum čte dost lid a někdo to samé třeba použije v daleko větší aplikaci, a opět bude problém na světě. Navíc vám rozhodně neuškodí dozvědět se, jak to udělat správně, aby to fungovalo na 100% a vždy.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

To určitě máte pravdu

Díky

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Nezbývá než souhlasit, řešení je třeba dělat tak, aby se předcházelo těmto situací a to bez ohledu na vysokou pravděpodobnost, že se to stát nemůže. Párkrát jsem taky použil argument typu je to jen takový udělátko pro pár lidí, takže tohle nebezpečí nehrozí. Realita byla taková, že při vývoji a testování bylo vše ok a po 5 minutách v ostrém provozu nastala většina ze všech těchto velice nepravděpodobných situací.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Každou takovou věcí se zavře do skříně další kostlivec. Oprava takových problémů, které se vám v lepším případě projeví pouze nějakým duplicitním zobrazováním, v horším případě chybou a možnou ztrátou dat, pak zabere mnohem víc času než jeho vyřešení už při vývoji. Navíc je dobré tohle znát. I když to může bý pro začátečníka trochu složité, v budoucnu budete vědět kam se obrátit a jak hledat informace, které potřebujete.

Zároveň jsem upravil název threadu pro lepší dohledání.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Varianta s TABLOCX je ideově mnohem čistší ;-)

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Jde to spouště při přidání řádku pomocí triggeru ?

Poradíte syntaxi pro vytvoření toho triggeru ?

Díky

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.
  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback