在文本列中存储数字数据时出现问题 - SELECT ... BETWEEN

时间:2008-12-10 14:53:16

标签: sql performance

几年前,我在一个数字主键存储在[SQL Server] varchar列中的系统上工作,所以当使用BETWEEN运算符查询时,我很快就解开了:

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:感谢所有回复。我不确定我是否感到失望,没有一个明显的,复杂的解决方案,但我相应很高兴看起来我没有错过任何明显的东西!

我想我仍然更喜欢自己的解决方案;它很简单而且有效 - 我有什么理由不使用它吗?我无法相信其他解决方案提供的效率会有多大,甚至更低。

我意识到在理想世界中,这个问题不会存在;但不幸的是,我不是在一个理想的世界中工作,而且往往是一个充分利用糟糕情况的案例。

6 个答案:

答案 0 :(得分:7)

如果你确定ID中的值只是数字,为什么不只是CAST呢

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

检查“ - ”和“。”字符并不是真正需要的。我假设你的ID不能是负数或小数。

答案 1 :(得分:2)

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

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

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

然后使用该列而不是varchar ...

答案 2 :(得分:1)

相反如何投射。

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

给出的语法是MySQL,但T-SQl有类似的CAST运算符。

答案 3 :(得分:1)

也许 LPAD(id,12,'')会对你有用。 它应该使所有列值都宽12,并在左侧填充空格。

另外,我会对varchar2列中的数字有点关注。

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

答案 4 :(得分:1)

另一种选择是用零填充你的数字并使用之间的运算符。出于可争夺性原因,最好将其作为第二个条件包含在内(以便仍然可以使用可能的索引)。像这样......

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

我在SQL Server中对此进行了测试,并返回了正确的值。您可能需要修改它以使用Oracle。

答案 5 :(得分:0)

最后我坚持使用自己的解决方案(参见OP)。非常感谢您的努力。