2

我在使用以下查询时遇到问题:

SELECT
    B.EMPLOYEE_NAME,
    A.START_DATE+(LEVEL-1) AS INDIVIDUAL_DAY,
    TO_CHAR(A.START_DATE,'MM/DD/YYYY') START_DATE,
    TO_CHAR(A.END_DATE,'MM/DD/YYYY') END_DATE
FROM table1 A
INNER JOIN table2 B ON A.EMPLOYEE_NAME = B.EMPLOYEE_NAME
CONNECT BY LEVEL-1 <= (A.END_DATE-A.START_DATE);

如果表 A 中只有一条记录,那么我得到的正是我想要的。但是,如果有多个记录,那么我会得到过多的结果。

我在找什么:

我有一个表,其中的行包含EMPLOYEE_NAMESTART_DATEEND_DATE字段。我想把这一行信息变成多行。例子:

当前的 -

NAME          START       END
DAVID SMITH   1-1-2001    1-6-2011
JOHN SMITH    2-7-2012    2-9-2012

期望 -

NAME          DATE
DAVID SMITH   1-1-2001
DAVID SMITH   1-2-2001
DAVID SMITH   1-3-2001
DAVID SMITH   1-4-2001
DAVID SMITH   1-5-2001
DAVID SMITH   1-6-2001
JOHN SMITH    2-7-2012
JOHN SMITH    2-8-2012
JOHN SMITH    2-9-2012

关于我如何做到这一点的任何想法?注意:我使用的是 Oracle 10 和 11。

4

2 回答 2

3

在 10g/11g 中,您可以为此使用模型子句。

SQL> with emps as (select rownum id, name, start_date,
  2                       end_date, trunc(end_date)-trunc(start_date) date_range
  3                  from table1)
  4  select name, the_date
  5    from emps
  6  model partition by(id as key)
  7        dimension by(0 as f)
  8        measures(name, start_date, cast(null as date) the_date, date_range)
  9        rules (the_date [for f from 0 to date_range[0] increment 1]  = start_date[0] + cv(f),
 10               name[any] = name[0]);

NAME        THE_DATE
----------- ----------
DAVID SMITH 01-01-2001
DAVID SMITH 01-02-2001
DAVID SMITH 01-03-2001
DAVID SMITH 01-04-2001
DAVID SMITH 01-05-2001
DAVID SMITH 01-06-2001
JOHN SMITH  02-07-2012
JOHN SMITH  02-08-2012
JOHN SMITH  02-09-2012

9 rows selected.

即您的基本查询:

select rownum id, name, start_date,
       end_date, trunc(end_date)-trunc(start_date) date_range
  from table1

只定义日期+范围(我使用rownum id,但如果你有PK,你可以使用它。

分区将我们的计算拆分为每个 ID(唯一行):

6  model partition by(id as key)

措施:

8        measures(name, start_date, cast(null as date) the_date, date_range)

定义我们将输出/计算的属性。在这种情况下,我们使用名称、start_date 以及要生成的行范围。另外我定义了一个列the_date来保存计算的日期(即我们想要计算 start_date + n 其中 n 是从 0 到范围。

规则定义了我们将如何填充我们的列:

9        rules (the_date [for f from 0 to date_range[0] increment 1]  = start_date[0] + cv(f),
10               name[any] = name[0]);

所以与 

the_date [for f from 0 to date_range[0] increment 1]

我们是说我们将生成 date_range 包含的行数+1(即总共 6 个日期)。的值f可以通过cv(当前值)函数引用。

所以在大卫的第 1 行,我们有the_date [0] = start_date+0,随后在第 2 行,我们有the_date [1] = start_date+1. 一直到 start_date+5(即end_date

ps for connect by 你需要做这样的事情:

select 
    A.EMPLOYEE_NAME,
    A.START_DATE+(b.r-1) AS INDIVIDUAL_DAY,
    TO_CHAR(A.START_DATE,'MM/DD/YYYY') START_DATE,
    TO_CHAR(A.END_DATE,'MM/DD/YYYY') END_DATE
FROM table1 A
     cross join (select rownum r
                   from (select max(end_date-start_date) d from table1)
                  connect by level-1 <= d) b
 where A.START_DATE+(b.r-1) <= A.END_DATE
 order by 1, 2;

即隔离连接到子查询,然后过滤掉 individual_day > end_date 的行。

但我不会推荐这种方法。与模型方法相比,它的性能会更差(特别是如果范围变大)。

于 2013-01-22T23:12:23.043 回答
3

不求分数。找到你的帖子,无聊地做这个......

我的测试表:

EMPNO    ENAME    START_DATE    END_DATE
------------------------------------------
7369     SMITH    6/1/2011      6/7/2011
7499     ALLEN    7/1/2011      7/3/2011

SELECT ename, ind_start_date
  FROM
  (
   SELECT distinct ename
        , start_date
        , to_char(start_date + (LEVEL-1), 'MM/DD/YYYY') ind_start_date
        , to_char(end_date, 'MM/DD/YYYY') end_date
    FROM emp_test
   WHERE ename IN ('SMITH', 'ALLEN')
  CONNECT BY LEVEL <= (end_date-start_date)+1
  ORDER BY ename
 )
 /

ENAME    IND_START_DATE
--------------------------
ALLEN    07/01/2011
ALLEN    07/02/2011
ALLEN    07/03/2011
SMITH    06/01/2011
SMITH    06/02/2011
SMITH    06/03/2011
SMITH    06/04/2011
SMITH    06/05/2011
SMITH    06/06/2011
SMITH    06/07/2011

把事情简单化...

于 2013-03-14T17:33:16.840 回答