Cơ sở dữ liệu

Số hóa đơn liên tục: vì sao IDENTITY và SEQUENCE nhảy số

Quy định về số hóa đơn điện tử từ 01/7/2026, vì sao IDENTITY và SEQUENCE để lại lỗ trong dãy số, và cách cấp số không hở bằng bảng đếm trên SQL Server.

Mục lục
  1. 1. Quy định nói gì về số hóa đơn
  2. 2. Hai đường làm hở dãy số
  3. 3. Đo trên LocalDB: mỗi lần dừng mất bao nhiêu số
  4. 4. Bảng đếm: cấp số trong cùng giao dịch
  5. 5. Cái giá: mọi phiên cùng ký hiệu xếp hàng
  6. 6. Đặt con số cạnh 11.700 hóa đơn mỗi ngày
  7. Những chỗ hay hiểu sai
  8. Đọc tiếp
  9. Nguồn

8:30 sáng 15/9/2026, kế toán của BanHang đối chiếu dãy hóa đơn ký hiệu 1C26TBH của ngày hôm trước. Hóa đơn lập lúc 14:02 mang số 3.006.412, hóa đơn kế tiếp lúc 14:09 mang số 3.007.001: 588 số không có hóa đơn, vì 14:05 máy chủ database mất điện. Sáng hôm đó còn một chỗ hở một số, đúng lúc ký số lỗi và giao dịch ghi hóa đơn rollback. Số đang lấy từ một SEQUENCE có CACHE 1000, chép cách sinh mã đơn ở chương khóa chính. Giờ và số ở đoạn này là minh họa. Bài này đọc quy định về số hóa đơn, đo hai đường làm hở dãy số trên SQL Server 2019, và dựng cách cấp số không hở.

Đọc nhanh

  • Từ 01/7/2026, theo Nghị định 254/2026/NĐ-CP, số hóa đơn tối đa 8 chữ số, bắt đầu từ 1 mỗi năm, liên tục trong cùng ký hiệu mẫu số và ký hiệu hóa đơn.
  • IDENTITY và SEQUENCE mất số khi rollback và khi dừng đột ngột: đo trên LocalDB 2019, 48 số với SEQUENCE mặc định, 998 với IDENTITY kiểu int. Dừng sạch không mất số.
  • Bảng đếm cập nhật cùng giao dịch với INSERT hóa đơn thì không hở: qua 5 lần chạy, SEQUENCE hở 1.582 số cho 1.582 lần rollback, bảng đếm hở 0.
  • Cái giá là xếp hàng trên một dòng, nhưng lần đo chậm nhất vẫn gấp 7 lần nhu cầu của BanHang. Cấp số lúc phát hành, ký số sau COMMIT.

1. Quy định nói gì về số hóa đơn

Từ 01/7/2026, Nghị định 254/2026/NĐ-CP ngày 30/6/2026 thay Nghị định 123/2020/NĐ-CP và Nghị định 70/2025/NĐ-CP (Điều 43). Điều 10 đưa số hóa đơn vào nội dung bắt buộc, phần chi tiết ở mục 2 của Phụ lục. Điểm a:

Số hóa đơn được ghi bằng chữ số Ả-rập có tối đa 8 chữ số, bắt đầu từ số 1 vào ngày 01/01 hoặc ngày bắt đầu sử dụng hóa đơn và kết thúc vào ngày 31/12 hàng năm có tối đa đến số 99 999 999. Hóa đơn được lập theo thứ tự liên tục từ số nhỏ đến số lớn trong cùng một ký hiệu hóa đơn và ký hiệu mẫu số hóa đơn.

Câu sau của điểm a: khi nhiều cơ sở dùng chung một ký hiệu, số liên tục "theo thời điểm người bán ký số, ký điện tử trên hóa đơn". Điểm b: nếu không theo nguyên tắc trên, số phải "tăng theo thời gian" và mỗi số chỉ dùng một lần. Trước 01/7/2026, đoạn gần như giống hệt nằm ở Điều 10 khoản 3 Nghị định 123/2020/NĐ-CP; Nghị định 70/2025/NĐ-CP (Điều 1 khoản 7) sửa Điều 10 nhưng không đụng khoản 3.

