简体   繁体   中英

Fixing an SQL where statement that is ugly and confusing

I am directly querying the back-end MS SQL Server for a software package. The key field (vehicle number) is defined as alpha though we are entering numeric value in the field. 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). 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. 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. Except that these "R" vehicles are kicking me in the butt now.

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.

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.

I have also tried other variations with HAVING and a sub-query all producing a casting error.

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

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):

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

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

There are some issues with isnumeric, but if it works you could write an isint() function and use that instead.

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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