SQL生成列之间指定范围之间的数字

时间:2017-07-14 13:51:03

标签: sql sql-server oracle

我有一张看起来像这样的表

IDNUM | Name  | LowRange | HighRange | Notes
123   | TESTS | 100      | 201       | Hello
124   | TEST2 | 200      | 210       | 
125   | TESTS | 100      | 201       | Hello

我想知道是否有一个会返回以下结果的查询

IDNUM | Name  | Number | Notes
123   | TESTS | 100      | Hello
123   | TESTS | 101      | Hello
123   | TESTS | 102      | Hello
123   | TESTS | 103      | Hello
......til 201
124   | TEST2 | 200      | 
124   | TEST2 | 201      |
124   | TEST2 | 202      |  
......til 210 

我正在寻找在SQL Server 2016和Oracle 11g中执行此操作的方法,但任何一方的帮助都将受到赞赏

5 个答案:

答案 0 :(得分:2)

最简单的方法是数字表。以下内容适用于Oracle和SQL Server - 假设“数字”的基表足够大:

with numbers as (
      select row_number() over (order by idnum) - 1 as n
      from t
     )
select idnum, name, lowrange + n.n as number, notes
from t join
     numbers n
     on lowrange + n.n <= highrange;

如果上述内容无法生成足够的数字,您可以在CTE中使用cross join来获取更多数据。

每个数据库都有其他生成数字的方法,但这适用于两个数据库。

答案 1 :(得分:2)

Oracle分层查询

SELECT t.IDNUM,
       t.Name,
       n.COLUMN_VALUE AS "Number",
       t.Notes
FROM   your_table t
       CROSS JOIN
       TABLE(
         CAST(
           MULTISET(
             SELECT t.LowRange + LEVEL - 1
             FROM   DUAL
             CONNECT BY t.LowRange + LEVEL - 1 < t.HighRange
           ) AS SYS.ODCINUMBERLIST
         )
       ) n;

递归子查询保理条款

WITH numbers ( IDNUM, Name, LowRange, HighRange, Notes ) AS (
  SELECT IDNUM, Name, LowRange, HighRange, Notes
  FROM   your_table
UNION ALL
  SELECT IDNUM, Name, LowRange + 1, HighRange, Notes
  FROM   numbers
  WHERE  LowRange < HighRange
)
SELECT IDNUM,
       Name,
       LowRange AS "Number",
       Notes
FROM   numbers;

答案 2 :(得分:0)

一种方法是使用this accepted answer中的代码并使用以下内容:

DECLARE @startnum INT
DECLARE @endnum INT

SELECT  @startnum = MIN(LowRange) ,
        @endnum = MAX(HighRange)
FROM    IDsAndRanges

;
WITH gen AS (
    SELECT @startnum AS num
    UNION ALL
    SELECT num+1 FROM gen WHERE num+1<=@endnum
)

SELECT  * 
FROM    [IDsAndRanges] I
LEFT
JOIN    gen G
        ON G.num BETWEEN I.LowRange AND I.HighRange
option (maxrecursion 10000)

这是使用以下测试工具编写的:

CREATE TABLE [IDsAndRanges]
(
    IDNUM       [INT],
    Name        NVARCHAR(100),
    LowRange    INT,
    HighRange   INT,
    Notes       NVARCHAR(100)
)

INSERT
INTO    [IDsAndRanges]
        (
            IDNUM, Name, LowRange, HighRange, Notes
        )
VALUES  (123, 'TESTS', 100, 201, 'Hello'),
        (124, 'TEST2', 200, 210, ''),
        (125, 'TESTS', 100, 201, 'Hello')

答案 3 :(得分:0)

您可以将此作为递归的with子句(也称为递归CTE),如下所示:

WITH main_data (idnum, NAME, NUM, highrange, notes) AS (SELECT idnum,
                                                               NAME,
                                                               lowrange NUM,
                                                               highrange,
                                                               notes
                                                        FROM   sample_data
                                                        UNION ALL
                                                        SELECT idnum,
                                                               NAME,
                                                               NUM + 1 NUM,
                                                               highrange,
                                                               notes
                                                        FROM   main_data
                                                       WHERE  NUM < highrange)
SELECT idnum,
       NAME,
       NUM,
       notes
FROM   main_data
ORDER BY idnum, NUM;


     IDNUM NAME         NUM NOTES
---------- ----- ---------- -----
       123 TESTS        100 Hello
       123 TESTS        101 Hello
       123 TESTS        102 Hello
       123 TESTS        103 Hello
       124 TEST2        200 
       124 TEST2        201 
       124 TEST2        202 
       124 TEST2        203 
       124 TEST2        204 
       125 TESTS        150 Hello
       125 TESTS        151 Hello
       125 TESTS        152 Hello
       125 TESTS        153 Hello
       125 TESTS        154 Hello
       125 TESTS        155 Hello
       125 TESTS        156 Hello
       125 TESTS        157 Hello
       125 TESTS        158 Hello
       125 TESTS        159 Hello
       125 TESTS        160 Hello

以上查询在Oracle中工作(我相信它也应该在SQL Server中工作)基于以下示例数据:

SELECT 123 idnum, 'TESTS' NAME, 100 lowrange, 103 highrange, 'Hello' notes FROM dual UNION ALL
SELECT 124 idnum, 'TEST2' NAME, 200 lowrange, 204 highrange, NULL notes FROM dual UNION ALL
SELECT 125 idnum, 'TESTS' NAME, 150 lowrange, 160 highrange, 'Hello' notes FROM dual

答案 4 :(得分:0)

也许不是一个好方法,但它对我有用;

CREATE TYPE shin.tab_rows AS OBJECT (
  idnum           NUMBER,
  description  VARCHAR2(50),
  num_ber  NUMBER,
  notes  VARCHAR2(50)
);
/

CREATE TYPE shin.test_tab IS TABLE OF shin.tab_rows;
/


CREATE OR REPLACE FUNCTION shin.get_numbers
 RETURN shin.test_tab PIPELINED AS
BEGIN
  for records in (select IDNUM,lowrange,highrange,name,notes from shin.test_stack) LOOP

  FOR num_ber IN records.lowrange..records.highrange LOOP
    PIPE ROW(shin.tab_rows(records.IDNUM, records.name,num_ber,records.notes));  

    END LOOP; 
  END LOOP;

  RETURN;
END;
/

select * from table(shin.get_numbers)