Theo Phụ lục I Thông tư 91/2026/TT-BTC (hiệu lực 01/7/2026), mẫu số 1 là hóa đơn giá trị gia tăng. Ký hiệu gồm C (có mã của cơ quan thuế), hai số cuối của năm, chữ T và hai ký tự "do người bán tự xác định căn cứ theo nhu cầu quản lý". BanHang dùng 1C26TBH.

Với database: mỗi cặp mẫu số và ký hiệu một bộ đếm; năm nằm trong ký hiệu nên năm mới là dòng đếm mới, từ 0; trần 99 999 999 dư sức cho 11.700 × 365 = 4.270.500 hóa đơn mỗi năm. Một chỗ hở có vi phạm hay không là việc của kế toán thuế. Hệ thống chỉ cần không tự sinh ra chỗ hở.

2. Hai đường làm hở dãy số

Chương khóa chính đã so sánh IDENTITY với SEQUENCE. Ở đây chỉ tách hai đường hở để đo.

Đường thứ nhất: số được cấp ngoài giao dịch. Tài liệu CREATE SEQUENCE ghi số được sinh ngoài phạm vi giao dịch hiện tại và bị tiêu dù giao dịch commit hay rollback. Tài liệu IDENTITY nói tương tự và gọi đó là lựa chọn vì hiệu năng. Mỗi lần ký số lỗi làm rollback là một số mất.

Đường thứ hai: cache. Với CACHE n, engine giữ n số trong bộ nhớ và chỉ ghi mốc cuối khối xuống bảng hệ thống. Dừng sạch thì giá trị hiện tại được ghi xuống trước. Dừng đột ngột thì phần còn lại của khối mất. NO CACHE ghi xuống mỗi lần cấp. Từ SQL Server 2017, IDENTITY_CACHE = OFF tắt cache của IDENTITY cho cả database.

Rollback SEQUENCE hoặc IDENTITY 1 2 3 4 5 Số 3 cấp cho giao dịch rollback: mất hẳn. Bảng còn 1, 2, 4, 5. Dừng đột ngột SEQUENCE, CACHE mặc định 1 2 3 4 … 50 trong RAM dừng 51 Đĩa chỉ giữ mốc cuối khối. Khởi động lại đi tiếp từ 51: mất 4 … 50, tức 47 số. Bảng đếm SoCuoi trong giao dịch 1 2 3 4 4 5 Rollback trả SoCuoi về 3, lần sau lại nhận 4.
Nét đứt: số đã cấp cho một lần không thành hóa đơn. Chỉ bảng đếm cấp lại số đó. Khối 50 số là cache mặc định đo được ở mục 3.

3. Đo trên LocalDB: mỗi lần dừng mất bao nhiêu số

Thí nghiệm dừng SQL Server nhiều lần, nên dùng một instance LocalDB riêng, KumeoSoHoaDon, chứ không dùng MSSQLLocalDB.

-- 01-tao.sql
CREATE SEQUENCE dbo.seq_MacDinh AS int START WITH 1;              -- CACHE mặc định
CREATE SEQUENCE dbo.seq_Cache1000 AS int START WITH 1 CACHE 1000;
CREATE SEQUENCE dbo.seq_KhongCache AS int START WITH 1 NO CACHE;
CREATE TABLE dbo.Id_Int (So int IDENTITY(1, 1) PRIMARY KEY, Ghi char(1) NULL);
CREATE TABLE dbo.Id_Bigint (So bigint IDENTITY(1, 1) PRIMARY KEY, Ghi char(1) NULL);
-- 02-lay-so.sql: lấy một số từ mỗi bộ, in một dòng
SET NOCOUNT ON;
DECLARE @i int, @b bigint;
INSERT dbo.Id_Int (Ghi) VALUES (NULL);    SET @i = SCOPE_IDENTITY();
INSERT dbo.Id_Bigint (Ghi) VALUES (NULL); SET @b = SCOPE_IDENTITY();
SELECT NEXT VALUE FOR dbo.seq_MacDinh, NEXT VALUE FOR dbo.seq_Cache1000,
       NEXT VALUE FOR dbo.seq_KhongCache, @i, @b;

Script dưới lấy số, dừng instance theo ba cách rồi lấy tiếp. LocalDB tự khởi động khi có kết nối mới.

sqllocaldb create KumeoSoHoaDon 15.0
$S = "(localdb)\KumeoSoHoaDon"; $DB = "Kumeo_sohoadon"
sqlcmd -S $S -Q "CREATE DATABASE Kumeo_sohoadon; ALTER DATABASE Kumeo_sohoadon SET AUTO_CLOSE OFF;"
sqlcmd -S $S -d $DB -i 01-tao.sql
function LaySo { sqlcmd -S $S -d $DB -l 60 -i 02-lay-so.sql -W -s "," -h -1 }
LaySo; LaySo; LaySo
sqlcmd -S $S -Q "SHUTDOWN"; LaySo; LaySo                    # dừng sạch
sqlcmd -S $S -Q "SHUTDOWN WITH NOWAIT"; LaySo; LaySo        # dừng không checkpoint
sqllocaldb stop KumeoSoHoaDon -k; LaySo; LaySo              # giết tiến trình, gần như mất điện
sqlcmd -S $S -d $DB -Q "ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE = OFF;"
sqllocaldb stop KumeoSoHoaDon -k; LaySo; LaySo
sqllocaldb stop KumeoSoHoaDon -k; LaySo; LaySo

Số mất bằng số đầu tiên sau khi khởi động, trừ số cuối trước khi dừng, trừ 1. LocalDB 15.0.4382 (SQL Server 2019 CU27):

Bộ sinh số SHUTDOWN NOWAIT Giết Giết, sau IDENTITY_CACHE = OFF Giết lần nữa
SEQUENCE, CACHE mặc định 0 48 48 48 48
SEQUENCE, CACHE 1000 0 998 998 998 998
SEQUENCE, NO CACHE 0 0 0 0 0
IDENTITY, int 0 998 998 998 0
IDENTITY, bigint 0 9.998 9.998 9.998 0

Dừng đột ngột mất phần còn lại của khối: 50 số với SEQUENCE không ghi CACHE (tài liệu không cam kết kích thước này), 1.000 với IDENTITY kiểu int, 10.000 với bigint. Bật IDENTITY_CACHE = OFF xong, lần giết đầu vẫn mất khối đã giữ từ trước, lần sau thì không. Cài đặt này không đụng tới SEQUENCE.

Database mới trên LocalDB này có is_auto_close_on = 1: phiên cuối thoát là database đóng sạch, cache được ghi xuống, và lần thử đầu không thấy lỗ nào. Vì vậy script tắt AUTO_CLOSE.

4. Bảng đếm: cấp số trong cùng giao dịch

Bảng đếm giữ số cuối đã cấp của mỗi ký hiệu. Thủ tục tăng nó và ghi hóa đơn trong một giao dịch. Thủ tục SEQUENCE ở cuối chỉ để so sánh.

CREATE TABLE dbo.HoaDonDemSo (
    MauSo tinyint NOT NULL,              -- 1: hóa đơn giá trị gia tăng
    KyHieu char(6) NOT NULL,             -- C26TBH: có mã, năm 2026, loại T, hai ký tự BH
    SoCuoi int NOT NULL CONSTRAINT DF_HoaDonDemSo_SoCuoi DEFAULT 0,
    CONSTRAINT PK_HoaDonDemSo PRIMARY KEY CLUSTERED (MauSo, KyHieu),
    CONSTRAINT CK_HoaDonDemSo_SoCuoi CHECK (SoCuoi BETWEEN 0 AND 99999999)
);

CREATE TABLE dbo.HoaDon (
    MauSo tinyint NOT NULL,
    KyHieu char(6) NOT NULL,
    SoHoaDon int NOT NULL,
    DonHangId bigint NOT NULL,
    TongTien decimal(18, 2) NOT NULL,
    TrangThai tinyint NOT NULL,          -- 1 đã cấp số, 2 đã ký, 3 đã có mã của cơ quan thuế
    CapSoLuc datetime2(3) NOT NULL CONSTRAINT DF_HoaDon_CapSoLuc DEFAULT SYSUTCDATETIME(),
    CONSTRAINT PK_HoaDon PRIMARY KEY CLUSTERED (MauSo, KyHieu, SoHoaDon),
    CONSTRAINT UQ_HoaDon_DonHang UNIQUE (DonHangId)
);

INSERT dbo.HoaDonDemSo (MauSo, KyHieu) VALUES (1, 'C26TBH');
GO

CREATE OR ALTER PROCEDURE dbo.usp_HoaDon_CapSo
    @MauSo tinyint, @KyHieu char(6), @DonHangId bigint, @TongTien decimal(18, 2),
    @SoHoaDon int OUTPUT
