【问题标题】:MAX/ORDER BY on char columnchar 列上的 MAX/ORDER BY
【发布时间】:2010-12-01 14:34:26
【问题描述】:

在我的 SQL Server 2005 数据库中,我在表 RMA 中有一个数据类型为 char(10) 的列 RMA_Number

该值是一个递增的数字,格式为RMA0002511。在插入时获得最大数字以增加它的最快方法是什么?

我的第一个方法是:

SELECT     TOP (1) RMA_Number
FROM         RMA
WHERE     (RMA_generated = 1)
ORDER BY Creation_Date DESC

但这很容易出错,因为更高的 RMA_Number 可能有更早的创建日期。作为一种解决方法,按主键排序有效:

SELECT     TOP (1) RMA_Number
FROM         RMA
WHERE     (RMA_generated = 1)
ORDER BY idRMA DESC

但也许这也是一个可能的错误来源。

从逻辑上讲,最好的方法是ORDER BY RMA_Number DESC

但是因为我不确定这是否总是给出正确的结果,并且认为如果记录数量增加,对 char 列进行排序可能会变慢,所以我选择按 Date 列排序。

所以,

  1. 按 char(10) 列排序是个好主意(性能和准确性)吗?
  2. 最好SELECT MAX( RMA_Number ) FROM RMA 获得最高数字(性能和准确性)
  3. 如果前两点有误,我应该坚持使用主键排序,还是应该使用int 列并在应用程序中格式化数字?

编辑

我想我必须澄清一些我没有提到的事情。 RMA_Number 不是在每次插入时生成的。所以也许有很多没有编号的记录。 Martin 使用主键来构建数字。那会是个问题,因为差距太大了。

提前谢谢你。

【问题讨论】:

  • 值中偶尔出现间隙是否重要?例如,有 RMA0002511 和 RMA0002513,但没有 RMA0002512 是否可以?
  • 那没问题。实际上,如果我删除较旧的记录,就会发生这种情况。
  • 由于差距没问题,我会推荐一个身份列,就像@Martin 的回答一样。
  • 我想我必须澄清一些我没有提到的事情。 RMA_Number 不是在每次插入时生成的。所以可能有很多没有编号的记录。 Martin 使用主键来构建数字。这将是一个问题,因为差距太大。
  • @Tim Schmelter - 有 RMA_Number 的记录和没有 RMA_Number 的记录有什么区别?当时是否插入了任何其他值?将所有这些东西放在一个单独的表中,并带有 RMA 表的外键不是更好吗?对您的数据进行适当的规范化可能会解决这个问题。

标签: sql-server performance sql-server-2005 database-design sql-order-by


【解决方案1】:

最快和最安全的(并发)方法是根本不存储 RMA000... 前缀。

只需创建一个整数标识列并通过计算列添加前缀。

create table #RMA
(
id int identity(2511,1) primary key,
RMA_Number as 'RMA' + RIGHT('000000' + CAST(id as varchar(7)),7)
)

insert into #RMA
default values

select * from #RMA

或者根据并非所有记录都有RMA_Number 的新信息,您可以使用这种方法来实现非阻塞、高效和并发安全的解决方案。

CREATE TABLE dbo.Sequence(
 val int IDENTITY (2511, 2) /*Seed this at 1 + whatever your current max value is*/
 )

GO

/*Call this procedure to get allocated the next sequence number to use*/     
CREATE PROC dbo.GetSequence
@val AS int OUTPUT
AS
BEGIN TRAN
    SAVE TRAN S1
    INSERT INTO dbo.Sequence DEFAULT VALUES
    SET @val=SCOPE_IDENTITY()
    ROLLBACK TRAN S1 /*Rolls back just as far as the save point to prevent the 
                       sequence table filling up. The id allocated won't be reused*/
COMMIT TRAN

【讨论】:

  • 为什么会更快? INTs 比字符串快吗?较小的数据字段是否比较大的数据字段更快?即使它被正确编入索引?
  • @Brad。是的,int 比较比字符串快。如果它被索引,那么这个成本可能会在插入时支付,而不是在查找时支付(至少对于找到max 值的特定情况)。使用身份也比自制的并发安全解决方案更快,因为它是一种非阻塞解决方案。
  • @Martin,感谢您的澄清。我喜欢IDENTITY 解决方案。当我们开发我们的 ID 时,我们必须(为什么?我不知道)使用字母数字 ID。
  • @Martin:我想我必须澄清一些我没有提到的事情。 RMA_Number 不是在每次插入时生成的。所以可能有很多没有编号的记录。您使用主键来构建数字。这将是一个问题,因为差距太大。
  • @Martin,预先计算 RMA 编号的表格,然后使用您的proc 提取MAX 序列号然后删除它呢?跨度>
【解决方案2】:

首先,您看到的是严重的比赛状况。

当我们在一个项目中需要这个时,我们有一个单独的表,其中存储了当前值和一个生成下一个值的函数。我们实施了锁定以保持多次调用以获取下一个号码。我记得,这是因为我们必须使用字母数字身份号码(该函数负责复杂的递增)。

不过,我最喜欢@Martin 的解决方案:使用和IDENTITY 字段。您可以按照他的建议删除前缀,或者您可以简单地将其从列中删除,然后在 SELECTing 从表中重新添加。

【讨论】:

  • 我认为这有点矫枉过正,但我​​意识到了风险。我选择在此列上创建一个唯一约束并在应用程序中捕获该异常。如果它被捕获,我将开始一个新的尝试以在循环中生成一个唯一编号(最多 10 次尝试)。还是谢谢你。
  • @Tim,是的,这取决于您系统的并发性。我们正在开发一个具有 100 多个能够同时交易的终端的 POS 系统。
  • 只有少数人(最多〜10人)会同时使用该应用程序,所以我认为总是(10次)得到错误的最高数字的风险很低。
  • 捕获/重试是错误的做法,它会回来并非常讨厌地咬你(或继承代码的可怜的草皮)。马丁的方法是对的。努力去做,以后会有人感激不尽的。
  • @Tim:用户第一次看到这个时,他们会认为发生了可怕的事情并开始浪费公司时间,他们可能会以此为借口与老板交谈松懈。最终你的老板会问 WTF 并告诉你修复它。无论风险如何,一开始就编写错误的代码都是一个坏主意。别介意它会让你看起来很糟糕。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-09-30
  • 2019-03-21
  • 1970-01-01
  • 2015-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多