从用户计划中获取所有日期

时间:2018-05-03 01:38:45

标签: sql oracle

我有一个用户日程表,其中包含他的活动和每天所花费的时间。 看起来像这样:

cd_user | dt_log       | time_spent
123     | 01/01/2018   | 60
123     | 01/01/2018   | 35
123     | 02/01/2018   | 55
123     | 05/01/2018   | 45

我想得到每天花费的所有时间的总和,但我需要在没有任何日志的日子里返回0。

有没有办法用一个查询来做到这一点?

4 个答案:

答案 0 :(得分:1)

你可以用这个:

WITH table_name AS 
(
    SELECT 123 AS cd_user, TO_DATE('01/01/2018','dd/mm/yyyy') AS dt_log, 60 AS time_spent 
    FROM dual UNION ALL 
    SELECT 123, TO_DATE('01/01/2018','dd/mm/yyyy'), 35 FROM dual UNION ALL 
    SELECT 123, TO_DATE('02/01/2018','dd/mm/yyyy'), 55 FROM dual UNION ALL 
    SELECT 123, TO_DATE('05/01/2018','dd/mm/yyyy'), 45 FROM dual 
)
, date_range AS 
(
    SELECT TO_DATE('01/01/2018','dd/mm/yyyy') + ROWNUM - 1 AS date1
    FROM all_objects
    WHERE ROWNUM <= TO_DATE('11/01/2018','dd/mm/yyyy') - TO_DATE('01/01/2018','dd/mm/yyyy') + 1 
)
SELECT u.cd_user, 
    d.date1 AS dt_log,
    SUM(NVL(t.time_spent, 0)) AS total_time_spent
FROM date_range d
CROSS JOIN (SELECT DISTINCT cd_user FROM table_name) u
LEFT JOIN table_name t
ON d.date1 = TRUNC(t.dt_log) AND u.cd_user = t.cd_user
GROUP BY u.cd_user, d.date1
ORDER BY u.cd_user, d.date1;

如果dt_log没有时间参与其中,您可以省略TRUNC

在此示例中,01/01/2018是开始日期,11/01/2018是输出结果中的结束日期。

参考:Generating Dates between two date ranges_AskTOM

答案 1 :(得分:0)

您需要按原始表格编写日历表,然后编写LEFT JOIN日历表。

您可以使用 CTE recursive 来编写日历

  1. MIN(dt_log)来自日期,MAX(dt_log)为Todate
  2. 使用CTE制作
  3. 像这样。

    WITH CTE(from_date, i, datediff,cd_user) AS
    (
      SELECT MIN(dt_log) from_date, 1 AS i, trunc(MAX(dt_log) - MIN(dt_log)) as datediff,cd_user
      FROM t 
      GROUP BY cd_user
      UNION ALL
      SELECT from_date, i + 1, datediff,cd_user
      FROM CTE
      WHERE i <= datediff
    ), dates as (select i, from_date + i - 1 Dt,cd_user from CTE)
    SELECT d.CD_USER,d.DT,SUM(coalesce(t.TIME_SPENT,0)) cost
    FROM dates d
    LEFT JOIN T t on t.CD_USER = d.CD_USER
    AND d.Dt = t.dt_log 
    group by d.CD_USER,d.DT
    ORDER BY d.CD_USER,d.DT
    

    sqlfiddle:http://sqlfiddle.com/#!4/a461d/4

    您还可以使用connect by撰写日历

    WITH CTE as (
      SELECT cd_user,MIN(dt_log) FromDate,MAX(dt_log) ToDate
      FROM T
      GROUP BY cd_user
    ), calendar as 
    (
      select DISTINCT cd_user,(FromDate+level-1) dt
      from CTE 
      connect by level <=ToDate - FromDate + 1
    )
    select d.CD_USER,d.DT,SUM(coalesce(t.TIME_SPENT,0)) cost
    from calendar d
    LEFT JOIN T t on t.CD_USER = d.CD_USER
    AND d.dt = t.dt_log 
    group by d.CD_USER,d.DT
    ORDER BY d.CD_USER,d.DT
    

    sqlfiddle:http://sqlfiddle.com/#!4/a461d/9

答案 2 :(得分:0)

我不确定我是否理解这项任务,但我提出了一个简单的解决方案。在https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:14582643282111

中生成更好的日历
    --drop table test_tab
    create table test_tab(
    cd_user  number,
    dt_log   date,
    time_spent number
    )
    ;

    insert into test_tab values(123     , to_date('01/01/2018', 'DD/MM/YYYY')   , 60);
    insert into test_tab values(123     , to_date('01/01/2018', 'DD/MM/YYYY')   , 35);
    insert into test_tab values(123     , to_date('02/01/2018', 'DD/MM/YYYY')   , 55);
    insert into test_tab values(123     , to_date('05/01/2018', 'DD/MM/YYYY')   , 45);
insert into test_tab values(999     , to_date('05/01/2018', 'DD/MM/YYYY')   , 45);
    commit;

    with calendar as(
    SELECT  to_date('01/01/2018', 'DD/MM/YYYY')+level-1 as dt
     FROM dual connect by level < 10 --days 
    )
    SELECT cd_user, nvl(dt_log, dt), nvl(sum(time_spent),0) 
    FROM test_tab, calendar
    where dt_log(+) = dt
    group by cd_user, nvl(dt_log, dt)
    order by 2;

-- sample 2 with not null cd_user for emty entrence
with calendar as(
SELECT  to_date('01/01/2018', 'DD/MM/YYYY')+level-1 as dt
 FROM dual connect by level < 10 --days 
),users1 as(
SELECT distinct CD_USER FROM test_tab
), cal_users as(
select cd_user, dt 
from calendar, users1
)
select cu.CD_USER, cu.dt, nvl(sum(c.time_spent),0)  
from cal_users cu, test_tab c
where cu.cd_user = c.cd_user(+) 
  and cu.dt = c.dt_log(+)
group by cu.CD_USER, cu.dt
order by 1,2

答案 3 :(得分:-1)

编辑:如果这一天有两次......我建议您在表格中有一个唯一/主要ID。

你想用php显示吗?如果是......

我不知道单个查询,但是......

$curr_date = '';
$time_spent = 0;
// months
for($i = 1; $i <= 12; $i++) {
    // add a leading zero to months less than October
    if($i > 10) { $curr_date .= '0'; }
    $curr_date .= $i.'/';
    // number of days for each month
    if($i == 2) { $days = 28; }
    else if($i == 4 || $i == 6 || $i == 9 || $i == 11) { $days = 30; }
    else { $days = 31; }
    // days
    for($j = 1; $j <= $days; $j++) {
        // add a leading zero to days less than 10
        if($j > 10) { $curr_date .= '0'; }
        $curr_date .= $j.'/2018';
        // search for table if that date is there for the user
        // replace 123 with a variable if you have more than one user
        $SQL = "SELECT * FROM table_name WHERE cd_user = 123 AND dt_log = '$curr_date'";
        $result = mysqli_query($connect, $SQL);
        $num = mysqli_num_rows($result);
        // if that date is in the table
        if($num != 0) {
            while($field = mysqli_fetch_assoc($result)) {
                $time_spent += $field['time_spent'];
            }
            echo $curr_date.' time spent: '.$time_spent;
            // set time spent back to zero for next day
            $time_spent = 0;
        }
        // if that date is not in the table
        else { echo $curr_date.' time spent: '.$time_spent; }
    }
}

所以你去吧。对不起,这是一个带有某些代码的单一查询语句,但它可以做你想要的...我想(如果你试图用php显示它或者如果我正确理解你的问题)。