AS
BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT ON;
    BEGIN TRANSACTION;

    UPDATE dbo.HoaDonDemSo                 -- khóa X trên dòng đếm, giữ đến COMMIT
    SET @SoHoaDon = SoCuoi = SoCuoi + 1
    WHERE MauSo = @MauSo AND KyHieu = @KyHieu;

    IF @@ROWCOUNT = 0
        THROW 50010, N'Ký hiệu chưa có dòng trong dbo.HoaDonDemSo.', 1;

    INSERT dbo.HoaDon (MauSo, KyHieu, SoHoaDon, DonHangId, TongTien, TrangThai)
    VALUES (@MauSo, @KyHieu, @SoHoaDon, @DonHangId, @TongTien, 1);

    COMMIT TRANSACTION;
END;
GO

-- Bản so sánh: số lấy từ SEQUENCE với CACHE mặc định
CREATE SEQUENCE dbo.seq_HoaDon_1C26TBH AS int START WITH 1;
GO
CREATE OR ALTER PROCEDURE dbo.usp_HoaDon_CapSo_Seq
    @MauSo tinyint, @KyHieu char(6), @DonHangId bigint, @TongTien decimal(18, 2),
    @SoHoaDon int OUTPUT
AS
BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT ON;
    SET @SoHoaDon = NEXT VALUE FOR dbo.seq_HoaDon_1C26TBH;
    INSERT dbo.HoaDon (MauSo, KyHieu, SoHoaDon, DonHangId, TongTien, TrangThai)
    VALUES (@MauSo, @KyHieu, @SoHoaDon, @DonHangId, @TongTien, 1);
END;
GO

SET @SoHoaDon = SoCuoi = SoCuoi + 1 tăng cột và gán cho biến giá trị mới, không phải giá trị cũ, trong một câu. UPDATE giữ khóa X trên dòng đếm tới hết giao dịch, như cách a ở chương transaction. Phiên khác cùng ký hiệu chờ ở câu này, rollback trả SoCuoi về giá trị cũ, và số n + 1 chỉ được cấp sau khi giao dịch cầm số n kết thúc. Với SEQUENCE, số 102 có thể commit trước số 101. UQ_HoaDon_DonHang chặn ứng dụng thử lại sau timeout: lần thứ hai vi phạm ràng buộc và rollback cả lần tăng số. Hóa đơn điều chỉnh và thay thế cần khóa khác, ngoài phạm vi bài.

5. Cái giá: mọi phiên cùng ký hiệu xếp hàng

Chương trình dưới chạy N phiên gọi một trong hai thủ tục trong 5 giây. Ứng dụng mở giao dịch, gọi thủ tục, rồi commit; 2% lần rollback sau khi đã có số. Biến thể thứ hai ngủ 30 ms trước COMMIT, như gọi dịch vụ ký số khi giao dịch còn mở. Cần .NET 10 (dotnet run do-thong-luong.cs) và Microsoft.Data.SqlClient 7.1.1.

#:package Microsoft.Data.SqlClient@7.1.1
using System.Data;
using System.Diagnostics;
using Microsoft.Data.SqlClient;

const string Cs = @"Server=(localdb)\KumeoSoHoaDon;Database=Kumeo_sohoadon;Integrated Security=true;Encrypt=false";
const string WriteLog = "SELECT CAST({0} AS int) FROM sys.dm_os_wait_stats WHERE wait_type = 'WRITELOG'";
long donHangId = 0;

