我有一个带有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
结果我收到错误:
状态1,第67行转换失败 转换nvarchar值时 '***' 至 数据类型int。
但是,当我删除最后一个过滤器where Cast(table.BigMacsL as int) > 5
时,它会起作用,我得到了这个结果:
6 0x360000000000000000000000000000000000000000000000000000000000 6 23 0x320033000000000000000000000000000000000000000000000000000000 23 22 0x320032000000000000000000000000000000000000000000000000000000 22 24 0x320034000000000000000000000000000000000000000000000000000000 24 25 0x320035000000000000000000000000000000000000000000000000000000 25 3 0x330000000000000000000000000000000000000000000000000000000000 3 17 0x310037000000000000000000000000000000000000000000000000000000 17 17 0x310037000000000000000000000000000000000000000000000000000000 17 19 0x310039000000000000000000000000000000000000000000000000000000 19 20 0x320030000000000000000000000000000000000000000000000000000000 20 659 0x360035003900000000000000000000000000000000000000000000000000 659 1 0x310000000000000000000000000000000000000000000000000000000000 1 43 0x340033000000000000000000000000000000000000000000000000000000 43 44 0x340034000000000000000000000000000000000000000000000000000000 44 45 0x340035000000000000000000000000000000000000000000000000000000 45 46 0x340036000000000000000000000000000000000000000000000000000000 46 47 0x340037000000000000000000000000000000000000000000000000000000 47 44 0x340034000000000000000000000000000000000000000000000000000000 44 44 0x340034000000000000000000000000000000000000000000000000000000 44 47 0x340037000000000000000000000000000000000000000000000000000000 47 43 0x340033000000000000000000000000000000000000000000000000000000 43 50 0x350030000000000000000000000000000000000000000000000000000000 50 44 0x340034000000000000000000000000000000000000000000000000000000 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
您应该收到不应发生的错误:
Msg 245,Level 16,State 1,Line 1 将nvarchar值'fd'转换为数据类型int时,转换失败。
答案 0 :(得分: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值一起存储在表变量中,然后将最终查询作为这样的连接进行:
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
答案 1 :(得分:2)
状态1,第67行将nvarchar值'***'转换为数据类型int时,转换失败。
您获得此值是因为BigMacs.BigMac
中的某些值包含非数字值。在你的情况下“***”。
当我更改第一个查询'select * from'到'select top 18 * from'时,我也没有得到错误!
这是因为至少首先返回18行具有数字BigMacs.BigMac
值。
创建一个名为isReallyNumeric()的新用户定义方法,该方法可以解决实际数字与否的问题。
使用isReallyNumeric()功能
仅过滤掉数字BigMac
我还使用CTE(公用表表达式)优化了查询以将BigMacs.BigMac
转换为整数。
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
答案 2 :(得分: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的数字的所有行(假设不包含小数或负数)。
答案 3 :(得分:1)
在同一列中存储字符数据和数字时,OK isnumeric并不总是有效。它也不仅限于可以转换为整数的项目。请参阅此链接以获取解释: http://www.tek-tips.com/faqs.cfm?fid=6423
我的第一个问题是,为什么要将想要用作数字和字符的东西存储在同一列中?这是一个严重的设计缺陷,如果可能的话应该予以纠正。
我相信这个链接可能会帮助你弄清楚如果你不能改变结构会怎么做。
答案 4 :(得分: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
答案 5 :(得分:0)
以下是发生的事情:谓词BigMacs Like '%[0-9]%'
并不能完全按照你的想法行事。它选择字符串中某处至少有一位数的行。
这不是你想要的。您需要仅具有数字的字符串。不幸的是,LIKE wilcards并没有给我们一个简单的方法来要求它。
我们可能已经足够接近您的问题了。如果我们要求
not (BigMacs like "%[A-Za-z!@#$%^&*()=;:'""]%")
我们会过滤掉大多数包含但数字的行。 “大多数”,因为我们的通配符不包含所有可能的非数字字符。反过来,这应该允许演员工作。
所以:
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
答案 6 :(得分: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
答案 7 :(得分: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
答案 8 :(得分: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评估查询的顺序有关。