如何限制我的 sql 数据库中的行数,然后在达到限制时覆盖一行?

how to limit number of line in my sql database and then overwrite a line when the limit is reached?

我在 C# 中使用 SQL 数据库来存储事件。我的 table 名字是 TabOfEvents;它具有事件代码以及该事件的日期和时间。

CREATE TABLE [dbo].[TabOfEvents] (
    [Id] INT IDENTITY (1, 1) NOT NULL,
    [cta] NCHAR (10) NOT NULL,
    [code] NVARCHAR (MAX) NOT NULL,
    [date] DATETIME2 (7) NULL,
    PRIMARY KEY CLUSTERED ([Id] ASC)
)

每次发生事件时,我都会将其保存在我的 table 中,按日期排序(从最新到最旧)。

我想限制我的数据库中的行数,例如 100.000 行(或者根据我的数据库的大小,例如 300Mo,但我不确定是否可能)以及何时限制已达到编号,我想覆盖最旧的事件并用新事件替换它们。

我如何使用 C# 执行此操作?

您可以使用触发器来完成此操作。但是,可能没有必要。

删除 table 中的行不会自动回收 space。数据库 table 的工作方式与文件不同。删除行也可能是一个(相对)耗时的操作,因为记录、锁定和索引重组。

从性能的角度来看,设计良好的数据库应该可以轻松处理数百万行数据——对于许多常见类型的查询。

如果您确实想限制 table 的大小,我建议您使用分区方案。然后使用预定作业删除旧分区。删除分区比删除单个行更有效。而且,从丢失的分区中恢复 space 是微不足道的。

不要这样做。您可以这样做:

  • 为您的数据和事件制定保留政策 and/or 归档政策。因此,例如,您可以存档或删除早于 x 个月或几天的事件。您可以将它们存档在不同的 tables/ 或不同的数据库中。或者,

  • 您可以在应用程序的业务层上设置允许事件数量的规则。这样您就可以按照自己喜欢的方式控制和限制事件和数据。


因此,对于第二个选项,最大事件数可以存储在数据库中的配置 table 或配置文件(在 app.config 或 web.config 中)之类的东西中.如果您在线搜索,您可以找到 how to read and write in config file.

然后在您的应用程序中进行插入之前,从数据库中获取事件计数,例如:

SELECT COUNT(*) FROM events;

然后将此结果与 web.config 中的值进行比较。如果值 >= 最大值拒绝插入并显示错误消息,否则执行插入。

您可以使用 sequence with the CYCLE option used as pointer in the table used as ring buffer。这会在数据库中创建一个序列对象:

CREATE SEQUENCE BufferPtr  
    START WITH 1
    MINVALUE 1
    MAXVALUE 100000
    CYCLE;

以不同方式创建 table 以允许空条目并允许输入 ID

CREATE TABLE dbo.TabOfEvents (
    [Id] INT NOT NULL,
    [cta] NCHAR (10) NULL,
    [code] NVARCHAR (MAX) NULL,
    [date] DATETIME2 (7) NULL,
    PRIMARY KEY CLUSTERED ([Id] ASC)
)

然后用 100000 条空记录和 ID 1 .. 100000 填充 table。我们这样做是为了提前知道记录的数量并为我们自己保存一个查询。也就是说,我们不必查询 SELECT COUNT (*) FROM TabOfEvents 的记录数来知道我们是否必须进行插入或更新。 Sequence 在 UPDATE 命令本身中使用,以确定我们更新的记录。每次都会生成一个新的序列号,在达到 100,000 的限制后,序列号从 1 重新开始。

-- This creates 100000 empty records with an Id. The code is quite tricky and I won't explain
-- the details here. If you prefer, create a stored procedure with a FOR-loop or do it in an
-- external application (e.g. C# Console app). Performance is not important, since we are
-- doing it only once.

;WITH e1(n) AS
(
    SELECT 1 FROM (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(0)) t(n)
)
INSERT INTO dbo.TabOfEvents (Id)
SELECT ROW_NUMBER() OVER (ORDER BY n10.n) AS n
FROM
e1 n10
CROSS JOIN e1 n100
CROSS JOIN e1 n1000
CROSS JOIN e1 n10000
CROSS JOIN e1 n100000

现在 table 和序列已设置,您可以使用

更新记录
UPDATE TabOfEvents
SET cta = 'test', code = 'xxx', [date] = SYSDATETIME()
FROM
    TabOfEvents
    INNER JOIN
    (SELECT
        NEXT VALUE FOR BufferPtr AS Ptr
        FROM (VALUES (1)) t(n)
    ) a
    ON TabOfEvents.Id = a.Ptr;

您可以在此处 (http://sqlfiddle.com/#!6/2679e/5/2) 查看仅使用 4 条记录的测试。重复点击运行SQL,你会看到事件是如何循环添加的。

注意:我的第一次尝试是使用UPDATE TabOfEvents SET cta = 'test', code = 'xxx', [date] = SYSDATETIME() WHERE Id = NEXT VALUE FOR BufferPtr,但SQL-服务器拒绝查询WHERE 子句中的序列。我的 UPDATE 语句可以简化吗?