Console.WriteLine("thu_tuc                   giu_ms phien hoa_don/s tre_tb_ms writelog_ms rollback lo_hong");
foreach (int giuMs in new[] { 0, 30 })
foreach (string thuTuc in new[] { "dbo.usp_HoaDon_CapSo_Seq", "dbo.usp_HoaDon_CapSo" })
foreach (int soPhien in new[] { 1, 4, 16, 64 })
{
    Sql("TRUNCATE TABLE dbo.HoaDon; UPDATE dbo.HoaDonDemSo SET SoCuoi = 0;"
      + " ALTER SEQUENCE dbo.seq_HoaDon_1C26TBH RESTART WITH 1;");
    int daGhi = 0, rollback = 0, wlLan = -Sql(string.Format(WriteLog, "waiting_tasks_count")),
        wlMs = -Sql(string.Format(WriteLog, "wait_time_ms"));
    long tongTre = 0;
    var sanSang = new Barrier(soPhien + 1);
    var dongHo = new Stopwatch();
    var phien = Enumerable.Range(0, soPhien).Select(p => new Thread(() =>
    {
        var rnd = new Random(42_000 + p);
        using var conn = new SqlConnection(Cs);
        conn.Open();
        sanSang.SignalAndWait();                            // mọi phiên đã mở kết nối mới bấm giờ
        while (dongHo.ElapsedMilliseconds < 5000)
        {
            long batDau = dongHo.ElapsedTicks;
            bool hong = rnd.NextDouble() < 0.02;            // 2% lần phát hành lỗi sau khi đã có số
            using var tx = conn.BeginTransaction();
            using var cmd = new SqlCommand(thuTuc, conn, tx) { CommandType = CommandType.StoredProcedure };
            cmd.Parameters.Add("@MauSo", SqlDbType.TinyInt).Value = 1;
            cmd.Parameters.Add("@KyHieu", SqlDbType.Char, 6).Value = "C26TBH";
            cmd.Parameters.Add("@DonHangId", SqlDbType.BigInt).Value = Interlocked.Increment(ref donHangId);
            cmd.Parameters.Add("@TongTien", SqlDbType.Decimal).Value = 1_500_000m;
            cmd.Parameters.Add("@SoHoaDon", SqlDbType.Int).Direction = ParameterDirection.Output;
            cmd.ExecuteNonQuery();
            if (giuMs > 0) Thread.Sleep(giuMs);             // gọi dịch vụ ký số khi giao dịch còn mở
            if (hong) { tx.Rollback(); Interlocked.Increment(ref rollback); }
            else { tx.Commit(); Interlocked.Increment(ref daGhi); }
            Interlocked.Add(ref tongTre, dongHo.ElapsedTicks - batDau);
        }
    })).ToList();
    phien.ForEach(t => t.Start());
    sanSang.SignalAndWait();
    dongHo.Start();
    phien.ForEach(t => t.Join());
    double giay = dongHo.Elapsed.TotalSeconds, treMs = tongTre * 1000.0 / Stopwatch.Frequency / (daGhi + rollback);
    wlLan += Sql(string.Format(WriteLog, "waiting_tasks_count"));
    wlMs += Sql(string.Format(WriteLog, "wait_time_ms"));
    int soDaCap = Sql(thuTuc.EndsWith("_Seq")
        ? "SELECT CAST(current_value AS int) FROM sys.sequences WHERE name = 'seq_HoaDon_1C26TBH'"
        : "SELECT SoCuoi FROM dbo.HoaDonDemSo WHERE MauSo = 1 AND KyHieu = 'C26TBH'");
    int loHong = soDaCap - Sql("SELECT COUNT(*) FROM dbo.HoaDon");
    Console.WriteLine($"{thuTuc,-25} {giuMs,6} {soPhien,5} {daGhi / giay,9:F0} {treMs,9:F1} {(double)wlMs / wlLan,11:F2} {rollback,8} {loHong,7}");
}

static int Sql(string cau)
{
    using var conn = new SqlConnection(Cs);
    conn.Open();
    return new SqlCommand(cau, conn).ExecuteScalar() is int n ? n : 0;
}

Phần đếm không phụ thuộc máy: qua 5 lần chạy, SEQUENCE có 1.582 lần rollback và đúng 1.582 số hở, bảng đếm có 535 lần rollback và 0 số hở. Phần thời gian là trung vị 5 lần trên laptop Intel Core Ultra 5 125U, Windows 11, .NET 10.0.401, khi máy đang chạy nhiều việc khác (writelog_ms từ 0,4 đến 36,7 ms). Chỉ đọc bậc độ lớn.

Giữ giao dịch 30 ms, bảng đếm đứng ở 16 hóa đơn mỗi giây dù thêm phiên

SEQUENCEBảng đếmSEQUENCE, giữ 30 msBảng đếm, giữ 30 ms

Hóa đơn mỗi giây

101001.00010.000141664SEQUENCEBảng đếmSEQUENCE, giữ 30 msBảng đếm, giữ 30 ms

Số phiên đồng thời

