2016-10-28 3 views
3

Я работаю над отчетом и начал с созданием этого сводной SQL заявления:Сплит итоговых данных по месяцам

SELECT o.description, 
     sum(t.total_minutes) total_minutes, 
     sum(n.total_new_minutes) total_new_minutes 
FROM job i 
      INNER JOIN 
     operation o ON i.operation_id = o.operation_id 
      CROSS APPLY 
     (
      SELECT SUM(minutes) total_minutes 
      FROM job_time t 
      WHERE i.job_id = t.job_id 
      AND  record_date between '1 JAN 2016' and '29 FEB 2016' 
     ) t 
      CROSS APPLY 
     (
      SELECT SUM(minutes) total_new_minutes 
      FROM job_time t 
      WHERE i.job_id = t.job_id 
      AND  record_date between '1 JAN 2016' and '29 FEB 2016' 
      AND  NOT EXISTS (SELECT 1 FROM job_time v WHERE v.record_date < DATEADD(month, DATEDIFF(month, 0, t.record_date), 0) AND v.job_id = t.job_id) 
     ) n 
GROUP BY o.description 

Структура таблицы:

Table: job 
job_id  | operation_id 
-------------|------------- 
1   | 1 
2   | 1 
3   | 2 
4   | 2 

Table: operation 
operation_id | description 
-------------|------------- 
1   | 'OP1' 
2   | 'OP2' 

Table: job_time 
job_id  | record_date | minutes 
-------------|--------------|--------- 
1   | '1 JAN 2016' | 20 
1   | '2 JAN 2016' | 20 
2   | '1 JAN 2016' | 20 
2   | '1 FEB 2016' | 20 
3   | '1 FEB 2016' | 20 
3   | '2 FEB 2016' | 20 
4   | '2 JAN 2016' | 20 
4   | '2 FEB 2016' | 20 

Результат моего краткого запроса:

description | total_minutes | total_new_minutes 
-------------|---------------|----------------- 
'OP1'  | 80   | 60 
'OP2'  | 80   | 60 

SQL скрипку: http://sqlfiddle.com/#!6/f28f7e/1/0

Идея состоит в том, чтобы иметь общее количество часов, затрачиваемых на работу в заданном диапазоне данных, а также получать общее количество часов, затрачиваемых на новые рабочие места каждый месяц.

Пример работы 2: 20 минут в январе считаются новыми работами, однако в феврале эти 20 минут потрачены на старую работу.

Моя проблема заключается в том, что я хочу разделить этот результат в месяц на заданном диапазоне, как показано ниже:

description | month  |total_minutes | total_new_minutes 
-------------|-----------|--------------|----------------- 
'OP1'  | '01/2016' | 60   | 60 
'OP1'  | '02/2016' | 20   | 0 
'OP2'  | '01/2016' | 20   | 20 
'OP2'  | '02/2016' | 60   | 40 

Изменение запроса, чтобы отразить этот результат:

SELECT o.description, 
     sum(t.total_minutes) total_minutes, 
     sum(n.total_new_minutes) total_new_minutes, 
     t.record_month  
FROM job i 
      INNER JOIN 
     operation o ON i.operation_id = o.operation_id 
      CROSS APPLY 
     (
      SELECT SUM(minutes) total_minutes, right(convert(varchar(10),t.record_date,103),7) record_month 
      FROM job_time t 
      WHERE i.job_id = t.job_id 
      AND  record_date between '1 JAN 2016' and '29 FEB 2016' 
      GROUP BY right(convert(varchar(10),t.record_date,103),7) 
     ) t 
      CROSS APPLY 
     (
      SELECT SUM(minutes) total_new_minutes, right(convert(varchar(10),t.record_date,103),7) record_month 
      FROM job_time t 
      WHERE i.job_id = t.job_id 
      AND  record_date between '1 JAN 2016' and '29 FEB 2016' 
      AND  NOT EXISTS (SELECT 1 FROM job_time v WHERE v.record_date < DATEADD(month, DATEDIFF(month, 0, t.record_date), 0) AND v.job_id = t.job_id) 
      GROUP BY right(convert(varchar(10),t.record_date,103),7) 
     ) n  
GROUP BY o.description, t.record_month 

SQL скрипку: http://sqlfiddle.com/#!6/f28f7e/3/0

Проблема в том, что я не понимаю, как делается соединение между t и n, а в моем dev db с фактическими данными одна операция сообщает больше минут в total_new_minutes, чем total_minutes, чего никогда не может быть, не имеет значения, насколько плохи данные.

Любая идея, что я делаю неправильно здесь, или если я должен полностью изменить запрос?

+0

Есть более чем один подзапрос с именем т в вашем примере. Если вы спросите о больших t и n, я не думаю, что между ними есть какое-либо соединение. Они косвенно связаны на основе базовой записи, с которой они связаны, от временного набора данных, образованного соединением между заданием таблиц и операцией. – DVT

ответ

1

Я не знаю, почему вы хотите использовать КРЕСТ ПРИМЕНЯТЬ здесь. Моя стратегия заключалась бы в том, чтобы вычислить total_minutes и total_new_minutes в отдельных подзапросах, а затем объединить их вместе. Окончательный запрос заключается в следующем:

