Reputation: 257
So, I know this is bad database design but unfortunately this is what I have to use for my job, and I'm struggling to figure it out.
I'm writing a sql query that checks if the value of a certain field is between two values.
Select *
from TestTable
where TestField between ValueA and ValueB
The only problem is that ValueA and ValueB and TestField can be either integers or varchars. They can either be something like 100 or something like X100. Values that start with X are greater than any integer without an X.
So, if ValueA is 100 and ValueB is 2000, I want to return all rows where Testfield is between 100 and 2000.
However, if ValueA is 100 and ValueB is X500, I want to return all integers greater than 100 and all values that start with X and the trailing number less than 500.
For example the order would be, from smallest to greatest:
1. 100
2. 500
3. 1000
4. 5000
5. X100
6. X500
7. X1000
8. X2000
So, for my question: How could I write a query that accurately searches between ValueA and ValueB?
With valueA and valueB as varchars it prevents me from accurately checking which numbers are greater.
Upvotes: 0
Views: 192
Reputation: 19564
This should do it:
DECLARE
@Test TABLE
(
TestField VARCHAR(10)
);
INSERT INTO
@Test (TestField)
VALUES
('100'), ('500'), ('1000'), ('X10'), ('X500'), ('X3')
;
SELECT
TestField
FROM
@Test
ORDER BY
CASE
WHEN LEFT(TestField, 1) = 'X'
THEN 1
ELSE 0
END
,CAST(REPLACE(TestField, 'X', '') AS INTEGER)
Basically, you're ordering first based upon whether it begins with an "X" or not, then removing the "X" and ordering it by its integer value.
Upvotes: 1
Reputation: 50064
I think it would be a good idea to handle the building of the WHERE clause on the application side in C#.
sqlstring="SELECT * FROM table WHERE ";
If (Regex.Matches(yourstring2,@"[a-zA-Z]").Count > 0) {
sqlstring=sqlstring + " testfield > @param1a OR testfield BETWEEN @param2a AND param2b"
} else {
sqlstring=sqlstring + " testfield BETWEEN @param1 AND @param2"
}
Then you can bind with similar logic
yourstring1=500;
yourstring2="x5000";
If (Regex.Matches(yourstring2,@"[a-zA-Z]").Count > 0) {
command.Parameters.Add("@param1a", SqlDbType.Int);
command.Parameters["@param1a"].Value = yourstring1;
command.Parameters.Add("@param2a", SqlDbType.Varchar);
command.Parameters["@param2a"].Value = yourstring2.substring(1,1) + "0";
command.Parameters.Add("@param2b", SqlDbType.Varchar);
command.Parameters["@param2b"].value = yourstring2;
} else {
command.Parameters.Add("@param1", SqlDbType.Int);
command.Parameters["@param1"].Value = yourstring1;
command.Parameters.Add("@param2", SqlDbType.Int);
command.Parameters["@param2"].value = yourstring2;
}
My C# is rusty at best, so I'm 100% certain that the syntax is off here and there is a much more elegant way to write this, but... the gist is the same. The programming language feels like the right place to do this instead of the SQL where this may be possible, but it's going to be really ugly and potentially error prone. In C# we have all the info needed and a lot of nuanced control that we are lacking on the SQL side.
And that's coming from someone that is generally berating people for trying to do things on the application side that should be done in the database.
Upvotes: 1