Trung vị 5 lần chạy chương trình ở trên, mỗi cấu hình 5 giây. Laptop Core Ultra 5 125U đang bận, LocalDB 2019 CU27. Thang log.
Bảng số liệu
Số phiên đồng thờiSEQUENCEBảng đếmSEQUENCE, giữ 30 msBảng đếm, giữ 30 ms
189 hóa đơn/s53 hóa đơn/s17 hóa đơn/s16 hóa đơn/s
4348 hóa đơn/s89 hóa đơn/s74 hóa đơn/s17 hóa đơn/s
16586 hóa đơn/s103 hóa đơn/s201 hóa đơn/s16 hóa đơn/s
64530 hóa đơn/s284 hóa đơn/s1.102 hóa đơn/s16 hóa đơn/s

Bảng đếm giữ dòng đếm từ UPDATE tới khi COMMIT ghi log xong, nên thông lượng xấp xỉ 1 chia thời gian giữ một lượt. Giữ thêm 30 ms, một lượt mất khoảng 61 ms và hệ thống đứng ở 16 hóa đơn mỗi giây. Thêm phiên chỉ làm độ trễ tăng, tới 2.980 ms với 64 phiên. SEQUENCE không có dòng chung để chờ nên vẫn lên 1.102. Không giữ gì thêm, bảng đếm dao động từ 23 đến 1.071 hóa đơn mỗi giây, theo độ trễ ghi log lúc đo.

6. Đặt con số cạnh 11.700 hóa đơn mỗi ngày

11.700 hóa đơn mỗi ngày dồn hết vào một giờ cũng chỉ là 11.700 / 3.600 = 3,25 hóa đơn mỗi giây. Lần đo chậm nhất của bảng đếm, 23 hóa đơn mỗi giây, gấp 7 lần mức đó. Thứ đáng lo là việc chậm nằm trong giao dịch: ký số từ xa mất 300 ms thì trần còn khoảng 1 / 0,3 = 3,3 hóa đơn mỗi giây (ước lượng), hết chỗ cho một đợt dồn. Bốn quyết định:

  1. Cấp số lúc phát hành, không lúc tạo đơn. 13.000 đơn mỗi ngày chỉ ra 11.700 hóa đơn: cấp lúc tạo đơn thì khoảng 1.300 số mỗi ngày rơi vào đơn hủy hoặc dở dang.
  2. Giao dịch cấp số chỉ gồm UPDATE và INSERT. Ký số, gửi cơ quan thuế, gửi khách chạy sau COMMIT, theo quy tắc đầu tiên của chương transaction.
  3. Ký theo thứ tự số. Mỗi ký hiệu một luồng ký, lấy hóa đơn TrangThai = 1 theo SoHoaDon tăng dần, để thứ tự ký khớp thứ tự số. Ký lỗi thì thử lại đúng số đó.
  4. Chỉ tách ký hiệu khi cần quản lý riêng. Phụ lục Nghị định 254/2026/NĐ-CP đã tính trường hợp nhiều cơ sở dùng chung một ký hiệu. Dùng hai ký tự cuối để tách theo kho tổng, như C26THN, là cách bài đọc cụm "nhu cầu quản lý"; văn bản không nói riêng về chi nhánh, nên cần kế toán thuế xác nhận.
flowchart TD
  A["Đơn hoàn tất, TrangThai = 4"] --> B["usp_HoaDon_CapSo: UPDATE dòng đếm, INSERT hóa đơn, COMMIT"]
  H["Đơn hủy"] --> X["Không cấp số"]
  B --> C["Hàng đợi ký của ký hiệu, SoHoaDon tăng dần"]
  C --> D{"Ký số thành công?"}
  D -- "Không" --> E["Thử lại với đúng số đó"]
  E --> C
  D -- "Có" --> F["TrangThai = 2, gửi cơ quan thuế cấp mã"]
  F --> G["TrangThai = 3, gửi hóa đơn cho khách"]

Những chỗ hay hiểu sai

  • "NO CACHE hoặc IDENTITY_CACHE = OFF cho số liền nhau." Chỉ bịt đường cache. Rollback vẫn tiêu số.
  • "Khởi động lại SQL Server làm nhảy số." SHUTDOWN sạch không mất số nào. Mất số khi dừng đột ngột: mất điện, giết tiến trình, SHUTDOWN WITH NOWAIT.
  • "Năm của ký hiệu lấy từ SYSUTCDATETIME()." Từ 00:00 đến 07:00 ngày 01/01 giờ Việt Nam, giờ UTC vẫn là năm cũ. Lấy năm theo giờ Việt Nam, như SWITCHOFFSET(SYSDATETIMEOFFSET(), '+07:00').

Đọc tiếp

Nguồn

Đọc tiếp