WITH new_job AS (
    SELECT 
     jt1.JOB_ID 
     , EOMONTH(jt1.RECORD_DATE) AS mon 
     , SUM(jt1."MINUTES") AS "MINUTES" 
    FROM 
     #JOB_TIME jt1 
    WHERE 
     NOT EXISTS (
      SELECT 1 
      FROM 
       #JOB_TIME jt2 
      WHERE 
       jt2.JOB_ID = jt1.JOB_ID 
       AND EOMONTH(jt1.RECORD_DATE, -1) = EOMONTH(jt2.RECORD_DATE) 
     ) 
    GROUP BY 
     jt1.JOB_ID 
     , EOMONTH(jt1.RECORD_DATE) 
), new_total AS (
    SELECT 
     j.OPERATION_ID 
     , nj.mon 
     , SUM(nj."MINUTES") AS total_new_minutes 
    FROM 
     new_job nj 
     JOIN #JOB j ON j.JOB_ID = nj.JOB_ID 
    GROUP BY 
     j.OPERATION_ID 
     , nj.mon 
), total AS (
    SELECT 
     j.OPERATION_ID 
     , EOMONTH(jt.RECORD_DATE) AS mon 
     , SUM(jt."MINUTES") AS total_minutes 
    FROM 
     #JOB_TIME jt 
     JOIN #JOB j ON jt.JOB_ID = j.JOB_ID 
    GROUP BY 
     j.OPERATION_ID 
     , EOMONTH(jt.RECORD_DATE) 
) 
SELECT 
    o."DESCRIPTION" 
    , COALESCE(t.mon, nt.mon) 
    , COALESCE(t.total_minutes,0) AS total_minutes 
    , COALESCE(nt.total_new_minutes,0) AS total_new_minutes 
FROM 
    #OPERATION o 
    LEFT JOIN total t ON o.OPERATION_ID = t.OPERATION_ID 
    LEFT JOIN new_total nt ON o.OPERATION_ID = nt.OPERATION_ID AND nt.mon=t.mon; 

Все запроса необходимо проверить было бы это:

CREATE TABLE #JOB (
    JOB_ID INT 
    , OPERATION_ID INT 
); 

CREATE TABLE #OPERATION (
    OPERATION_ID INT 
    , "DESCRIPTION" NCHAR(5) 
); 

CREATE TABLE #JOB_TIME (
    JOB_ID INT 
    , RECORD_DATE DATE 
    , "MINUTES" INT 
) 

INSERT INTO #JOB (JOB_ID, OPERATION_ID) 
VALUES 
(1,1) 
,(2,1) 
,(3,2) 
,(4,2); 

INSERT INTO #OPERATION (OPERATION_ID, "DESCRIPTION") 
VALUES 
(1, 'OP1') 
, (2, 'OP2'); 

INSERT INTO #JOB_TIME (JOB_ID, RECORD_DATE, "MINUTES") 
VALUES 
(1, '20160101', 20) 
, (1, '20160102', 20) 
, (2, '20160101', 20) 
, (2, '20160201', 20) 
, (3, '20160201', 20) 
, (3, '20160202', 20) 
, (4, '20160102', 20) 
, (4, '20160202', 20); 

WITH new_job AS (
    SELECT 
     jt1.JOB_ID 
     , EOMONTH(jt1.RECORD_DATE) AS mon 
     , SUM(jt1."MINUTES") AS "MINUTES" 
    FROM 
     #JOB_TIME jt1 
    WHERE 
     NOT EXISTS (
      SELECT 1 
      FROM 
       #JOB_TIME jt2 
      WHERE 
       jt2.JOB_ID = jt1.JOB_ID 
       AND EOMONTH(jt1.RECORD_DATE, -1) = EOMONTH(jt2.RECORD_DATE) 
     ) 
    GROUP BY 
     jt1.JOB_ID 
     , EOMONTH(jt1.RECORD_DATE) 
), new_total AS (
    SELECT 
     j.OPERATION_ID 
     , nj.mon 
     , SUM(nj."MINUTES") AS total_new_minutes 
    FROM 
     new_job nj 
     JOIN #JOB j ON j.JOB_ID = nj.JOB_ID 
    GROUP BY 
     j.OPERATION_ID 
     , nj.mon 
), total AS (
    SELECT 
     j.OPERATION_ID 
     , EOMONTH(jt.RECORD_DATE) AS mon 
     , SUM(jt."MINUTES") AS total_minutes 
    FROM 
     #JOB_TIME jt 
     JOIN #JOB j ON jt.JOB_ID = j.JOB_ID 
    GROUP BY 
     j.OPERATION_ID 
     , EOMONTH(jt.RECORD_DATE) 
) 
SELECT 
    o."DESCRIPTION" 
    , COALESCE(t.mon, nt.mon) 
    , COALESCE(t.total_minutes,0) AS total_minutes 
    , COALESCE(nt.total_new_minutes,0) AS total_new_minutes 
FROM 
    #OPERATION o 
    LEFT JOIN total t ON o.OPERATION_ID = t.OPERATION_ID 
    LEFT JOIN new_total nt ON o.OPERATION_ID = nt.OPERATION_ID AND nt.mon=t.mon; 


DROP TABLE #JOB; 
DROP TABLE #JOB_TIME; 
DROP TABLE #OPERATION; 
+1

Большое спасибо. Я не знаю, почему я об этом не думал. Ницца и просто :) –