4

我有一个带有 BigMacs nvarchar(255) 列的 MS SQL 表 McTable。我想获得 BigMacs 值大于 5 的行。

我要做的是:

select * from
  (
    select 
      BigMacs BigMacsS, 
      CAST(BigMacs as Binary) BigMacsB, 
      CAST(BigMacs as int) BigMacsL
    from 
      McTable
    where 
      BigMacs Like '%[0-9]%'
  ) table
where 
  Cast(table.BigMacsL as int) > 5 

结果我得到一个错误:

将 nvarchar 值“***”转换为数据类型 int 时,状态 1,第 67 行转换失败。

但是当我删除最后一个过滤器where Cast(table.BigMacsL as int) > 5时,它可以工作,我得到了这个结果:

6 0x36000000000000000000000000000000000000000000000000000000000000 6
23 0x3200330000000000000000000000000000000000000000000000000000000 23
22 0x320032000000000000000000000000000000000000000000000000000000 22
24 0x3200340000000000000000000000000000000000000000000000000000000 24
25 0x3200350000000000000000000000000000000000000000000000000000000 25
3 0x33000000000000000000000000000000000000000000000000000000000000 3
17 0x3100370000000000000000000000000000000000000000000000000000000 17
17 0x3100370000000000000000000000000000000000000000000000000000000 17
19 0x310039000000000000000000000000000000000000000000000000000000 19
20 0x32003000000000000000000000000000000000000000000000000000000000 20
659 0x360035003900000000000000000000000000000000000000000000000000 659
1 0x31000000000000000000000000000000000000000000000000000000000000 1
43 0x3400330000000000000000000000000000000000000000000000000000000 43
44 0x3400340000000000000000000000000000000000000000000000000000000 44
45 0x3400350000000000000000000000000000000000000000000000000000000 45
46 0x3400360000000000000000000000000000000000000000000000000000000 46
47 0x340037000000000000000000000000000000000000000000000000000000 47
44 0x3400340000000000000000000000000000000000000000000000000000000 44
44 0x3400340000000000000000000000000000000000000000000000000000000 44
47 0x340037000000000000000000000000000000000000000000000000000000 47
43 0x3400330000000000000000000000000000000000000000000000000000000 43
50 0x3500300000000000000000000000000000000000000000000000000000000 50
44 0x3400340000000000000000000000000000000000000000000000000000000 44

当我将第一个查询“select * from”更改为“select top 18 * from”时,我也没有收到错误!

我不知道是什么问题以及如何使它工作!请你帮助我好吗?

再一次:我在这里尝试完成的是让这些具有 BigMacs 值大于 5 的 McTable 行。

更新

重现此错误的步骤:

我已经准备好查询,因此您可以轻松地在数据库上收到此错误:

创建数据库TestDB,创建表:

