简体   繁体   中英

SQL SELECT How to find a substring number in between two columns?

Given a search string (partial number) and a table that has two varchar columns FROM_NUMBER and TO_NUMBER, I want to select rows that contain the search string in any number between FROM_NUMBER and TO_NUMBER (inclusive).

I know LIKE '%xxx%' but this is for matching a column only.

Example: I have the following table NUMBER_RANGE

| ID | PREFIX | FROM_NUMBER | TO_NUMBER  |
|----|--------|-------------|------------|
| 1  | +1     | 4081234500  | 4081234599 |
| 2  | +61    | 267222000   | 267222009  |
| 3  | +81    | 11812205    | 11812205   |
| 4  | +61    | 240859600   | 240859600  |

Say, given "4501", it should return row 1 as "4501" is substring of a number "4081234501" which is between "4081234500" and "4081234599"

Given "4081234500" or "4081234509" or "4081234599", it should return row 1.

Given "408", it should return row 1 and 4.

How can I construct a SQL SELECT statement with correct WHERE conditions to return the records I want?

Can I search without expanding FROM_NUMBER to TO_NUMBER on-the-go (because it's possible to have 1000 numbers in a range and it takes too long to process)?

Thanks.

looking to your fist sample .. You could just convert the string as number

select 'OK' 
from dual
where 4501 between   
                 CAST(right('4081234500', length('4501')) AS INT)  
            AND  CAST(right('4081234599', length('4501')) AS INT)
; 

and for your table assuming my_value contain '4501'

select id 
from my_table 
 where cast(my_value) between    
               cast(right(FROM_NUMBER, length(my_value)) AS INT)  
         AND  CAST(right(TO_NUMBER, length(my_value)) AS INT)

for the second sample you should use the left part of the string instead of the right and use OR for use the two condition togheter

As far as I understood your problem, I have made query to find if exact number is present as sub string your columns or number that is given present in the last matching numbers of your columns. ie if length of provided number is 2 then last two digits of from and to number will be considered for range search.

You can try following query:

YOUR_NUMBER = 4501

SELECT * FROM TABLE
WHERE FROM_NUMBER LIKE '%' || YOUR_NUMBER || '%'
OR TO_NUMBER LIKE '%' || YOUR_NUMBER || '%'
OR YOUR_NUMBER 
BETWEEN MOD(FROM_NUMBER, POWER(10, LENGTH(YOUR_NUMBER))) 
AND MOD(TO_NUMBER, POWER(10, LENGTH(YOUR_NUMBER)));

db<>fiddle demo

Cheers!!

This query looks complicated but it is not. The idea is to cut numbers into blocks of pattern-length and compare with pattern, just like a man does it:

with 
  s(str) as (select 81220 from dual),
  a as (
    select id, str, length(str) ls, from_number fn, to_number tn, 
           lpad(from_number, length(to_number), '0') s1, to_char(to_number) s2
      from number_range join s on length(to_number) >= length(str)),
  c(col, id, str, ls, s1, s2, sb1, sb2, match) as (
    select 1, id, str, ls, s1, s2, substr(s1, 1, ls), substr(s2, 1, ls), 
           case when str between substr(s1, 1, ls) 
                             and substr(s2, 1, ls) then 1 end
      from a
    union all
    select col + 1, id, str, ls, s1, s2, substr(s1, col + 1, ls), substr(s2, col + 1, ls),
           case when str between substr(s1, col + 1, ls) 
                             and substr(s2, col + 1, ls) then 1 end
      from c 
      where col  <= length(s1) - ls and match is null )
select id, str, sb1, sb2, prefix, from_number, to_number
  from c join number_range using (id)
  where match = 1 order by id, col;

dbfiddle demo

s - value to find (you can put several values here using union)

a - data from your table where from_number is lpadded with zeros to the to_number 's length

c - is recursive query. I cut first block from both numbers and compare with pattern. If its between the analysis stops ( and match is null is responsible for that). Otherwise it continues.

Finally I join results (if anything was found) with original data and show which parts of strings where corresponding. This query finds first occurence for each row, if there are more and you want them all remove and match is null . Hope this helps.

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