Wednesday, September 30, 2009
Майкл К. Физерс. Эффективная работа с унаследованным кодом
Останнім часом я зустрічав багато посилань на книгу "Эффективная работа с унаследованным кодом" із дуже позитивним відгуками. Тому сподівався, що книга мені сподобається. Але прочитавши її, мої очікування не виправдались. Навпаки, книга здалась мені дуже нудною. Не знаю чия це вина, автора, перекладачів, чи мого сприйняття. Багато сторінок, я просто переглядав по діагоналі. Часто ловив себе на тому, що прочитавши якийсь розділ нічого не зрозумів, бо думки гуляли деінде, а перечитувати ніякого бажання немає. І взагалі не розумію, як можна стільки уваги приділити питанню - як розірвати залежність між кодом, і жодним словом не обмовитись про Dependecy Injection Containers (IOC). А, точно, це ж успадкований код. До того ж судячи з книги, більшість успадкованого коду - це код на C та C++. Але мене це мало цікавить.
Sunday, September 27, 2009
High/Low generator with SQL
In current project, we use High/Low generator strategy to generate identifiers for entities. (Fabio Maulo wrote a good explanation why using identity is a bad thing). The question I sometimes see in NHibernate forums is what to do if some other applications also use the same database. Usual answer is - implement some service which would generate id, and use it in that applications.
Yesterday I met similar issue. What I needed to do is to insert some fake data using SQL only. For that purpose I wrote such stored procedure:
Now I can generate identifiers in t-sql batches:
In my sandbox database I see such results:
Yesterday I met similar issue. What I needed to do is to insert some fake data using SQL only. For that purpose I wrote such stored procedure:
IF OBJECT_ID('dbo.GenerateHiLoIdRange') IS NOT NULL DROP PROCEDURE dbo.GenerateHiLoIdRange GO CREATE PROCEDURE dbo.GenerateHiLoIdRange ( @IdsCount INT ) AS /* =============================================================== */ /* == Generates a range of identifiers using High/Low strategy. == */ /* =============================================================== */ BEGIN SET NOCOUNT ON; IF @IdsCount <= 0 BEGIN RAISERROR('@IdsCount should be positive. But was %d', 16, -1, @IdsCount) RETURN(-100); END DECLARE @NextHi INT DECLARE @MaxLo INT SET @MaxLo = 100 -- Hardcoded value used over all tables in a database. BEGIN TRANSACTION UpdateHiLoTable SET @NextHi = ( SELECT next_hi FROM hibernate_unique_key WITH (UPDLOCK, ROWLOCK) ) UPDATE hibernate_unique_key SET next_hi = @NextHi + 1 + (@IdsCount - 1) / @MaxLo WHERE next_hi = @NextHi COMMIT TRANSACTION UpdateHiLoTable SELECT @NextHi * @MaxLo + IntValue FROM dbo.Integers(@IdsCount - 1) END GO
Main points of procedure:
- It uses hard coded MaxLo value (100) , which is used over all entities in our application.
- Calculates new NextHi value based on requested identifiers count and updates hibernate_unique_key table in transaction.
- To return a result set containing generated identifiers procedure needs some sort of "Integers" table. In my case i simulate such table with a table-valued function.
IF OBJECT_ID('dbo.Integers') IS NOT NULL DROP FUNCTION dbo.Integers GO CREATE FUNCTION dbo.Integers ( @MaxValue INT ) RETURNS @Integers TABLE (IntValue INT NOT NULL) AS /* =============================================================== */ /* == Returns the [0, @MaxValue] range of integer values. == */ /* =============================================================== */ BEGIN IF @MaxValue > 9999 BEGIN RETURN; END; DECLARE @Digits TABLE ( Digit INT NOT NULL PRIMARY KEY ) INSERT INTO @Digits (Digit) VALUES (0) INSERT INTO @Digits (Digit) VALUES (1) INSERT INTO @Digits (Digit) VALUES (2) INSERT INTO @Digits (Digit) VALUES (3) INSERT INTO @Digits (Digit) VALUES (4) INSERT INTO @Digits (Digit) VALUES (5) INSERT INTO @Digits (Digit) VALUES (6) INSERT INTO @Digits (Digit) VALUES (7) INSERT INTO @Digits (Digit) VALUES (8) INSERT INTO @Digits (Digit) VALUES (9) INSERT INTO @Integers SELECT Number FROM (SELECT Thousands.Digit * 1000 + Hundreds.Digit * 100 + Tens.Digit * 10 + Ones.Digit AS Number FROM @Digits Thousands CROSS JOIN @Digits Hundreds CROSS JOIN @Digits Tens CROSS JOIN @Digits Ones ) Integers WHERE Number <= @MaxValue ORDER BY Number RETURN; END GONote that number of integers in a function is limited to 10000. But that is what I need.
Now I can generate identifiers in t-sql batches:
CREATE TABLE #TempId (Id INT NOT NULL) SELECT * FROM hibernate_unique_key INSERT INTO #TempId EXEC dbo.GenerateHiLoIdRange 250 SELECT Id AS Id, 'PersonName' + CAST(ROW_NUMBER() OVER(ORDER BY Id) - 1 AS VARCHAR(255)) AS PersonName FROM #TempId DROP TABLE #TempId
In my sandbox database I see such results:
Subscribe to:
Posts (Atom)