【问题标题】:SQL Server - Prefix and zero padding ID columnSQL Server - 前缀和零填充 ID 列
【发布时间】:2017-07-02 06:27:03
【问题描述】:

为了快点,这是我的问题,我一直在尝试在 StackOverflow 上搜索答案,但找不到满意的答案。

我有一个包含列的 InvoiceHeader 表(简化):

InvoiceID   : Int - PK Identity(1,1)
InvoiceDate : date
CustomerID  : Int - FK to Customer table

等等……

我会经常向用户显示有关发票的信息。 当我向用户显示发票编号时,我需要为 InvoiceID添加前缀和零填充

例如:

InvoiceID : 1 
Invoice Number : INV0000001

我的问题是,我是否应该在 InvoiceHeader 表中创建一个 BRAND NEW PERSISTED COMPUTED 列来保存格式化的发票编号,如下所示:

InvoiceNumber AS 'INV' + RIGHT('000000'+cast(InvoiceID as varchar(7)),7)

或者我应该选择InvoiceID 并在运行时从我的应用程序中将其处理为INV0000001

我的困境:

  • 如果我添加一个新的 PERSISTED COMPUTED 列(即 InvoiceNumber),那么我可以在 InvoiceNumber 处创建一个非聚集索引,这将有助于在 WHERE 子句中使用 InvoiceNumber 进行查询。但这仍然需要覆盖索引或包含索引来获取其他列的值。另外一个优点是我不必每次需要显示它时都将其“格式化”为 INV0000001。
  • 如果我在我的应用程序的 SELECT QUERY 中将 InvoiceID 格式化为 INV0000001,这将是太多的“苦差事”,每次我想显示 InvoiceNumber 时,我都必须对其进行格式化。但我不需要另一个与 InvoiceID 具有基本相同值且带有一些前缀和零填充的列。
  • 发票号码将被用户用于搜索,例如:给我找一张编号为'1234'+'%'的发票。如果对 InvoiceID(整数列)执行此操作,它将进行隐式转换,是否会使查询变慢?查询计划是否仍会使用 PK 索引?

编辑: 考虑到很多地方我每次需要时都必须手动格式化发票编号(如果我不使用计算列),我现在几乎确信要使用 Esperento57 的解决方案: p>

新建 2 列:

InvoiceID      : int identity (PK)
Prefix         : char(3) --> INV, etc
InvoiceNumber  : Prefix + RIGHT('000000'+cast(InvoiceID as varchar(7)),7)

但我不会让 PK 包含 Prefix,因为 InvoiceID 是 identity,所以它本身就是唯一的。

我还在考虑@Matt 关于关注点分离的观点,因为它很有意义。

【问题讨论】:

  • 另一种选择:创建一个全新的列并使用插入触发器填充该列。
  • 我认为如果我最终添加了一个新列,那么在这种情况下,持久化计算列会更好,因为计算列将保证值,而且它也被设置并忘记了。虽然触发器是另一个 sql 语句,所以它会比计算慢。 Cmiiw*.

标签: sql sql-server database-design sql-server-2014


【解决方案1】:

我对您的专栏的建议:

  • InvoiceID : integer not null with autoincrement (sequence is not necessary, SQL Server do the job)

  • 前缀:varchar(10) not null FK on PREFIXTABLE

  • InvoiceID 和 Prefix 是主键

  • InvoiceNumber:计算和持久化列 = Prefix + RIGHT('0000000'+ InvoiceID,7)

  • 在 InvoiceNumber 上添加索引

根据我的建议,您可以:

  1. 必要时更改前缀
  2. 您在前缀上具有完整性
  3. 您的完整密钥具有完整性并保留真正的主密钥
  4. 您不必重新计算完整的密钥
  5. 在数据恢复的情况下,您可以将序列跳转到您的键或简单地选择另一个前缀
  6. 您可以在 PREFIX 表中添加描述以解释您的前缀(以示例为未来开发者)

【讨论】:

  • 使前缀引用成为前缀表的好主意。但是为什么我们必须在主键中包含前缀?由于 InvoiceID 是标识,所以它应该是唯一的,因此不会有 2 个具有相同 InvoiceID 的前缀。
  • 它是一个选择,前缀不是你的密钥所必需的,它是真的。但是你把 INV0000001 给你的客户。如果有人将您的前缀修改到您的表中(错误地),您将丢失您的密钥。但是如果您的多个密钥用作FK,他无法修改您的密钥。其他问题:如果您想要的数据恢复可能具有相同的 id 但具有不同的前缀
  • 如果我的回答让你满意,你可以帮我;)
