Oracle中的SQL查询,以查找varchar列中表示的范围内的数字字段
我在Oracle中有Oracle中的SQL查询,以查找varchar列中表示的范围内的数字字段,sql,oracle,range,Sql,Oracle,Range,我在Oracle中有Colors表,数据如下: ID Color Ranges (nvarchar2!) -- ----- ------------------------- 1 Blue 1-9,23.5-25.1,27.11,99.14 2 Red 4 3 Green 4.44-5.3 4 Black 18-22,101 正如您所猜测的,Ranges列表示一些数字和数字范围 我无法将范围保存在其他一些表中
Colors
表,数据如下:
ID Color Ranges (nvarchar2!)
-- ----- -------------------------
1 Blue 1-9,23.5-25.1,27.11,99.14
2 Red 4
3 Green 4.44-5.3
4 Black 18-22,101
正如您所猜测的,Ranges
列表示一些数字和数字范围
我无法将范围保存在其他一些表中(例如RangesTable
使用ColorID
,MinVal
,MaxVal
),但我可以以某种方式将该范围
列规范化(所有方式排序,或将单个数字表示为范围(4-4
)“4
”)等)
问题:我正在寻找一种根据此字段查询Oracle的方法,询问它:哪些颜色(或ID…)的范围包含5?(答案是蓝色和绿色),或者哪些颜色与“5-6”范围重叠?(答案是蓝色[1-9]和绿色[4.44-5.3])
怎么做呢?(我想正则表达式在这里帮不上忙…)
在DB函数中编写能够分割这些范围并在其中搜索的函数是否合理?还有其他建议吗
谢谢!您可以创建如下PL/SQL函数:
function value_included (p_value in number, p_ranges in varchar2)
return number
is
l_ranges_tab apex_application_global.vc_arr2;
l_values_tab apex_application_global.vc_arr2;
l_retval number := 0;
begin
l_ranges_tab := apex_util.string_to_table (p_ranges, ',');
for i in 1..l_ranges_tab.count loop
l_values_tab := apex_util.string_to_table (l_ranges_tab(i), '-');
if l_values_tab.count = 1 then
if p_value = l_values_tab(1) then
l_retval := 1;
exit;
end if;
else
if p_value between l_values_tab(1) and l_values_tab(2) then
l_retval := 1;
exit;
end if;
end if;
end loop;
return l_retval;
end;
select color from colors where value_included(5, ranges);
如果该值包含在范围内,则返回1;如果不在范围内,则返回0,并且可以这样使用:
function value_included (p_value in number, p_ranges in varchar2)
return number
is
l_ranges_tab apex_application_global.vc_arr2;
l_values_tab apex_application_global.vc_arr2;
l_retval number := 0;
begin
l_ranges_tab := apex_util.string_to_table (p_ranges, ',');
for i in 1..l_ranges_tab.count loop
l_values_tab := apex_util.string_to_table (l_ranges_tab(i), '-');
if l_values_tab.count = 1 then
if p_value = l_values_tab(1) then
l_retval := 1;
exit;
end if;
else
if p_value between l_values_tab(1) and l_values_tab(2) then
l_retval := 1;
exit;
end if;
end if;
end loop;
return l_retval;
end;
select color from colors where value_included(5, ranges);
可以编写类似的函数来处理重叠范围:
function range_overlap (p_from in number, p_to in number, p_ranges in varchar2)
return number
is
l_ranges_tab apex_application_global.vc_arr2;
l_values_tab apex_application_global.vc_arr2;
l_retval number := 0;
begin
l_ranges_tab := apex_util.string_to_table (p_ranges, ',');
for i in 1..l_ranges_tab.count loop
l_values_tab := apex_util.string_to_table (l_ranges_tab(i), '-');
if l_values_tab.count = 1 then
if l_values_tab(1) between p_from and p_to then
l_retval := 1;
exit;
end if;
else
if p_to >= l_values_tab(1) and p_from <= l_values_tab(2) then
l_retval := 1;
exit;
end if;
end if;
end loop;
return l_retval;
end;
函数范围重叠(p_从in-number,p_到in-number,p_范围在varchar2)
返回号码
是
l_ranges_tab apex_application_global.vc_arr2;
l_values_tab apex_application_global.vc_arr2;
检索号:=0;
开始
l_ranges_tab:=apex_util.string_to_table(p_ranges,,);
对于1..l\u范围中的i\u tab.count循环
l_values_tab:=apex_util.string_to_table(l_ranges_tab(i),'-');
如果l_值_tab.count=1,则
如果l_值在p_from和p_to之间,则
l_retval:=1;
出口
如果结束;
其他的
如果p_to>=l_values_tab(1)和p_from可以创建如下PL/SQL函数:
function value_included (p_value in number, p_ranges in varchar2)
return number
is
l_ranges_tab apex_application_global.vc_arr2;
l_values_tab apex_application_global.vc_arr2;
l_retval number := 0;
begin
l_ranges_tab := apex_util.string_to_table (p_ranges, ',');
for i in 1..l_ranges_tab.count loop
l_values_tab := apex_util.string_to_table (l_ranges_tab(i), '-');
if l_values_tab.count = 1 then
if p_value = l_values_tab(1) then
l_retval := 1;
exit;
end if;
else
if p_value between l_values_tab(1) and l_values_tab(2) then
l_retval := 1;
exit;
end if;
end if;
end loop;
return l_retval;
end;
select color from colors where value_included(5, ranges);
如果该值包含在范围内,则返回1;如果不在范围内,则返回0,并且可以这样使用:
function value_included (p_value in number, p_ranges in varchar2)
return number
is
l_ranges_tab apex_application_global.vc_arr2;
l_values_tab apex_application_global.vc_arr2;
l_retval number := 0;
begin
l_ranges_tab := apex_util.string_to_table (p_ranges, ',');
for i in 1..l_ranges_tab.count loop
l_values_tab := apex_util.string_to_table (l_ranges_tab(i), '-');
if l_values_tab.count = 1 then
if p_value = l_values_tab(1) then
l_retval := 1;
exit;
end if;
else
if p_value between l_values_tab(1) and l_values_tab(2) then
l_retval := 1;
exit;
end if;
end if;
end loop;
return l_retval;
end;
select color from colors where value_included(5, ranges);
可以编写类似的函数来处理重叠范围:
function range_overlap (p_from in number, p_to in number, p_ranges in varchar2)
return number
is
l_ranges_tab apex_application_global.vc_arr2;
l_values_tab apex_application_global.vc_arr2;
l_retval number := 0;
begin
l_ranges_tab := apex_util.string_to_table (p_ranges, ',');
for i in 1..l_ranges_tab.count loop
l_values_tab := apex_util.string_to_table (l_ranges_tab(i), '-');
if l_values_tab.count = 1 then
if l_values_tab(1) between p_from and p_to then
l_retval := 1;
exit;
end if;
else
if p_to >= l_values_tab(1) and p_from <= l_values_tab(2) then
l_retval := 1;
exit;
end if;
end if;
end loop;
return l_retval;
end;
函数范围重叠(p_从in-number,p_到in-number,p_范围在varchar2)
返回号码
是
l_ranges_tab apex_application_global.vc_arr2;
l_values_tab apex_application_global.vc_arr2;
检索号:=0;
开始
l_ranges_tab:=apex_util.string_to_table(p_ranges,,);
对于1..l\u范围中的i\u tab.count循环
l_values_tab:=apex_util.string_to_table(l_ranges_tab(i),'-');
如果l_值_tab.count=1,则
如果l_值在p_from和p_to之间,则
l_retval:=1;
出口
如果结束;
其他的
如果p_to>=l_值_选项卡(1)和p_from,您可以通过此查询获得您的范围:
SQL> select id
2 , color
3 , to_number(case when ranges like '%-%' then regexp_substr(ranges,'[^-]+',1,1) else ranges end) low_value
4 , to_number(case when ranges like '%-%' then regexp_substr(ranges,'[^-]+',1,2) else ranges end) high_value
5 from colors
6 model
7 return updated rows
8 partition by (id,color)
9 dimension by (0 i)
10 measures (ranges,nvl(length(regexp_replace(ranges,'[^,]')),0) + nvl2(ranges,1,0) as number_of_parts)
11 ( ranges[for i from 1 to number_of_parts[0] increment 1]
12 = regexp_substr(ranges[0],'[^,]+',1,cv(i))
13 )
14 /
ID COLOR LOW_VALUE HIGH_VALUE
-------------------- ----- -------------------- --------------------
2 Red 4 4
1 Blue 1 9
1 Blue 23.5 25.1
1 Blue 27.11 27.11
1 Blue 99.14 99.14
4 Black 18 22
4 Black 101 101
3 Green 4.44 5.3
8 rows selected.
问候,
Rob.您可以通过此查询获取您的范围:
SQL> select id
2 , color
3 , to_number(case when ranges like '%-%' then regexp_substr(ranges,'[^-]+',1,1) else ranges end) low_value
4 , to_number(case when ranges like '%-%' then regexp_substr(ranges,'[^-]+',1,2) else ranges end) high_value
5 from colors
6 model
7 return updated rows
8 partition by (id,color)
9 dimension by (0 i)
10 measures (ranges,nvl(length(regexp_replace(ranges,'[^,]')),0) + nvl2(ranges,1,0) as number_of_parts)
11 ( ranges[for i from 1 to number_of_parts[0] increment 1]
12 = regexp_substr(ranges[0],'[^,]+',1,cv(i))
13 )
14 /
ID COLOR LOW_VALUE HIGH_VALUE
-------------------- ----- -------------------- --------------------
2 Red 4 4
1 Blue 1 9
1 Blue 23.5 25.1
1 Blue 27.11 27.11
1 Blue 99.14 99.14
4 Black 18 22
4 Black 101 101
3 Green 4.44 5.3
8 rows selected.
问候,
Rob。我通过使用SQL嵌套表和管道函数提供第三个选项来完成这项工作
首先创建SQL类型和相关嵌套表:
create or replace type range_type as object (range_from number, range_to number);
create or replace type range_table as table of range_type;
然后创建一个可以解码范围列的流水线函数。该函数可能很容易重写,以利用上面使用的apex_util.string_to_table函数
create or replace function range_to_nested_table(i_ranges in varchar2)
return range_table pipelined is
thisRange varchar2(4000);
loop_counter number := 1;
output_row range_type;
begin
loop
thisRange := rtrim(regexp_substr(i_ranges, '[^,]*,?', 1, loop_counter), ',');
exit when thisRange is null;
loop_counter := loop_counter + 1;
if thisRange like '%-%' then
output_row := range_type(to_number(regexp_substr(thisRange, '[^-]*', 1, 1)),
to_number(regexp_substr(thisRange, '[^-]*(-|$)', 1, 2)));
else
output_row := range_type(to_number(thisRange), to_number(thisRange));
end if;
pipe row(output_row);
end loop;
RETURN;
end;
然后运行以下查询以检索数据:
with my_sample_data as (
select 1 as id, 'Blue' as color, '1-9,23.5-25.1,27.11,99.14' as range from dual union all
select 2 as id, 'Red' as color, '4' as range from dual union all
select 3 as id, 'Green' as color, '4.44-5.3' as range from dual union all
select 4 as id, 'Black' as color, '18-22,101' as range from dual
)
select id, color, range, b.*
from my_sample_data a, table(range_to_nested_table(a.range)) b
where 5 between b.range_from and b.range_to
我通过使用SQL嵌套表和管道函数提供第三个选项来实现这一点
首先创建SQL类型和相关嵌套表:
create or replace type range_type as object (range_from number, range_to number);
create or replace type range_table as table of range_type;
然后创建一个可以解码范围列的流水线函数。该函数可能很容易重写,以利用上面使用的apex_util.string_to_table函数
create or replace function range_to_nested_table(i_ranges in varchar2)
return range_table pipelined is
thisRange varchar2(4000);
loop_counter number := 1;
output_row range_type;
begin
loop
thisRange := rtrim(regexp_substr(i_ranges, '[^,]*,?', 1, loop_counter), ',');
exit when thisRange is null;
loop_counter := loop_counter + 1;
if thisRange like '%-%' then
output_row := range_type(to_number(regexp_substr(thisRange, '[^-]*', 1, 1)),
to_number(regexp_substr(thisRange, '[^-]*(-|$)', 1, 2)));
else
output_row := range_type(to_number(thisRange), to_number(thisRange));
end if;
pipe row(output_row);
end loop;
RETURN;
end;
然后运行以下查询以检索数据:
with my_sample_data as (
select 1 as id, 'Blue' as color, '1-9,23.5-25.1,27.11,99.14' as range from dual union all
select 2 as id, 'Red' as color, '4' as range from dual union all
select 3 as id, 'Green' as color, '4.44-5.3' as range from dual union all
select 4 as id, 'Black' as color, '18-22,101' as range from dual
)
select id, color, range, b.*
from my_sample_data a, table(range_to_nested_table(a.range)) b
where 5 between b.range_from and b.range_to