How do you select using a range of strings in SQL? - sql

I have a table of vehicles with registration numbers, and want to select a subset of them that are between some user-supplied 'from' and 'to' values.
So lets say the table looks like this:
id reg_num
1 DD1111
2 DD1112
3 DE2245
4 EE5678
5 EF6547
The SQL I have so far looks like this:
select *
from vehicles
where reg_num >= 'DD' -- this value is user supplied
and reg_num <= 'DE' -- and so is this one
Which should (by my thinking) return:
1 DD1111
2 DD1112
3 DE2245
But instead, only returns:
1 DD1111
2 DD1112
I imagine that SQL server sees 'DE2245' as greater than 'DE', and so excludes the row.
My question: How do I get SQL server to include all rows that start with 'DE'?

You have to add 'zzzz's at the end as many as necessary to match your column width definition.
select * from vehicles
where reg_num >= 'DD' and reg_num <= 'DE' + 'ZZZZZZZZZZZZ'

where reg_num >= #userValueFrom
and left(reg_num,char_length(#userValueTo) <= #userValueTo
but please note that this where does not utilize any index because of a function on the column in SARG.

If the format is guaranteed, you can simply do:
SELECT *
FROM vehicles
WHERE LEFT(reg_num, 2) BETWEEN 'DD' AND 'DE'
But again, this is supposedly not SARGable - which always baffles me, because surely an index on reg_num can be used...

DE2245 is not less than DE. To make it more clear, DE2245 is less than DE3

Related

finding range by comparing two tables

I have a table in database as "EXPERIENCE RANGE" with rows as (I can also edit this table according to my need)
0
0.5
1
2
3
5
10
20
I have total experience as integer. I need to display the range in which it lies.
Example - for experience of 8, Range will be 5 - 10
I need to write a sql query. Any ideas will be quite helpful as I am new to SQL.
I cannot hard code it..need to take values from tables only.
Assuming that you are using Oracle, the following query works fine with your existing table:
SELECT
( SELECT MAX( value ) FROM experience_range WHERE value <= :search_value ) AS range_start,
( SELECT MIN( value ) FROM experience_range WHERE value > :search_value ) AS range_end
FROM dual;
No need to hardcode the values, and no need to store the lower and upper bounds redundantly.
you can do it with CASE Expression, the syntax is:
SELECT
CASE
WHEN experience >= 0 and experience <= 4 THEN '0-4'
WHEN experience >= 5 and experience <= 10 THEN '5-10'
.....
ELSE 'No Range'
END as Range
FROM Table_Name
If you do need to store the ranges in a table, I would personally suggest altering the structure of the range table (Assuming you are able to), maybe something like:
|--------------------------------------|
|ID|DESCRIPTION|LOWER_LIMIT|UPPER_LIMIT|
|1 | 0 - 0.5 | 0 | 0.5 |
|2 | 0.5 - 1 | 0.5 | 1 |
...
Then you could get your range by running something like:
SELECT DESCRIPTION FROM [RANGES] WHERE <VALUE> >= LOWER_LIMIT AND <VALUE> < UPPER_LIMIT
EDIT - Mikhail's answer also works, defining the ranges within the query itself is also an option and probably simpler providing you don't need to get these ranges from several reports. (That would require editing every report/query individually)
EDIT 2 - I see you are not able to hardcode the ranges, in which case the above would be best. Can I ask why you are unable to hardcode them?

SQL COUNT between dates in two different column

Let's say, we have this table:
STUDENT | START | END
1 |1998-1-1 |2001-1-1
2 |1999-1-1 |2001-1-1
3 |2000-1-1 |2004-1-1
4 |2000-1-1 | NULL
I'm trying to do is:
Count number of students between start and end dates!
Looks like you need to use a basic COUNT aggregate:
SELECT COUNT(Student)
FROM YourTable
WHERE Start >= #Start
AND End <= #End
I've used >= and <= respectively around the start and end date fields. Feel free to change to > or < as needed. It was unclear from your question whether you wanted between a specific field or if you were checking for a range between those two fields.
Use the between Operator and COUNT aggregate function
SELECT COUNT(student) column_name(s)
FROM table_name
WHERE column_name
BETWEEN value1 AND value2
Between can be used with text so insert the dates where the values are,
Read more here if you still don't understand
EDIT : That should work, sorry about the error
http://www.w3schools.com/sql/sql_between.asp

SQL Select using distinct and Cast [duplicate]

This question already exists:
Closed 10 years ago.
Possible Duplicate:
SQL Select DISTINCT using CAST
Let me try this one more time... I'm not a sql guy so please bear with me as I try to explain this... I have a table called t_recordkeepingleg with three columns of data. Column1 is named LEGTRIPNUMBER that happens to be a string that starts with the letter Q followed by 4 numbers. I need to strip off the Q and convert the remaining 4 characters (numbers) to an integer. Everyone with me so far? Column2 of this table is named LEGDATE. Column3 is named LEGGROUP.
Here's the input scenario
LEGTRIPNUMBER LEGDATE LEGGROUP
Q1001 08/12/12 0001
Q1001 09/15/12 0002
Q1002 09/01/12 0001
Q1002 09/08/12 0003
Q1002 09/09/12 0002
As you can see the input table has rows where LEGTRIPNUMBER occurs more than once. I only want the first occurrence.
This is my current select statement - it works but returns all rows.
SELECT *,
CAST(
substring("t_RecordkeepingLeg"."LEGTRIPNUMBER",2,4) as INT
) as Num_Trip_Num
FROM "1669"."dbo"."t_RecordkeepingLeg" "t_RecordkeepingLeg"
Where left "t_RecordkeepingLeg"."LEGTRIPNUMBER",1) = 'Q'
I want to modify this so that it only selects ONE occurance of the Qnnnn. When the row gets selected I want to have LEGDATE and LEGGROUP available to me. How do I do this?
Thank you,
Can it be as simple as below? I've just added condiotion on leggroup being 0001
SELECT *,
CAST(substring("t_RecordkeepingLeg"."LEGTRIPNUMBER",2,4) as INT) as Num_Trip_Num
FROM "1669"."dbo"."t_RecordkeepingLeg" "t_RecordkeepingLeg"
Where left ("t_RecordkeepingLeg"."LEGTRIPNUMBER",1) = 'Q'
and "t_RecordkeepingLeg"."LEGGROUP"='0001'
If you have a unique primay key in your table you can do something like the below;
SELECT CAST(
substring("t_RecordkeepingLeg"."LEGTRIPNUMBER",2,4) as INT
) as Num_Trip_Num
FROM "1669"."dbo"."t_RecordkeepingLeg" "t_RecordkeepingLeg"
Where "t_RecordkeepingLeg"."ID" In(
Select Min("t_RecordkeepingLeg"."ID")
From "1669"."dbo"."t_RecordkeepingLeg" "t_RecordkeepingLeg"
Where left ("t_RecordkeepingLeg"."LEGTRIPNUMBER",1) = 'Q'
Group By "t_RecordkeepingLeg"."LEGTRIPNUMBER"
)
Which values of LEGDATE & LEGGROUP do you want for the distinct LEGTRIPNUMBER? there are multiple non-distinct possibilities and the concept of "first occurrence" is only valid with an explicit order.
To get the values where LEGDATE is the earliest for example;
select Num_Trip_Num, LEGDATE, LEGGROUP from (
select
cast(substring(t_RecordkeepingLeg.LEGTRIPNUMBER, 2, 4) as INT) as Num_Trip_Num,
row_number() over (partition by substring(t_RecordkeepingLeg.LEGTRIPNUMBER, 2, 4) order by t_RecordkeepingLeg.LEGDATE asc) as row,
t_RecordkeepingLeg.LEGDATE,
t_RecordkeepingLeg.LEGGROUP
from t_RecordkeepingLeg
where left (t_RecordkeepingLeg.LEGTRIPNUMBER, 1) = 'Q'
) T
where row = 1

substring and trim in Teradata

I am working in Teradata with some descriptive data that needs to be transformed from a gerneric varchar(60) into the different field lengths based on the type of data element and the attribute value. So I need to take whatever is in the Varchar(60) and based on field 'ABCD' act on field 'XYZ'. In this case XYZ is a varchar(3). To do this I am using CASE logic within my select. What I want to do is
eliminate all occurances of non alphabet/numeric data. All I want left are upper case Alpha chars and numbers.
In this case "Where abcd = 'GROUP' then xyz should come out as a '000', '002', 'A', 'C'
eliminate extra padding
Shift everything Right
abcd xyz
1 GROUP NULL
2 GROUP $
3 GROUP 000000000000000000000000000000000000000000000000000000000000
4 GROUP 000000000000000000000000000000000000000000000000000000000002
5 GROUP A
6 GROUP C
7 GROUP r
To do this I have tried TRIM and SUBSTR amongst several other things that did not work. I have pasted what I have working now, but I am not reliably working through the data within the select. I am really looking for some options on how to better work with strings in Teradata. I have been working out of the "SQL Functions, Operators, Expressions and Predicates" online PDF. Is there a better reference. We are on TD 13
SELECT abcd
, CASE
-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
WHEN abcd= 'GROUP'
THEN(
CASE
WHEN SUBSTR(tx.abcd,60, 4) = 0
THEN (
SUBSTR(tx.abcd,60, 3)
)
ELSE
TRIM (TRAILING FROM tx.abcd)
END
)
END AS abcd
FROM db.descr tx
WHERE tx.abcd IS IN ( 'GROUP')
The end result should look like this
abcd xyz
1 GROUP 000
2 GROUP 002
3 GROUP A
4 GROUP C
I will have to deal with approx 60 different "abcd" types, but they should all conform to the type of data I am currently seeing.. ie.. mixed case, non numeric, non alphabet, padded, etc..
I know there is a better way, but I have come in several circles trying to figure this out over the weekend and need a little push in the right direction.
Thanks in advance,
Pat
The SQL below uses the CHARACTER_LENGTH function to first determine if there is a need to perform what amounts to a RIGHT(tx.xyz, 3) using the native functions in Teradata 13.x. I think this may accomplish what you are looking to do. I hope I have not misinterpreted your explanation:
SELECT CASE WHEN tx.abcd = 'GROUP'
AND CHARACTER_LENGTH(TRIM(BOTH FROM tx.xyz) > 3
THEN SUBSTRING(tx.xyz FROM (CHARACTER_LENGTH(TRIM(BOTH FROM tx.xyz)) - 3))
ELSE tx.abcd
END
FROM db.descr tx;
EDIT: Fixed parenthesis in SUBSTRING

Calculating a field from SQL Query Selecting from multiple tables with Union

I have the following query, which works great. The problem I have is that in both tables (and the aggregate unioned table), there is a field called MTGUID. I need to multiply MTGUID by a number (let's say 1.35, for ease of use) and have it return that number in the MTGUID field. I have tried a dozen ways to do this and can't get anything to play ball. I can create a new column for each calculated price, like (BKRETAIL.MTGUID * 1.35) AS MTG1, but we've got tens of thousands of lines of code that specifically use MTGUID. Any ideas?
I'm using Firebird SQL.
SELECT * FROM (
SELECT BKRETAIL.* FROM BKRETAIL WHERE BKRETAIL.MKEY='SOMEKEY'
UNION SELECT BKWHOLESALE.* FROM BKWHOLESALE WHERE MKEY='SOMEKEY')
ORDER BY
case STATUS
WHEN 'RT' then 1
WHEN 'WH' then 2
WHEN 'OL' then 3
WHEN 'OD' then 4
WHEN NULL then 5
else 6
end;
How about this:
SELECT MTGUID * 1.35 as calculatedMTGUID, SUBSEL.* FROM (
SELECT BKRETAIL.* FROM BKRETAIL WHERE BKRETAIL.MKEY='SOMEKEY'
UNION SELECT BKWHOLESALE.* FROM BKWHOLESALE WHERE MKEY='SOMEKEY') SUBSEL
ORDER BY
case STATUS
WHEN 'RT' then 1
WHEN 'WH' then 2
WHEN 'OL' then 3
WHEN 'OD' then 4
WHEN NULL then 5
else 6
end;
try this
SELECT MTGUID * 1.35 AS MTGUID,<list rest OF COLUMNS here>
FROM (
SELECT BKRETAIL.* FROM BKRETAIL WHERE BKRETAIL.MKEY='SOMEKEY'
UNION SELECT BKWHOLESALE.* FROM BKWHOLESALE WHERE MKEY='SOMEKEY')
ORDER BY
case STATUS
WHEN 'RT' then 1
WHEN 'WH' then 2
WHEN 'OL' then 3
WHEN 'OD' then 4
WHEN NULL then 5
else 6
end;
One option would be to replace the original MTGUID column with computed one, ie
rename the original MTGUID column in table(s);
add new MTGUID column with desired expression using COMPUTED BY (expr);
Advantage of this is that you don't have to alter your SQL statements, disadvantage is that you have to maintain the expression in many places (all the tables which have the column). Of course, the queries which need the original MTGUID value must be updated to use the renamed column, but if the number of such statements is significantly lower it could be worth the trouble.
I think a better solution would be to "hide" all this stuff behind a view but this requires alerting your SQL queries...