【解决方案2】:

您应该将其作为数值保存在数据库中并对其进行格式化,这将阻止您不必要地存储没有价值的数据并提高查询的性能。如果这样可以减少需要格式化的位置数量,则可以在应用程序的低级别应用格式,但根据应用程序的复杂性,您不太可能需要多次编写此格式化逻辑。

编辑

其他答案中有一些有效点,例如关于丢失数字的要点对您来说可能重要也可能不重要 - 只有您知道这是否相关。

未来更改前缀的问题我会提防,除非您知道这是现在的具体要求。有多种方法可以修改前缀的更改,无论是将其存储在数据库中、在视图中还是在 UI/业务层中生成。这是我建议KISSYAGNI 的事情之一!

注意。 Martin Fowler 在上面的文章中提出了一个很好的观点,即构建推定特性和构建可扩展软件之间的区别。这是在考虑围绕这些类型问题进行设计时始终使用的重要措施 - 尽你所能确保你有扩展点,但只实现你知道你需要的功能。

【讨论】:

  • 我倾向于这个选项,但我需要在很多地方将 InvoiceID 格式化为 INV0000001。像报表,销售退货模块,AR模块等。如果我在数据库中用前缀和InvoiceID参数制作一个标量值函数并在每次需要显示发票编号时调用它也许可以?或者这会在选择数千个发票行时使查询变慢?
  • 所以它变成了一个架构决策,如果在数据库视图中进行格式化是有意义的,那可能没问题,或者你可以在一个通用的业务层中进行,等等。这就是我意味着根据需要在低级别应用格式,但还要确保您是separating your concerns
【解决方案3】:

我认为您应该真正将内部ID 与用户看到的字符串分开,这是一个主键,用于标识记录。

最终用户不应该看到IDENTITY 生成的 ID,因为很容易出现空白。

所以,我将有两个普通列:int InvoiceIDchar(10) InvoiceNumber。是的,将有一个单独的方法来生成下一个发票编号。在 SQL Server 2014 中,我将使用 SEQUENCE 对象和 NO CACHE 选项。

当您将内部 ID 与用户可见的发票编号分开时,您可以在以后轻松更改这些编号的格式。例如,两年后财务部门可能决定要引入另一个前缀ABC000001。显然,所有现有的历史发票都应保留其编号。使用单独的发票编号列没有问题。使用计算列会有问题。

【讨论】:

  • 如果插入失败,afaik 序列也会留下间隙。更改前缀可以通过以下 Esperento57 的命题来解决。
  • 我的主要观点 - 不要向最终用户显示您的内部 ID。您可能需要在某个时候更改ID,但无法更改历史发票编号。今天你的发票号码是数字,明天你的公司被另一家公司收购,发票的整体结构可能完全不同。
【解决方案4】:

如果这是发票编号的计算公式,

InvoiceNumber  : Prefix + RIGHT('000000'+cast(InvoiceID as varchar(7)),7)

invoicenumber 之间的差距无关紧要。

然后我将只保留 InvoiceID。在显示时,我将在低级别或 proc 本身中以上述方式格式化 invoicenumber。 当用户搜索发票号码时,我删除前缀并将其转换为 int 变量。

然后我将搜索 invoiceID 列。

但是如果Prefix可能会改变并且invoicenumber之间不应该有GAP,那么,

i) 将前缀保留在同一个表中是个坏主意。

ii) 由于您必须保持连续性,因此发票编号的计算也会发生变化。

但我不会让 PK 包含前缀,因为 InvoiceID 是身份所以 它本身就是独一无二的。

这取决于。在我看来,当然前缀也是不可能的。但是 InvoiceID 可以是 PK+CI 或仅 PK 或仅身份或仅 CI。

您需要检查每个选项。

【讨论】:

  • 是的,昨天重新考虑之后,我打算将 InvoiceNumber 设为持久计算列,但没有任何索引。我将在查询中使用 InvoiceID,因为它是聚集索引并且与 InvoiceNumber 具有相同的值。我最终制作了 InvoiceNumber,因为有太多地方需要将 InvoiceID 格式化为 InvoiceNumber。 InvoiceNumber 序列在我的情况下并不重要。我什至对销售退货使用同一张表(带有负数列,前缀将用于识别销售发票和销售退货)。我想对它进行编码,这样它会更难以追踪,但我觉得我不需要。
  • *顺便问一下,什么是“低级”?这是否意味着在我的应用程序中创建一个返回格式化字符串的函数?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-26
  • 1970-01-01
相关资源
最近更新 更多