Sql 在文本列中存储数字数据时出现问题-选择…介于

Sql 在文本列中存储数字数据时出现问题-选择…介于,sql,performance,Sql,Performance,几年前,我在一个系统上工作,其中一个数字主键存储在[SQL Server]varchar列中,因此当使用BEVER运算符进行查询时,我很快就失去了联系: SELECT ID FROM MyTable WHERE ID BETWEEN 100 AND 110; 结果: 100 102 103 109 110 11 这简直是个糟糕的设计。然而,我正在开发一个第三方ERP系统,正如你所想象的,它需要通用性和灵活性;因此,我们有各种表格,其中提供了字母数字字段,而业务仅使用数字-因此可能会出现类似的

几年前,我在一个系统上工作,其中一个数字主键存储在[SQL Server]varchar列中,因此当使用BEVER运算符进行查询时,我很快就失去了联系:

SELECT ID FROM MyTable WHERE ID BETWEEN 100 AND 110;
结果:

100
102
103
109
110
11
这简直是个糟糕的设计。然而,我正在开发一个第三方ERP系统,正如你所想象的,它需要通用性和灵活性;因此,我们有各种表格,其中提供了字母数字字段,而业务仅使用数字-因此可能会出现类似的问题

我猜这是一个相当普遍的问题;我有一个足够简单的解决方案,但我很好奇其他人是如何处理这些问题的

我的简单解决方案是:

SELECT ID FROM MyTable 
WHERE ID BETWEEN iStartValue AND iEndValue 
AND (LENGTH(ID) = LENGTH(iStartValue)
 OR LENGTH(ID) = LENGTH(iEndValue));
正如您可能知道的,这是一个Oracle系统,但我通常在SQL Server中工作——因此,也许数据库无关解决方案更可取

编辑1:我不明白为什么专有解决方案不受欢迎

编辑2:感谢所有的回复。我不确定我是否对没有一个明显的、复杂的解决方案感到失望,但我相应地感到高兴的是,我似乎没有遗漏任何明显的东西

我想我还是喜欢我自己的解决方案;它很简单而且有效-有什么理由我不应该使用它吗?我不相信它比其他解决方案的效率低多少(如果有的话)


我意识到在一个理想的世界里,这个问题是不存在的;但不幸的是,我并不是在理想的环境中工作,通常情况下,这是一种充分利用不利情况的情况。

如果您确定ID中的值仅为数字,为什么不直接将其转换为强制转换

WHERE CAST(ID as int) BETWEEN iStartValue AND iEndValue
编辑1: 强制转换方法的一个扩展应该是使用子查询来提取所有数字记录。请注意-我不认为这个方法比上面建议的方法好,我包括它,因为它回答了问题

SELECT ID 
FROM    (
    SELECT  ID
    FROM    MyTable 
    WHERE   ISNUMERIC(ID) = 1
    AND CHARINDEX ('.', ID) = 0
    AND CHARINDEX ('-', ID) = 0
    ) a
WHERE   CONVERT(bigint, ID)  BETWEEN 0 AND 12000
ORDER BY LENGTH(ID) ASC, ID

实际上并不需要检查“-”和“.”字符。我假设你的身份证不能是负数或小数

换个演员怎么样

SELECT ID FROM MyTable 
WHERE cast(ID as signed) BETWEEN cast(iStartValue as signed) AND cast(iEndValue as signed)

给出的这个语法是MySQL,但是T-SQl也有类似的CAST操作符。

我不知道这是否适用于您的情况,但是

如何向表中添加一个实际的数字列,并填充该值(SQL Server您可以使用一个计算列,并在其上建立持久化索引)

在其他供应商中,DBs使用一些其他机制来填充(触发器、物化视图等)

然后使用该列而不是varchar列

也许LPAD(id,12,”)适合您。 它应该使所有列值的宽度为12,并在左侧填充空格

我也会有点担心varchar2列中的数字


如果你做任何数字的事情,比如分析,你可能会在非数字数据上得到一个例外。

另一个选择是用零填充数字,并在上面使用between操作符。出于可搜索性的原因,最好将其作为第二个where条件(以便仍然可以使用可能的索引)。像这样的

SELECT ID FROM MyTable 
WHERE  ID BETWEEN iStartValue AND iEndValue 
       And Right('0000000000' + ID, 10) Between iStartValue and iEndValue 

我在SQLServer中对此进行了测试,它返回了正确的值。在第一个示例中,有历史数据阻止了这种情况的发生-数据库被多个应用程序使用,而我们没有能力更改它。在第二种情况下,许多表是通用/多用途的,因此在某些情况下,应用程序的其他部分允许/使用alpha。以这种方式强制转换将防止使用索引。欢迎使用TableScan。[如果不清楚-当表中有字母数字数据时强制转换导致错误(SQL错误:ORA-01722:无效数字)]我已经更新了它,以显示一个应该可以工作的方法。然而,我并不推荐它!回复:David B的观点——在这种情况下,可以使用基于函数的索引。是的,我考虑过这一点,但我的建议并不恰当(请参阅对RB的评论)。这是个好主意,但在这两种情况下,我都不能改变模式。在第二个示例中,我正在为ERP系统构建附加功能,因此我不得不在ERP开发人员设定的范围内工作。很遗憾,您可以添加视图吗?你可以把你的计算列(和聚集索引)放在那里。我已经有一年左右没有使用Oracle了,但是如果你在强制转换时遇到转换错误,那么当它在上面的语句中进行隐式转换时,你不会遇到错误吗?LENGTH是一个用于处理字符串的函数,所以不会。