我正在编写一份报告,并从创建此摘要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:1月份的20分钟被认为是新工作,但是在2月份,这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 :(得分:1)
我不知道你为什么要在这里使用CROSS APPLY。我的策略是在单独的子查询中计算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;