Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

SQL Server Уникален композитен ключ от две полета с автоматично увеличение на второ поле

Откакто някой е публикувал подобен въпрос, се замислям над това. Първият проблем е, че DBs не предоставят "разделяеми" последователности (които биха рестартирали/запомняли въз основа на различни ключове). Второто е, че SEQUENCE обекти, които са предоставени са насочени към бърз достъп и не могат да бъдат връщани назад (т.е. вие ще получи пропуски). Това по същество изключва използването на вградена помощна програма... което означава, че трябва да пуснем нашата собствена.

Първото нещо, от което ще се нуждаем, е таблица за съхраняване на нашите последователни номера. Това може да бъде доста просто:

CREATE TABLE Invoice_Sequence (base CHAR(1) PRIMARY KEY CLUSTERED,
                               invoiceNumber INTEGER);

В действителност base колоната трябва да бъде препратка с чужд ключ към каквато и да е таблица/идентификатор, който дефинира бизнеса(ите)/субектите, за които издавате фактури. В тази таблица искате записите да бъдат уникални за всеки издаден субект.

След това искате съхранена процедура, която ще вземе ключ (base ) и изплюйте следващото число в последователността (invoiceNumber ). Наборът от необходими ключове ще варира (т.е. някои номера на фактури трябва да съдържат годината или пълната дата на издаване), но основният формуляр за тази ситуация е както следва:

CREATE PROCEDURE Next_Invoice_Number @baseKey CHAR(1), 
                                     @invoiceNumber INTEGER OUTPUT 
AS MERGE INTO Invoice_Sequence Stored
              USING (VALUES (@baseKey)) Incoming(base)
                 ON Incoming.base = Stored.base
   WHEN MATCHED THEN UPDATE SET Stored.invoiceNumber = Stored.invoiceNumber + 1
   WHEN NOT MATCHED BY TARGET THEN INSERT (base) VALUES(@baseKey)
   OUTPUT INSERTED.invoiceNumber ;;

Имайте предвид, че:

  1. Вие трябва стартирайте това в сериализирана транзакция
  2. Транзакцията трябва да бъде същият, който се вмъква в таблицата на местоназначението (фактура).

Точно така, все още ще получавате блокиране на бизнес при издаване на номера на фактури. Виене можете Избягвайте това, ако номерата на фактурите трябва да са последователни, без пропуски – докато редът действително бъде ангажиран, той може да бъде върнат назад, което означава, че номерът на фактурата няма да бъде издаден.

Сега, тъй като не искате да си спомняте да извикате процедурата за записа, увийте го в тригер:

CREATE TRIGGER Populate_Invoice_Number ON Invoice INSTEAD OF INSERT
AS 
  DECLARE @invoiceNumber INTEGER
  BEGIN
    EXEC Next_Invoice_Number Inserted.base, @invoiceNumber OUTPUT
    INSERT INTO Invoice (base, invoiceNumber) 
                VALUES (Inserted.base, @invoiceNumber)
  END

(очевидно имате повече колони, включително други, които трябва да се попълват автоматично - ще трябва да ги попълните)
...които след това можете да използвате, като просто кажете:

INSERT INTO Invoice (base) VALUES('A');

И така, какво направихме? Най-вече цялата тази работа беше за свиване на броя на редовете, заключени от транзакция. До този INSERT е ангажиран, има само два заключени реда:

  • Редът в Invoice_Sequence запазване на поредния номер
  • Редът в Invoice за новата фактура.

Всички останали редове за конкретна base са безплатни - те могат да бъдат актуализирани или запитани по желание (изтриването на информация от този вид система има тенденция да изнервя счетоводителите). Вероятно трябва да решите какво да се случи, когато заявките обикновено включват чакащата фактура...



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Архивно шифроване на база данни на SQL Server

  2. Мониторинг на TempDB на SQL Server чрез използване на динамични изгледи за управление (DMV)

  3. Как да използвате UPDATE от SELECT в SQL Server

  4. Улавяне на броя от SQL заявка

  5. Намерете sql записи, съдържащи подобни низове