如何根据字段是否可以转换为数字进行过滤?

发布于 2024-10-14 07:15:24 字数 1176 浏览 1 评论 0原文

我有一份已经使用了很长一段时间的报告 - 事实上,公司的发票系统很大程度上依赖于这份报告(免责声明:我没有写它)。过滤基于 VarChar(50) 类型的字段是否位于用户传入的两个数值之间。

问题在于,现在过滤数据的字段不仅具有简单的非数字值,例如“/A”、“TEST”和大量其他非数字数据,而且还具有似乎是违背我能想到的任何类型的数字转换。

以下(简化的)测试查询演示了失败:

Declare  @StartSummary Int,
         @EndSummary Int

Select   @StartSummary = 166285,
         @EndSummary = 166289

Select   SummaryInvoice
From     Invoice
Where    IsNull(SummaryInvoice, '') <> ''
And      IsNumeric(SummaryInvoice) = 1
And      Convert(int, SummaryInvoice) Between @StartSummary And @EndSummary

我还尝试使用 bigint、real 和 float 进行转换,但都给出了类似的错误:

消息 8115,第 16 级,状态 2,第 7 行 算术溢出错误转换 表达式转换为数据类型 int。

我尝试过其他更大的数字数据类型,例如 BigInt,但出现相同的错误。我还尝试使用子查询来回避转换问题,方法是仅提取具有数字数据的字段,然后在包装器查询中转换这些字段,但随后我收到其他错误,这些错误都是主题的变体,表明存储在SummaryInvoice 字段无法转换为相关数据类型。

除了仅将具有数字 SummaryInvoice 字段的记录提取到临时表然后查询临时表之外,是否有任何一步解决方案可以解决此问题?

编辑:这是我怀疑导致问题的字段数据:

发票摘要

<小时>

11111111111111111111111111

IsNumeric 表明此字段是数字 - 它就是。但尝试将其转换为 BigInt 会导致算术溢出。有什么想法吗?这似乎不是一个孤立的事件,似乎有许多记录填充了导致此问题的数据。

