我想在SQL表列中存储范围。例如4-10,[14-18),然后使用如下查询过滤行
select * from table_name where range_column contains 16;
我对oracle更感兴趣,但也想知道是否有其他数据库具有允许这样做的数据类型。
我知道这可以使用多行两列的range_begin
和range end
来完成,但是我想知道是否有一种数据类型可以使用单列来实现这一点。
发布于 2017-04-26 18:58:17
对于原生数据类型,没有一种简单的内置方法可以做到这一点。
这不会将两个单独的范围保留在您所显示的表单中,但您可以使用一个嵌套表来保存范围内的所有值:
create type range_type as table of number
/
create table table_name (id number, range_column range_type)
nested table range_column store as range_tab;
显然,对于较大的范围,存储要求将高于仅存储低值和高值。
然后,一个重要的问题变成了如何从原始范围数据填充它,以及如何维护它-因为原始范围的分界丢失了。对于一组连续的值,您无法判断该集合是从单个范围构建的,还是从多个连续甚至重叠的范围构建的。
你可以像这样设置你的范围,但是你怎么做取决于你如何首先得到范围的低/高值:
insert into table_name (id, range_column)
values (1, cast(multiset(
select level + 3 from dual connect by level <= 7
) as range_type));
insert into table_name (id, range_column)
values (2, cast(multiset(
select level + 25 from dual connect by level <= 5
) as range_type));
insert into table_name (id, range_column)
values (3, cast(multiset(
select level + 3 from dual connect by level <= 7
union select level + 13 from dual connect by level <= 5
) as range_type));
然后,您的查询将是:
select * from table_name where 16 member of range_column;
ID RANGE_COLUMN
---------- ------------------------------------------------------------
3 RANGE_TYPE(4, 5, 6, 7, 8, 9, 10, 14, 15, 16, 17, 18)
如果您想让区域单独可识别,您可以使用对象类型来保存低值和高值,然后使用包含这些值的嵌套表,尽管查询会变得更加复杂:
create type range_object as object(low number, high number)
/
create type range_type as table of range_object;
/
create table table_name (id number, range_column range_type)
nested table range_column store as range_tab;
insert into table_name (id, range_column)
values (1, range_type(range_object(4, 10)));
insert into table_name (id, range_column)
values (2, range_type(range_object(25, 5)));
insert into table_name (id, range_column)
values (3, range_type(range_object(4, 10), range_object(14, 18)));
select t.* from
table_name t
cross join table(t.range_column) r
where 16 between r.low and r.high;
ID RANGE_COLUMN(LOW, HIGH)
---------- -----------------------------------------------------
1 RANGE_TYPE(RANGE_OBJECT(4, 10), RANGE_OBJECT(14, 18))
与具有低/高值的单独的范围表相比,这两个表似乎都不太吸引人;存储范围的字符串表示并拥有一个函数来确定给定的数字是否在其中,尤其是验证范围的格式将是一件痛苦的事情。
发布于 2017-04-27 13:21:12
Oracle12.1引入了一种新的语法,使处理范围变得更容易。不幸的是,它只适用于日期,但它可以扭曲,以适用于数字。
通常,我不会推荐任何将数据存储为错误类型的解决方案。只有在迫切需要更方便的范围语法时才使用它。
使用dates和PERIOD FOR
子句创建表:
--drop table table_name;
create table table_name
(
id number,
range_begin date,
range_end date,
period for table_name_period(range_begin, range_end)
);
插入数据,并使用'J'
格式模型将其转换为日期。它使用儒略日期格式,从公元前4712年开始计算天数。日期仅适用于9999年,这意味着此解决方案仅适用于0到5373484之间的数字。
insert into table_name values (1, to_date(4, 'J'), to_date(10, 'J'));
insert into table_name values (2, to_date(14, 'J'), to_date(18, 'J'));
commit;
现在可以简化比较条件。
select id, to_number(to_char(range_begin, 'J')) begin, to_number(to_char(range_end, 'J')) end
from table_name
as of period for table_name_period to_date(16, 'J');
ID BEGIN END
-- ----- ---
2 14 18
上面的代码中有很多丑陋的转换。它们可以通过一些简单的函数来简化,以便进行日期到数字的转换。
--Date-to-number.
create or replace function d2n(p_date date) return number is
begin
return to_number(to_char(p_date, 'J'));
end;
/
--Number-to-date.
create or replace function n2d(p_number number) return date is
begin
return to_date(p_number, 'J');
end;
/
使用这些函数可以使代码看起来更好。
select id, d2n(range_begin) begin, d2n(range_end) end
from table_name
as of period for table_name_period n2d(16);
这仍然是一团糟,可能只适用于一些极端情况。希望Oracle的未来版本将扩展范围语法以处理数字。
https://stackoverflow.com/questions/43630481
复制相似问题