1

在 oracle 数据库表中,我需要找到给定批号的结果。保存批号的字段是一个包含类似“1-3,5,10-15,20”的字符串(此字符串中的数字已排序)

有没有办法做到这一点?

在上面的示例中,应为以下批号找到结果:

1,2,3,5,10,11,12,13,14,15,20

在应用程序中没有办法做到这一点,所以它必须在数据库中完成。

类似:“SELECT * FROM products WHERE lot = 2”

4

4 回答 4

7

可以通过使用REGEXP_SUBSTR函数和分层查询在 SQL 中完成这一切:

with list_of_ids as (
select regexp_substr(a, '[[:digit:]]+',1, 1) as lot1
     , nvl( regexp_substr(a, '(-)([[:digit:]]+)',1, 1, 'i', '2')
          , regexp_substr(a, '[[:digit:]]+',1, 1)) as lot2
  from (select regexp_substr('1-3,5,10-15,20' , '[^,]+', 1, level) as a
          from dual
       connect by regexp_substr('1-3,5,10-15,20' , '[^,]+', 1, level) is not null
               )
       )
select a.*
  from products a
  join list_of_ids b
    on a.lot between b.lot1 and b.lot2

但是,我必须强调正确规范化您的数据库是可行的方法。此解决方案可能无法很好地扩展,并且会做大量不必要的工作。

它是这样工作的:

首先用逗号分割你的数据:

SQL>  select regexp_substr('1-3,5,10-15,20', '[^,]+', 1, level) as a
  2     from dual
  3  connect by regexp_substr('1-3,5,10-15,20', '[^,]+', 1, level) is not null
  4          ;

A
--------------
1-3
5
10-15
20

接下来,在连字符上拆分它以提供在 BETWEEN 中使用的最小和最大手数,然后最终将其加入表中。NVL 的存在是为了确保始终存在最大值。

SQL> select regexp_substr(a, '[[:digit:]]+',1, 1) as lot1
  2       , nvl( regexp_substr(a, '(-)([[:digit:]]+)',1, 1, 'i', '2')
  3             , regexp_substr(a, '[[:digit:]]+',1, 1)) as lot2
  4    from (select regexp_substr('1-3,5,10-15,20' , '[^,]+', 1, level) as a
  5            from dual
  6         connect by regexp_substr('1-3,5,10-15,20' , '[^,]+', 1, level) is not null
  7                 )
  8         ;

LOT1           LOT2
-------------- --------------
1              3
5              5
10             15
20             20

SQL>

这是一个带有完整查询的有效SQL Fiddle 。

于 2013-01-22T14:03:06.967 回答
3

这是一个具有PIPELINED FUNCTION的解决方案:

create type array_number
    as table of number
/ 

create or replace function x_tbl(a varchar2) return array_number pipelined  as
token varchar2(10);
str varchar2(1000):=a;
k number;
should_exit boolean;
begin
should_exit := false;
loop
    if instr(str,',') > 0 then 
        token:=substr(str,1,instr(str,',')-1);
    else 
        token := str;
        should_exit:=true;
    end if;
    if instr(token,'-') > 0 then 
        k:=to_number(substr(token, 1, instr(token,'-')-1)) ;
        loop
            pipe row(k);
            k:=k+1;
            exit when k>to_number(substr(token, instr(token,'-')+1)) ;
        end loop;
    else pipe row(token);
    end if;
    --dbms_output.put_line(token);
    --dbms_output.put_line(instr(str,','));
    str:=substr(str, instr(str,',')+1);
    exit when should_exit;
end loop;
end;
/

询问:

select * from table(x_tbl('22-27,33,444-448'));

结果:

22
23
24
25
26
27
33
444
445
446
447
448

这样你就可以:

 select 1 from dual where 23 in (select * from  table(x_tbl('22-27,33,444-448')));
于 2013-01-22T14:21:10.567 回答
-1

这是 T-SQL,但移植起来应该不难。

DECLARE @ranges NVARCHAR(MAX);
DECLARE @number INT;

SET @ranges = N'1-3,5,10-15,20';
SET @number = 13;

DECLARE @found BIT;
DECLARE @commaIndex INT;
DECLARE @dashIndex INT;
DECLARE @range NVARCHAR(MAX);
DECLARE @rangeStart INT;
DECLARE @rangeEnd INT;

SET @found = 0;

SET @commaIndex = CHARINDEX(',', @ranges);

WHILE (@commaIndex > 0) BEGIN

  SET @range = SUBSTRING(@ranges, 1, @commaIndex - 1);

  SET @dashIndex = CHARINDEX('-', @range);

  IF (@dashIndex > 0) BEGIN

     SET @rangeStart = CAST(SUBSTRING(@range, 1, @dashIndex - 1) AS INT);
     SET @rangeEnd = CAST(SUBSTRING(@range, @dashIndex + 1, LEN(@range) - @dashIndex) AS INT);

  END ELSE BEGIN

     SET @rangeStart = CAST(@range AS INT);
     SET @rangeEnd = @rangeStart;

  END;

  IF ((@rangeStart <= @number) AND (@number <= @rangeEnd)) BEGIN

    SET @found = 1;

    BREAK;

  END;

  SET @ranges = SUBSTRING(@ranges, @commaIndex + 1, LEN(@ranges) - @commaIndex);
  SET @commaIndex = CHARINDEX(',', @ranges);

END;

IF (@found = 1) BEGIN

    PRINT N'Contained.';

END ELSE BEGIN

    PRINT N'Not contained.';

END;
于 2013-01-22T13:56:16.187 回答
-4

如果您必须只使用 SQL,这是最简单的解决方案。如果您使用 PL/SQL,还有更多解决方案。但这可能是最有效和最简单的。当然,不是硬编码的数字,而是您的字符串:

SELECT * FROM products WHERE lot IN (Replace('1-3,5,10-15,20', '-', ',') )
/

如果需要,有一种方法可以删除/替换字符串中的更多字符。上面的示例仅将 '-' 替换为 ',' ...

问题是如何得到这个:

"SELECT * FROM products WHERE lot = 2..."

不是这个:

1-3,
2,
4
...

清楚地陈述你的问题/例子。

于 2013-01-22T13:43:30.403 回答