当强制实施IsDeleted(软删除)时,合适的索引体系结构是什么?


17

当前,我们有一个功能齐全的现有数据库和应用程序。我目前无法更改架构。今天,数据库中的每个表都有一个“ IsDeleted” NOT NULL BIT字段,默认值为“ 0”。当应用程序“删除”数据时,它只是将IsDeleted标志更新为1。

我无法理解的是每个表的索引应如何构造。现在,每个查询/联接/等总是执行IsDeleted检查。这是我们开发人员必须遵循的标准。话虽这么说,我试图确定是否需要更改每个表上的所有群集主键索引,以包括主键和IsDeleted BIT字段。另外,由于每个查询/加入/等。必须执行IsDeleted检查,是否适当地假设每个单索引(以及非聚簇索引)都应将IsDeleted字段包括为索引的第一个字段?

我还有一个问题是关于过滤索引的。我知道我可以在诸如“ WHERE IsDeleted = 0”之类的索引上放置过滤器,以减少索引的大小。但是,由于每个联接/查询都必须实现IsDeleted检查,这是否会阻止使用过滤后的索引(因为联接/查询中使用了IsDeleted列)?

请记住,我没有能力更改IsDeleted方法。

Answers:


13

这里最简单的方法是不使用键和聚集索引,而对非聚集索引使用过滤索引。

此外,您可以将一些大表迁移到分区堆或分区集群列存储(SQL Server 2016+),而主键和唯一索引保持未分区状态。这将使您能够将IsDeleted行的非关键列推入单独的数据结构,该数据结构可以另外进行不同的压缩或存储在不同的文件组中。

并确保开发人员使用文字而不是参数来过滤IsDeleted行。对于一个参数,SQL Server在两种情况下都必须使用相同的查询计划。

例如

SELECT ... WHERE ... AND IsDeleted=0

并不是:

SELECT ... WHERE ... AND IsDeleted=@IsDeleted

使用参数将阻止使用过滤后的索引,并可能使您陷入参数嗅探的麻烦。


考虑到该IsDeleted列的普遍性和重要性,无论物理存储如何,都可以通过两个视图(可选地在不同的模式下)公开数据,从而解决参数化问题并在访问本不应该访问的数据时犯错误。访问的可能性较小。访问基本数据仅与极少数情况有关,在极少数情况下,需要以某种方式组合已删除和未删除的数据,以及实际上需要将行切换为“已删除”的情况。
Jeroen Mostert

@JeroenMostert很好的建议。RLS也可以在这里使用,或诸如EF Core全局查询过滤器之类的东西。docs.microsoft.com/en-us/ef/core/querying/filters
David Browne-Microsoft

9

这可能是一个不受欢迎的观点,但我认为没有“随处可做” /一种尺寸适合您的问题的所有答案。

如果您的查询无缘无故地扫描大量IsDeleted行,则一种解决方案是创建一个过滤的非聚集索引来满足该查询。

另一个选择是创建一个索引视图,该视图可以被许多不同的查询所利用,这些查询仅被过滤为未删除的行。这在Enterprise Edition上特别有用,在Enterprise Edition中,自动索引视图匹配有效而无需提供NOEXPAND提示。

对于小型表或读取量大的表,添加过滤的非聚簇索引或视图或任何其他内容实际上可能只会给数据库增加不必要的开销。


2

在删除很少见的合理假设下,对索引进行任何更改都不是合适的解决方案。

我发现迟早必须查询对已删除行的引用,索引中的行突然变得非常值得。

请注意,除非您使用视图,否则您必须编辑所有查询以包含过滤器。


0

我已经看到IS_DELETED标志为0或PK值的系统。在其他系统中,它是PK的负面因素。

由于大多数查询都是通过“自然”或业务(有时是多字段)键来检索值的,因此除非通过联接,否则它们就永远不会被PK查询。但他们确实总是在主表和所有联接表的末尾添加AND IS_DELETED = 0。

对于每个跟踪更改的事务表,该系统还具有一个审核表;该应用程序具有显示所有数据更改(包括已删除数据)的功能。


0

希望您有权更改查询。

但是,由于每个联接/查询都必须实现IsDeleted检查,这是否会阻止使用过滤后的索引(因为联接/查询中使用了IsDeleted列)?

我想说一个重要的观点,希望我能解释一下。

在复杂的查询中,where Transaction tableMastertable都使用。

IsDeleted=0仅在Transaction表中使用。不要在Master桌子上使用。

例,

Select * from dbo.Order O
inner join dbo.category C on o.categoryid=o.categoryid
inner join dbo.Product P on P.Productid=o.Productid
where o.isdeleted=0

c.isdeleted=0(在Category表中使用)没有任何意义。

同样,使用中有什么意义P.isdeleted=0吗?

因为我想要所有未删除的Order及其详细信息。

怎样才能Product当被删除OrderActive或地方Productid的参考。

因此,如果您在重要查询中仔细调试,则可能会删除一些isdeleted = 0。

不要盲目创建筛选索引,首先选择所有那些非常重要且缓慢的查询。

优化那些缓慢的查询,然后仅决定过滤索引或调整索引。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.