3

我们的应用程序有一个CustomerNumber字段。我们有数百个不同的人在使用该系统(每个人都有自己的登录名和自己的列表CustomerNumber)。单个用户最多可能有 100,000 个客户。许多人少于100个。

有些人只在他们的客户编号字段中输入实际数字,而另一些人则混合使用。系统允许 20 个字符,可以是 AZ、0-9 或破折号,并将这些字符存储在 VARCHAR2(20) 中。任何小写字母在存储之前都会变成大写字母。

现在,假设我们有一个简单的报告,其中列出了特定用户的所有客户,按客户编号排序。例如

SELECT CustomerNumber,CustomerName
FROM Customer
WHERE User = ?
ORDER BY CustomerNumber;

这是一个幼稚的解决方案,因为只使用数字的人不想看到简单的字母排序(“10”在“9”之前)。

我不希望就他们的数据向用户提出任何不必要的问题。

我正在使用 Oracle,但我认为看到其他数据库的一些解决方案会很有趣。请包括您的答案适用于哪个数据库。

您认为实现这一点的最佳方法是什么?

4

5 回答 5

4

可能您最好的选择是预先计算一个单独的列并将其用于订购并使用客户编号进行显示。这可能涉及将任何内部整数 0 填充为固定长度。

另一种可能性是对返回的结果进行排序后选择。

Jeff Atwood 整理了一篇关于一些人如何计算人类友好排序顺序的博客文章。

于 2009-02-16T02:55:51.787 回答
2

在 Oracle 10g 中:

SELECT  cust_name
FROM    t_customer c 
ORDER BY
    REGEXP_REPLACE(cust_name, '[0-9]', ''), TO_NUMBER(REGEXP_SUBSTR(cust_name, '[0-9]+'))

这将按数字的第一次出现排序,而不是关于它的位置,即:

  1. customer1 < customer2 < customer10
    • cust1omer ? customer1
    • cust8omer1 ? cust8omer2

,其中 a?表示订单未定义。

这足以满足大多数情况。

要对 case 强制排序2,您可以添加一个REGEXP_INSTR(cust_name, '[0-9]', n)ORDER BY列表n时间,强制在第n-th (2nd3rd)组数字的第一次出现时排序。

要在 case 上强制排序3,您可以添加一个TO_NUMBER(REGEXP_SUBSTR(cust_name, '[0-9]+', n))ORDER BY列表n时间,强制n-th 的顺序。一组数字。

在实践中,我写的查询就足够了。

您可以在这些表达式上创建一个基于函数的索引,但您需要通过提示强制执行它,并且SORT ORDER BY无论如何都会执行一次通过,因为CBO不信任函数基索引足以允许ORDER BY对它们进行索引。

于 2009-02-16T12:01:06.610 回答
1

您可以有一个数字列 [CustomerNumberInt] 仅在 CustomerNumber 是纯数字时使用(否则为 NULL [1]),然后

ORDER BY CustomerNumberInt, CustomerNumber

[1] 根据您的 SQL 版本如何处理 ORDER BY 中的 NULL,您可能希望将其默认为零(或无穷大!)

于 2009-02-16T12:15:24.703 回答
1

我有类似的可怕情况,并开发了一个合适的可怕功能来处理它(SQLServer)

在我的情况下,我有一个“单元”表(这是一个学生的工作跟踪系统,所以在这种情况下,单元代表他们正在做的一门课程)。单位有一个代码,其中大部分是纯数字的,但由于各种原因,它被做成了一个 varchar,他们决定在一些前面加上最多 5 个字符。所以他们期望 53,123,237,356 可以正常排序,但也有 T53、T123、T237、T356

UnitCode 是一个 nvarchar(30)

这是函数的主体:

declare @sortkey nvarchar(30)

select @sortkey = 
    case
        when @unitcode like '[^0-9][0-9]%' then left(@unitcode,1) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-1)
        when @unitcode like '[^0-9][^0-9][0-9]%' then left(@unitcode,2) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-2)
        when @unitcode like '[^0-9][^0-9][^0-9][0-9]%' then left(@unitcode,3) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-3)
        when @unitcode like '[^0-9][^0-9][^0-9][^0-9][0-9]%' then left(@unitcode,4) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-4)
        when @unitcode like '[^0-9][^0-9][^0-9][^0-9][^0-9][0-9]%' then left(@unitcode,5) + left('000000000000000000000000000000',30-(len(@unitcode))) + right(@unitcode,len(@unitcode)-5)
        when @unitcode like '%[^0-9]%' then @unitcode
        else left('000000000000000000000000000000',30-len(@unitcode)) + @unitcode
    end 

return @sortkey

写完之后我想朝自己的脸开枪,但是它可以工作并且在运行时似乎不会杀死服务器。

于 2009-02-16T23:51:04.640 回答
0

我在 SQL SERVER 中使用了它并且工作得很好:这里的解决方案是在前面用一个字符填充数值,这样所有的字符串长度都相同。

这是使用该方法的示例:

select MyCol
from MyTable
order by 
    case IsNumeric(MyCol) 
        when 1 then Replicate('0', 100 - Len(MyCol)) + MyCol
        else MyCol
    end

100 应替换为该列的实际长度。

于 2014-06-20T02:56:02.153 回答