USE [TestDB]
GO
/****** Object:  Table [dbo].[TestTable]    Script Date: 04/08/2009 16:27:40 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[TestTable](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [MyVal] [nvarchar](255) COLLATE Polish_CI_AS NOT NULL,
 CONSTRAINT [PK_TestTable] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

插入值:

delete from TestDB.dbo.TestTable
insert into TestDB.dbo.TestTable (MyVal) values ('fd')
insert into TestDB.dbo.TestTable (MyVal) values ('54543534')
insert into TestDB.dbo.TestTable (MyVal) values ('fat3tv3gv5')
insert into TestDB.dbo.TestTable (MyVal) values ('fdf4v43 4v434v')
insert into TestDB.dbo.TestTable (MyVal) values (' g dfg dfg df')
insert into TestDB.dbo.TestTable (MyVal) values ('f sd 4t4gsdf')
insert into TestDB.dbo.TestTable (MyVal) values ('f df 4 trwefg')
insert into TestDB.dbo.TestTable (MyVal) values ('f sd f4  fgsfg sd')
insert into TestDB.dbo.TestTable (MyVal) values ('54534534')
insert into TestDB.dbo.TestTable (MyVal) values ('454')

这个查询:

    select 
        CAST(MyVal as int) MyValInt 
    from 
        dbo.TestTable 
    where 
        IsNumeric(MyVal) = 1

产生有效数字,如下所示:

54543534

54534534

454

当您尝试使用此查询获取过滤值时:

select 
    * 
from
    (
        select 
            CAST(MyVal as int) MyValInt 
        from 
            dbo.TestTable 
        where 
            IsNumeric(MyVal) = 1
    ) tabela
where 
    tabela.MyValInt > 6

你应该得到这个不应该发生的错误:

消息 245,级别 16,状态 1,行 1 将 nvarchar 值“fd”转换为数据类型 int 时转换失败。

4

9 回答 9

6

修改后的脚本的新答案。正在发生的事情是 SQL Server 查询优化器正在优化您的子查询。它对测试表执行单次扫描,并将内部和外部 WHERE 子句合并为一个。这就是为什么你仍然得到错误。要查看这一点,请查看查询的估计执行计划,并将鼠标悬停在聚集索引扫描图标上以查看实际执行的内容。您将看到以下谓词应用于扫描:

CONVERT(int,[testdb].[dbo].[TestTable].[MyVal],0)>(6)
AND isnumeric(CONVERT_IMPLICIT(varchar(510),
    [testdb].[dbo].[TestTable].[MyVal],0))=(1)

因此,无论您的查询结构如何,它都会尝试对表中的每一行进行 CAST/CONVERT ......

为避免这种情况,请使用无法优化的表变量或临时表:

DECLARE @integers table (
    MyValInt int
)

INSERT
INTO    @integers
SELECT  CAST(MyVal AS int)
FROM    dbo.TestTable 
WHERE   ISNUMERIC(MyVal) = 1

SELECT  *
FROM    @integers
WHERE   MyValInt > 6

您实际想要返回的结果集会有所不同,因此我建议将主键与 int 值一起存储在 table 变量中,然后像这样将最终查询作为连接进行:

DECLARE @integers table (
    ID int,
    MyValInt int
)

INSERT
INTO    @integers
SELECT  ID, CAST(MyVal AS int)
FROM    dbo.TestTable 
WHERE   ISNUMERIC(MyVal) = 1

SELECT  b.*
FROM    @integers t
        INNER JOIN
                TestTable b
                ON b.ID = t.ID
WHERE   t.MyValInt > 6
于 2009-04-08T15:36:45.993 回答
2

将 nvarchar 值“***”转换为数据类型 int 时,状态 1,第 67 行转换失败。

您正在获取此值,因为其中的某些值BigMacs.BigMac包含非数字值。在你的情况下“***”。

当我将第一个查询“select * from”更改为“select top 18 * from”时,我也没有收到错误!

这是因为至少首先返回的 18 行具有数值BigMacs.BigMac

创建一个名为isReallyNumeric()的新用户定义方法,该方法处理真正的数字或非数字。

使用isReallyNumeric()函数仅过滤掉数字 BigMac
我还优化了查询,以便BigMacs.BigMac使用 CTE(公用表表达式)将查询转换为整数。

with NumericBigMacs as (
    select 
      BigMacs as BigMacsS, 
      CAST(BigMacs as Binary) as BigMacsB, 
      CAST(BigMacs as int) as BigMacsL
    from 
      McTable
    where 
      -- Filter only numeric values to compare.
      -- BigMacs Like '%[0-9]%'
      dbo.isReallyNumeric(BigMacs) = 1
)
select  *
from    NumericBigMacs NBM
where   BigMacsL > 5
于 2009-04-08T12:04:41.383 回答
1

问题是,如果它确实包含一个 int,您只能将一个值 CAST 到一个 int。显然你的前 18 行是这样。但是,如果您包含更多行,它会到达无法将值强制转换为 int 的行,并且您会收到您描述的错误。这个怎么样:

select 
      BigMacs BigMacsS, 
      CAST(BigMacs as Binary) BigMacsB 
    from 
      McTable
    where 
      BigMacs Like '%[6-9]%'
    or
      BigMacs LIKE '%[1-9][0-5]%'

这将在该列的文本中查找包含大于 5 的数字的所有行(假设不包含小数或负数)。

于 2009-04-08T11:57:34.303 回答
1

将字符数据和数字存储在同一列中时,OK isumeric 并不总是有效。它也不限于可以转换为整数的项目。有关说明,请参阅此链接: http ://www.tek-tips.com/faqs.cfm?fid=6423

我的第一个问题是,为什么要将要用作数字和字符的东西存储在同一列中?这是一个严重的设计缺陷,应尽可能予以纠正。

我相信该链接可能会帮助您弄清楚如果您无法更改结构该怎么办。

于 2009-04-08T13:37:56.183 回答
0

这似乎给出了预期的结果

编辑

包括你的 where 子句

DECLARE @McTable TABLE (BigMacs VARCHAR(20))

INSERT INTO @McTable VALUES ('1')
INSERT INTO @McTable VALUES ('1dqsf')
INSERT INTO @McTable VALUES ('qsfsq1')
INSERT INTO @McTable VALUES ('10')

select   
  BigMacs,
  cast(BigMacs as Binary) as BigMacsB, 
  cast(BigMacs as int) as BigMacsL
from @McTable
where IsNumeric(BigMacs) = 1
      and cast(BigMacs as int) > 5
于 2009-04-08T12:00:21.037 回答
0

这就是正在发生的事情:谓词BigMacs Like '%[0-9]%'并不完全符合您的想法。它选择字符串中某处至少有一位数字的行。

这不是你想要的。你想要只有数字的字符串。不幸的是,LIKE 通配符并没有给我们一个简单的方法来要求它。

我们可能会为您的问题提供足够的帮助。如果我们要求

not (BigMacs like "%[A-Za-z!@#$%^&*()=;:'""]%") 

我们将过滤掉除数字之外的大多数。“大多数”,因为我们的 like 通配符不包含所有可能的非数字字符。反过来,这应该允许演员工作。

所以:

select * from
  (
    select 
      BigMacs BigMacsS, 
      CAST(BigMacs as Binary) BigMacsB, 
      CAST(BigMacs as int) BigMacsL
    from 
      McTable
    where 
      not (BigMacs like "%[A-Za-z!@#$%^&*()=;:'""]%") 
  ) table
where 
  Cast(table.BigMacsL as int) > 5
于 2009-04-08T12:07:17.727 回答
0

我认为使用ISNUMERIC()函数也有帮助。

例子:

SELECT * FROM
(
    SELECT CAST(CASE WHEN ISNUMERIC(myval)=1 THEN myval ELSE 0 END AS INT) AS mi
    FROM dbo.TestTable 
) AS t2
WHERE mi>5
于 2009-04-08T12:10:00.590 回答
0

我认为 David M. 已经把它钉在了头上,但对于那些要求重现问题的脚本的人来说:

CREATE TABLE dbo.Test_Int_Conversion
(
    my_id   INT IDENTITY    NOT NULL,
    my_str  VARCHAR(20)     NOT NULL,
    CONSTRAINT PK_Test_Int_Conversion PRIMARY KEY CLUSTERED (my_id)
)
GO

INSERT INTO dbo.Test_Int_Conversion (my_str) VALUES ('1')
INSERT INTO dbo.Test_Int_Conversion (my_str) VALUES ('2')
INSERT INTO dbo.Test_Int_Conversion (my_str) VALUES ('3')
INSERT INTO dbo.Test_Int_Conversion (my_str) VALUES ('4')
INSERT INTO dbo.Test_Int_Conversion (my_str) VALUES ('5')
INSERT INTO dbo.Test_Int_Conversion (my_str) VALUES ('6')
INSERT INTO dbo.Test_Int_Conversion (my_str) VALUES ('7')
INSERT INTO dbo.Test_Int_Conversion (my_str) VALUES ('8')
INSERT INTO dbo.Test_Int_Conversion (my_str) VALUES ('9')
INSERT INTO dbo.Test_Int_Conversion (my_str) VALUES ('*')
GO

SELECT * FROM (
SELECT
    my_id,
    CAST(my_str AS INT) my_strI
FROM
    dbo.Test_Int_Conversion
WHERE
    my_str LIKE '%[0-9]%'
) SQ
WHERE
    CAST(SQ.my_strI AS INT) > 5
于 2009-04-08T16:01:10.113 回答
0

该代码应该可以工作:

select
    tabela.*
from
    (
        select
                CAST(MyVal as int) MyValInt
        from
                dbo.TestTable
        where
                IsNumeric(MyVal) = 1
    ) tabela
    left join (select 1 a )a on tabela.MyValInt > 6

我认为原始查询失败的原因可能与SQL评估查询的顺序有关。

于 2009-04-08T16:24:43.520 回答