简体   繁体   English

修复SQL where丑陋且令人困惑的语句

[英]Fixing an SQL where statement that is ugly and confusing

I am directly querying the back-end MS SQL Server for a software package. 我直接查询后端MS SQL Server的软件包。 The key field (vehicle number) is defined as alpha though we are entering numeric value in the field. 虽然我们在字段中输入数值,但关键字段(车辆编号)被定义为alpha。 There is only one exception to this, we place an "R" before the number when the vehicle is being retired (which means we sold it or the vehicle is junked). 这只有一个例外,我们在车辆退役时在号码前加上“R”(这意味着我们卖掉它或者车辆被废弃)。 Assuming the users do this right, we should not run into a problem using this method. 假设用户这样做,我们不应该使用此方法遇到问题。 (Right or wrong isn't the issue here) (对或错不是问题)

Fast forward to now. 快进到现在。 I am trying to query a subset of these vehicle numbers (800 - 899) for some special processing. 我正在尝试查询这些车辆编号的子集(800 - 899)以进行一些特殊处理。 By doing a range of '800' to '899' we also get 80, 81, etc. If I cast the vehicle number into an INT, I should be able to get the right range. 通过做'800'到'899'的范围,我们也得到80,81等。如果我将车辆编号投入INT,我应该能够获得正确的范围。 Except that these "R" vehicles are kicking me in the butt now. 除了这些“R”车辆现在踢我的屁股。

I have tried where vehicleId not like 'R%' and cast(vehicleId as int) between 800 and 899 however, I get a casting error on one of these "R" vehicles. 我已经尝试过where vehicleId not like 'R%' and cast(vehicleId as int) between 800 and 899但是,我在其中一辆“R”车辆上出现了投射错误。

What does work is where vehicleId not between '800' and '899' and cast(vehicleId as int) between 800 and 899' , but I feel there has to be a better way and less confusing way. 什么工作是where vehicleId not between '800' and '899' and cast(vehicleId as int) between 800 and 899' ,但我觉得必须有更好的方式和更少混乱的方式。

I have also tried other variations with HAVING and a sub-query all producing a casting error. 我还尝试了HAVING和子查询的其他变体都产生了投射错误。

虽然未经测试,但这应该可以解决问题:

where cast(replace(vehicleId,'R','') as int) between 800 and 899

Use the _ operator instead of the % operator: 使用_运算符而不是%运算符:

WHERE vehicleId LIKE 'R8__' OR vehicleId LIKE '8__'

You may also combine them like so: 您也可以将它们组合起来:

WHERE vehicleId LIKE '%8__'

There is no guarantee that the optimizer will execute these in the right order (there's currently no short-circuit evaluation in SQL): 无法保证优化器将以正确的顺序执行这些(当前SQL中没有短路评估):

where vehicleId not like 'R%' and cast(vehicleId as int) between 800 and 899 

So the cast can fail. 演员阵容可能会失败。

Try this: 尝试这个:

WHERE
    CASE
        WHEN vehicleId NOT LIKE 'R%' THEN
            CAST(vehicleId as int)
        ELSE
            0
    END BETWEEN 800 AND 899 

How about this: 这个怎么样:

WHERE vehicleId BETWEEN '800' and '899' AND LEN(vehicleId) = 3

Not perfectly clean, but it would eliminate the two character matches. 不是很干净,但它会消除两个角色的匹配。

OK your problem is you are trying to do a math sort on a string field. 好吧你的问题是你正在尝试对字符串字段进行数学排序。

try something like using a derived table to first exclude the nonnumerics and tehn cast the resultant data to int 尝试使用派生表来首先排除非数字和tehn将结果数据转换为int

select * from 
(select * from mytable where vehicleId not like 'R%')a
where cast(vehicleId as int) between 800 and 900

Well that didn't work, you could try select * from (select * from mytable where isnumeric(vehicleId)=0)a where cast(vehicleId as int) between 800 and 900 好吧,这不起作用,你可以尝试select * from(select * from mytable where isnumeric(vehicleId)= 0)a cast(vehicleId as int)在800和900之间

There are some issues with isnumeric, but if it works you could write an isint() function and use that instead. isnumeric存在一些问题,但是如果它有效,你可以编写一个isint()函数并使用它。

where 1=
(
    case 
        when 
            vehicleId like 'R%' then 0 
        else 
            (case when cast(vehicleId as int) between 800 and 899 then 1 else 0 end) 
    end
)

I'm not sure if you wish to include the 'R%' records and whether the range of vehicleIds is variable or not, but this might help. 我不确定您是否希望包含'R%'记录以及vehicleIds的范围是否可变,但这可能会有所帮助。

WHERE vehicleId LIKE '8[0-9][0-9]' 
OR vehicleId LIKE 'R8[0-9][0-9]'

If the range is a variable then you may want something like this. 如果范围是变量,那么你可能想要这样的东西。

WHERE CASE WHEN vehicleId LIKE 'R%' THEN CAST(SUBSTRING(vehicleId,2,99) AS INT)
    WHEN vehicleId LIKE '[0-9][0-9][0-9]' THEN CAST(vehicleId AS INT)
    END BETWEEN 800 AND 899

I do somthing like this when I'm dealing with things like this... 当我处理这样的事情时,我会做这样的事情......

DECLARE @Autos Table  (
ID varchar(100)
)

Insert into @Autos (ID) values ('R1')
Insert into @Autos (ID) values ('2')
Insert into @Autos (ID) values ('3')
Insert into @Autos (ID) values ('R4')
Insert into @Autos (ID) values ('5')
Insert into @Autos (ID) values ('R52')
Insert into @Autos (ID) values ('53')
Insert into @Autos (ID) values ('R8')

Select * from @Autos
Where cast(REPLACE(ID, 'R', '0') as integer) between 2 and 55
and ID not like 'R%'

Output is, 输出是,

ID
---
2
3
5
53

--Kris --Kris

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM