'Id' dengan format: YYYYNNNNNN dengan bagian NNNNNN memulai kembali setiap tahun


11

Saya memiliki persyaratan bisnis yang setiap catatan dalam tabel Faktur memiliki id yang terlihat seperti YYYYNNNNNNN.

Bagian NNNNNN perlu memulai kembali pada awal setiap tahun. Jadi baris pertama yang dimasukkan pada 2016 akan terlihat seperti 2016000001 dan yang kedua seperti 2016000002 dll. Katakanlah rekor terakhir untuk 2016 adalah 2016123456, Baris berikutnya (tahun 2017) akan terlihat seperti 2017000001

Saya tidak perlu id ini menjadi kunci utama dan saya menyimpan tanggal pembuatan juga. Idenya adalah bahwa 'tampilan id' ini unik (jadi saya bisa menanyakannya) dan dapat dikelompokkan berdasarkan manusia, berdasarkan tahun.

Tidak mungkin ada catatan yang akan dihapus; Namun, saya akan cenderung kode pertahanan terhadap sesuatu seperti itu.

Apakah ada cara saya bisa membuat id ini tanpa harus mencari id maksimum tahun ini setiap kali memasukkan baris baru?

Ide ide:

  • A CreateNewInvoiceSP, yang mendapat MAXnilai untuk tahun itu (yucky)
  • Beberapa fitur magis bawaan untuk melakukan hal ini (saya dapat bermimpi dengan benar)
  • Mampu menentukan beberapa UDF atau sesuatu di dalam IDENTITYatau DEFAULTdeklarasi (??)
  • Tampilan yang menggunakan PARTITION OVER + ROW()(dihapus akan bermasalah)
  • Pemicu aktif INSERT(masih perlu menjalankan beberapa MAXpermintaan :()
  • Pekerjaan latar belakang tahunan, memperbarui meja dengan MAX untuk setiap tahun dimasukkan yang kemudian saya ... Sesuatu ?!

Semuanya agak tidak ideal. Ada ide atau variasi yang disambut baik!


Anda memiliki beberapa jawaban yang baik tetapi jika Anda memiliki tahun, id sebagai PK maka pilih max cukup cepat.
paparazzo

menggunakan kueri id pilihan pilih adalah praktik umum. gunakan itu.
Uğur Gümüşhan

Jawaban:


17

Ada 2 elemen untuk bidang Anda

  • Tahun
  • Nomor yang bertambah secara otomatis

Mereka tidak perlu disimpan sebagai satu bidang

Contoh:

  • Kolom tahun yang memiliki default YEAR(GETDATE())
  • Kolom angka berdasarkan urutan.

Lalu buat kolom terkomputasi yang menyatukan mereka (dengan pemformatan yang sesuai). Urutan dapat diatur ulang saat pergantian tahun.

Contoh kode dalam SQLfiddle : * (SQLfiddle tidak selalu berfungsi)

-- Create a sequence
CREATE SEQUENCE CountBy1
    START WITH 1
    INCREMENT BY 1 ;

-- Create a table
CREATE TABLE Orders
    (Yearly int NOT NULL DEFAULT (YEAR(GETDATE())),
    OrderID int NOT NULL DEFAULT (NEXT VALUE FOR CountBy1),
    Name varchar(20) NOT NULL,
    Qty int NOT NULL,
    -- computed column
    BusinessOrderID AS RIGHT('000' + CAST(Yearly AS VARCHAR(4)), 4)
                     + RIGHT('00000' + CAST(OrderID AS VARCHAR(6)), 6),
    PRIMARY KEY (Yearly, OrderID)
    ) ;


-- Insert two records for 2015
INSERT INTO Orders (Yearly, Name, Qty)
    VALUES
     (2015, 'Tire', 7),
     (2015, 'Seat', 8) ;


-- Restart the sequence (Add this also to an annual recurring 'Server Agent' Job)
ALTER SEQUENCE CountBy1
    RESTART WITH 1 ;

-- Insert three records, this year.
INSERT INTO Orders (Name, Qty)
    VALUES
     ('Tire', 2),
     ('Seat', 1),
     ('Brake', 1) ;

1
Mungkin lebih bersih memiliki satu urutan per tahun. Dengan begitu tidak perlu menjalankan DDL sebagai bagian dari operasi reguler.
usr

@ GBN Jadi saya akan membutuhkan pekerjaan latar belakang untuk memulai kembali SEQUENCE pada awal setiap tahun?
DarcyThomas

@ usr Sedihnya Anda tidak dapat menggunakan NEXT VALUE FORdalam CASEpernyataan (saya mencoba)
DarcyThomas

8

Apakah Anda mempertimbangkan untuk membuat bidang identitas dengan seed = 2016000000?

 create table Table1 (
   id bigint identity(2016000000,1),
   field1 varchar(20)...
)

Benih ini harus ditingkatkan secara otomatis setiap tahun, misalnya pada malam 2017/1/1 Anda harus menjadwalkan

DBCC CHECKIDENT (Table1, RESEED, 2017000000)

Tapi saya sudah melihat masalah dengan desain, misalnya: bagaimana jika Anda memiliki jutaan catatan?


2
Masalah lain adalah jika catatan tidak muncul secara kronologis. Identitas mungkin bukan jalan yang harus ditempuh jika ini masalahnya.
Daniel Hutmacher

@LiyaTansky Dalam kasus saya, saya diberitahu bahwa seharusnya hanya 50 ribu catatan per tahun. Tapi saya mengerti maksud Anda soal rapuh dengan baris
1kk

1

Apa yang saya lakukan dalam skenario ini adalah mengalikan tahun dengan 10 ^ 6 dan menambahkan nilai urutan itu. Ini memiliki keuntungan karena tidak memerlukan bidang yang dikomputasi dengan overhead kecil yang sedang berlangsung dan bidang tersebut dapat digunakan sebagai a PRIMARY KEY.

Ada dua kemungkinan gotcha:

  • pastikan bahwa pengganda Anda cukup besar agar tidak pernah habis, dan

  • Anda tidak dijamin urutan tanpa celah karena caching urutan.

Saya bukan ahli SQL Server, tetapi Anda mungkin dapat mengatur acara untuk memicu pada 201x 00:00:00 untuk mengatur ulang urutan Anda ke nol. Itu juga yang saya lakukan di Firebird (atau apakah itu Interbase?).


1

Sunting: Solusi ini tidak berfungsi di bawah beban

Saya bukan penggemar pemicu, tapi sepertinya ini yang terbaik yang bisa saya lakukan.

Pro:

  • Tidak ada pekerjaan latar belakang
  • Dapat membuat kueri cepat di DisplayId
  • Pemicu tidak perlu memindai bagian NNNNNN sebelumnya
  • Akan memulai kembali bagian NNNNN setiap tahun
  • Akan bekerja jika ada lebih dari 100000 baris per tahun
  • Tidak memerlukan pembaruan skema (misalnya, urutan ulang) untuk tetap berfungsi di masa depan

Edit: Kontra:

  • Akan gagal di bawah beban (kembali ke papan gambar)

(Kredit untuk @gbn ketika saya mengambil beberapa inspirasi dari jawaban mereka) (Umpan balik & menunjukkan kesalahan yang jelas, selamat datang :)

Tambahkan beberapa COLUMNs dan baruINDEX

ALTER TABLE dbo.Invoices
ADD     [NNNNNNId]      INT  NULL 

ALTER TABLE dbo.Invoices
ADD [Year]              int NOT NULL DEFAULT (YEAR(GETDATE()))

ALTER TABLE dbo.Invoices
ADD [DisplayId]     AS  'INV' +
                        CAST([Year] AS VARCHAR(4))+
                        RIGHT('00000' + CAST([NNNNNNId] AS VARCHAR(4)),  IIF (5  >= LEN([NNNNNNId]), 5, LEN([NNNNNNId])) )                  

EXEC('CREATE NONCLUSTERED INDEX IX_Invoices_DisplayId
ON dbo.Invoices (DisplayId)')

Tambahkan yang baru TRIGGER

CREATE TRIGGER Invoices_DisplayId
ON dbo.Invoices
  AFTER  INSERT
AS 
BEGIN

SET NOCOUNT ON;    

UPDATE dbo.Invoices
SET NNNNNNId = CalcDisplayId
FROM (SELECT I.ID, IIF (Previous.Year = I.Year , (ISNULL(Previous.NNNNNNId,0) + 1), 1) AS CalcDisplayId  FROM
        (SELECT 
            ID  
           ,NNNNNNId 
           ,[year]
        FROM  dbo.Invoices
        ) AS Previous
    JOIN inserted AS I 
    ON Previous.Id = (I.Id -1) 
    ) X
WHERE 
   X.Id = dbo.Invoices.ID       
END
GO

Saya sangat merekomendasikan untuk tidak melakukan ini. Kemungkinan menemui jalan buntu dan menyebabkan kegagalan memasukkan setelah Anda menerima beban ringan. Sudahkah Anda meletakkan salinan ke dalam basis data dummy dan menggedornya dengan beberapa lusin utas sekaligus melakukan sisipan (dan mungkin juga memilih / memperbarui / menghapus) untuk melihat apa yang terjadi?
Cody Konior

@CodyKonior secara mendasar cacat atau mungkinkah dibangkitkan kembali dengan sedikit penguncian yang bijaksana? Jika tidak, bagaimana Anda akan mendekati masalah?
DarcyThomas

Hmmm. Berlari dengan 10 utas. Tidak yakin apakah itu kunci mati, tetapi saya mendapatkan beberapa kondisi lomba. Di mana satu pemicu selesai, sebelum pemicu baris sebelumnya selesai. Ini mengarah ke banyak NULLnilai yang dimasukkan. Kembali ke papan gambar ...
DarcyThomas

Bencana dihindari kemudian :-) Rahasia saya adalah bahwa saya mengenali pola untuk sesuatu yang saya lakukan sekitar lima tahun yang lalu. Saya hanya tahu bahwa cara Anda memindai tabel di dalam pelatuk mencari urutan berikutnya membuat segalanya menjadi kurang beban. Saya tidak ingat bagaimana saya menyelesaikannya tetapi saya bisa memeriksanya nanti.
Cody Konior

@CodyKonior Saya tidak berpikir sedang melakukan pemindaian ( ON Previous.Id = (I.Id -1) hanya harus mencari), tapi ya masih tidak berfungsi. Jika saya bisa mengunci tabel (?) Selama memasukkan dan memicu maka saya pikir itu akan berhasil. Tapi itu terdengar seperti bau kode juga.
DarcyThomas
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.