I've got a report that has been in use quite a while - in fact, the company's invoice system rests in a large part upon this report (Disclaimer: I didn't write it). The filtering is based upon whether a field of type VarChar(50) falls between two numeric values passed in by the user.

The problem is that the field the data is being filtered on now not only has simple non-numeric values such as '/A', 'TEST' and a slew of other non-numeric data, but also has numeric values that seem to be defying any type of numeric conversion I can think of.

The following (simplified) test query demonstrates the failure:

Declare  @StartSummary Int,
         @EndSummary Int

Select   @StartSummary = 166285,
         @EndSummary = 166289

Select   SummaryInvoice
From     Invoice
Where    IsNull(SummaryInvoice, '') <> ''
And      IsNumeric(SummaryInvoice) = 1
And      Convert(int, SummaryInvoice) Between @StartSummary And @EndSummary

I've also attempted conversions using bigint, real and float and all give me similar errors:

Msg 8115, Level 16, State 2, Line 7
Arithmetic overflow error converting
expression to data type int.

I've tried other larger numeric datatypes such as BigInt with the same error. I've also tried using sub-queries to sidestep the conversion issue by only extracting fields that have numeric data and then converting those in the wrapper query, but then I get other errors which are all variations on a theme indicating that the value stored in the SummaryInvoice field can't be converted to the relevant data type.

Short of extracting only those records with numeric SummaryInvoice fields to a temporary table and then querying against the temporary table, is there any one-step solution that would solve this problem?

Edit: Here's the field data that I suspect is causing the problem:

SummaryInvoice


11111111111111111111111111

IsNumeric states that this field is numeric - which it is. But attempting to convert it to BigInt causes an arithmetic overflow. Any ideas? It doesn't appear to be an isolated incident, there seems to have been a number of records populated with data that causes this issue.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

一指流沙 2024-10-21 07:15:24

看来您会遇到 ISNUMERIC 函数的问题,因为如果可以转换为任何数字类型(包括 .、、e0 等)。如果数字长度超过 2^63-1,则可以使用 DECIMALNUMERIC。我不确定您是否可以使用 PATINDEXSummaryInvoice 执行正则表达式查找,但如果可以,那么您应该尝试以下操作:

SELECT SummaryInvoice
FROM Invoice
WHERE ISNULL(SummaryInvoice, '') <> ''
AND CASE WHEN PATINDEX('%[^0-9]%',SummaryInvoice) > 0 THEN CONVERT(DECIMAL(30,0), SummaryInvoice) ELSE -1 END
BETWEEN @StartSummary And @EndSummary

It seems that you are gonna have problems with the ISNUMERIC function, since it returns 1 if can be cast to any number type (including ., ,, e0, etc). If you have numbers longer than 2^63-1, you can use DECIMAL or NUMERIC. I'm not sure if you can use PATINDEX to perform an regex look on SummaryInvoice, but if you can, then you should try this:

SELECT SummaryInvoice
FROM Invoice
WHERE ISNULL(SummaryInvoice, '') <> ''
AND CASE WHEN PATINDEX('%[^0-9]%',SummaryInvoice) > 0 THEN CONVERT(DECIMAL(30,0), SummaryInvoice) ELSE -1 END
BETWEEN @StartSummary And @EndSummary
云柯 2024-10-21 07:15:24

您无法保证 WHERE 子句过滤器的应用顺序。

一种将内部和外部分离的丑陋选择。

SELECT
   *
FROM
    (
    Select   TOP 2000000000
             SummaryInvoice
    From     Invoice
    Where    IsNull(SummaryInvoice, '') <> ''
    And      IsNumeric(SummaryInvoice) = 1
    ORDER BY SummaryInvoice
    ) foo
WHERE
    Convert(int, SummaryInvoice) Between @StartSummary And @EndSummary

另一个使用 CASE

Select   SummaryInvoice
From     Invoice
Where    IsNull(SummaryInvoice, '') <> ''
    And     
    CASE WHEN IsNumeric(SummaryInvoice) = 1 THEN Convert(int, SummaryInvoice) ELSE -1 END
          Between @StartSummary And @EndSummary

YMMV

编辑:问题更新后

  1. 使用十进制(38,0)而不是 int
  2. 将 ISNUMERIC(SummaryInvoice) 更改为 ISNUMERIC(SummaryInvoice + '0e0')

You can't guarantee what order the WHERE clause filters will be applied.

One ugly option to decouple inner and outer.

SELECT
   *
FROM
    (
    Select   TOP 2000000000
             SummaryInvoice
    From     Invoice
    Where    IsNull(SummaryInvoice, '') <> ''
    And      IsNumeric(SummaryInvoice) = 1
    ORDER BY SummaryInvoice
    ) foo
WHERE
    Convert(int, SummaryInvoice) Between @StartSummary And @EndSummary

Another using CASE

Select   SummaryInvoice
From     Invoice
Where    IsNull(SummaryInvoice, '') <> ''
    And     
    CASE WHEN IsNumeric(SummaryInvoice) = 1 THEN Convert(int, SummaryInvoice) ELSE -1 END
          Between @StartSummary And @EndSummary

YMMV

Edit: after question update

  1. use decimal(38,0) not int
  2. Change ISNUMERIC(SummaryInvoice) to ISNUMERIC(SummaryInvoice + '0e0')
给我一枪 2024-10-21 07:15:24

AND 与 IsNumeric(SummaryInvoice) = 1 时,在 SQL Server 中不会短路。

但也许您可以使用

AND (CASE IsNumeric(SummaryInvoice) = 1 THEN Convert(int, SummaryInvoice) ELSE 0 END)
@StartSummary 和 @EndSummary 之间

AND with IsNumeric(SummaryInvoice) = 1, will not short circuit in SQL Server.

But may be you can use

AND (CASE IsNumeric(SummaryInvoice) = 1 THEN Convert(int, SummaryInvoice) ELSE 0 END)
Between @StartSummary And @EndSummary

轮廓§ 2024-10-21 07:15:24

您的第一个问题是修复数据库结构,以便不良数据无法进入该字段。您正在将创可贴贴在需要缝合的伤口上,并想知道为什么伤口无法愈合。

数据库重构并不好玩,但是当出现数据完整性问题时就需要这样做。我假设您实际上并没有向某人开具 11,111,111,111,111,111,111,111,111 或“测试”的发票。因此,不要允许输入这些值(如果您无法将结构更改为正确的数据类型,请考虑使用触发器来防止输入错误数据)并删除您所拥有的错误值。

Your first issue is to fix your database structure so bad data cannot get into the field. You are putting a band-aid on a wound that needs stitches and wondering why it doesn't heal.

Database refactoring is not fun, but it needs to be done when there is a data integrity problem. I assume you aren't really invoicing someone for 11,111,111,111,111,111,111,111,111 or 'test'. So don't allow those values to ever get entered (if you can't change the structure to the correct data type, consider a trigger to prevent bad data from going in) and delete the ones you do have that are bad.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文