嵌套 select 并加入

Nested select and joins

我使用下面的代码从其他表中计算出所需的信息。我使用联接来显示名称而不是 ID,并从其他表中获取所需的总和。我使用 COALESCE 将 null 转换为零。

如果我需要对已经合并的值求和,我不得不再次使用它,上面的代码很难理解,而且越来越难,因为我需要添加更多信息,这只是主要项目的一小部分所以使用它真的很难,而且会有很多错误和错误。

有必要这么复杂吗?还是我做错了?如果它必须像这样复杂,是否有任何替代方法可以用更简单的方法和代码获得相同的结果?另一个 RDBMS 还是其他?

SELECT P.PROJ_ID, P.PROJ_STATUS, P.TYPE_ID, PT.TYPE_NAME, P.CLASS_ID, CLA.CLASS_NAME, P.PROJ_NO, P.PROJ_YEAR, P.PROJ_NAME, P.OLD_PROJ_NAME, 
P.AGENCY_ID, A.AGENCY_NAME, P.CONTRACT_NO, P.CONTRACT_DATE, P.MINISTRY_ID, M.MINISTRY_NAME, 
P.DIRECTORATE_ID, DIR.DIRECTORATE_NAME, 
P.COST, P.ESTIMATED_COST, COALESCE(CO.ADDED_COSTS, 0) AS ADDED_COSTS, (COALESCE(P.COST, 0) + COALESCE(CO.ADDED_COSTS, 0)) AS TOTAL_COST, 
P.ALLOCATION, 
COALESCE(EPY.PAST_YEARS, 0) AS PAST_YEARS, 
COALESCE(EF.PAST_MONTHS, 0) AS PAST_MONTHS, 
COALESCE(ECM.CURRENT_MONTH, 0) AS CURRENT_MONTH,
COALESCE(ECY.CURRENT_YEAR, 0) AS CURRENT_YEAR,
COALESCE(E.TOTAL_EXPENSES, 0) AS TOTAL_EXPENSES,

COALESCE(CASH_EPY.CASH_PAST_YEARS, 0) AS CASH_PAST_YEARS, 
COALESCE(CASH_EF.CASH_PAST_MONTHS, 0) AS CASH_PAST_MONTHS, 
COALESCE(CASH_ECM.CASH_CURRENT_MONTH, 0) AS CASH_CURRENT_MONTH,
COALESCE(CASH_ECY.CASH_CURRENT_YEAR, 0) AS CASH_CURRENT_YEAR,
COALESCE(CASH_E.CASH_TOTAL_EXPENSES, 0) AS CASH_TOTAL_EXPENSES,

COALESCE(TOTAL_E.TOTAL_EXPENSES_CASH, 0) AS TOTAL_EXPENSES_CASH,

((COALESCE(P.COST, 0) + COALESCE(CO.ADDED_COSTS, 0)) - COALESCE(E.TOTAL_EXPENSES, 0)) AS REMAINING,
P.DURATION, COALESCE(DU.ADDED_DURATIONS, 0) AS ADDED_DURATIONS,
(COALESCE(P.DURATION, 0) + COALESCE(DU.ADDED_DURATIONS, 0)) AS TOTAL_DURATION, P.START_DATE, P.FINISH_DATE, 
P.GOVERNORATE_ID, G.GOVERNORATE_NAME, P.PROVINCE_ID, PR.PROVINCE_NAME, P.DISTRICT_ID, D.DISTRICT_NAME, 
P.TOWN_ID, T.TOWN_NAME, 

COALESCE( (E.TOTAL_EXPENSES / (COALESCE(P.COST, 0) + COALESCE(CO.ADDED_COSTS, 0)))/100, 0) AS FINANCIAL_ACHIEVEMENT,

P.MATERIAL_ACHIEVEMENT, P.NOTES
FROM PROJECTS P

INNER JOIN PROJECTS_TYPES PT
ON P.TYPE_ID = PT.TYPE_ID

INNER JOIN CLASSES CLA
ON P.CLASS_ID = CLA.CLASS_ID

INNER JOIN AGENCIES A
ON P.AGENCY_ID = A.AGENCY_ID

LEFT JOIN MINISTRIES M
ON P.MINISTRY_ID = M.MINISTRY_ID

LEFT JOIN DIRECTORATES DIR
ON P.DIRECTORATE_ID = DIR.DIRECTORATE_ID

INNER JOIN GOVERNORATES G
ON P.GOVERNORATE_ID = G.GOVERNORATE_ID

LEFT JOIN PROVINCES PR
ON P.PROVINCE_ID = PR.PROVINCE_ID

LEFT JOIN DISTRICTS D
ON P.DISTRICT_ID = D.DISTRICT_ID

LEFT JOIN TOWNS T
ON P.TOWN_ID = T.TOWN_ID

-- ADDED COSTS
LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(ADDED_VALUE, 0) - COALESCE(REMOVED_VALUE, 0)) as ADDED_COSTS
FROM COSTS
GROUP BY PROJ_ID ) AS CO
ON P.PROJ_ID = CO.PROJ_ID

-- EXPENSES FROM PAST YEARS
  LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as PAST_YEARS
  FROM EXPENSES WHERE EXTRACT(YEAR FROM DOC_DATE) < EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = FALSE
GROUP BY PROJ_ID ) AS EPY
ON P.PROJ_ID= EPY.PROJ_ID

-- EXPENSES FROM PAST MONTHS IN CUREENT YEAR
  LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as PAST_MONTHS
FROM EXPENSES WHERE EXTRACT(MONTH FROM DOC_DATE) < EXTRACT(MONTH FROM CURRENT_DATE) 
AND EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE)  AND CASH_DEDUCTIONS = FALSE
GROUP BY PROJ_ID ) AS EF
ON P.PROJ_ID= EF.PROJ_ID

-- EXPENSES FROM CURRENT MONTH AND YEAR
  LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as CURRENT_MONTH
  FROM EXPENSES WHERE EXTRACT(MONTH FROM DOC_DATE) = EXTRACT(MONTH FROM CURRENT_DATE) AND EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = FALSE
GROUP BY PROJ_ID ) AS ECM
ON P.PROJ_ID= ECM.PROJ_ID

-- SUM OF EXPENSES IN CURRENT YEAR
  LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as CURRENT_YEAR
  FROM EXPENSES WHERE EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = FALSE
GROUP BY PROJ_ID ) AS ECY
ON P.PROJ_ID= ECY.PROJ_ID

-- TOTAL EXPENSES FROM ALL TIME
  LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as TOTAL_EXPENSES
FROM EXPENSES WHERE CASH_DEDUCTIONS = FALSE
GROUP BY PROJ_ID ) AS E
ON P.PROJ_ID= E.PROJ_ID

-- CASH DEDUCTIONS SUMS
-- CASH DEDUCTIONS FROM PAST YEARS
  LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as CASH_PAST_YEARS
  FROM EXPENSES WHERE EXTRACT(YEAR FROM DOC_DATE) < EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = TRUE
GROUP BY PROJ_ID ) AS CASH_EPY
ON P.PROJ_ID= CASH_EPY.PROJ_ID

-- CASH DEDUCTIONS FROM PAST MONTHS IN CUREENT YEAR
  LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as CASH_PAST_MONTHS
FROM EXPENSES WHERE EXTRACT(MONTH FROM DOC_DATE) < EXTRACT(MONTH FROM CURRENT_DATE) 
AND EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE)  AND CASH_DEDUCTIONS = TRUE
GROUP BY PROJ_ID ) AS CASH_EF
ON P.PROJ_ID= CASH_EF.PROJ_ID

-- CASH DEDUCTIONS FROM CURRENT MONTH AND YEAR
  LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as CASH_CURRENT_MONTH
  FROM EXPENSES WHERE EXTRACT(MONTH FROM DOC_DATE) = EXTRACT(MONTH FROM CURRENT_DATE) AND EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = TRUE
GROUP BY PROJ_ID ) AS CASH_ECM
ON P.PROJ_ID= CASH_ECM.PROJ_ID

-- SUM OF CASH DEDUCTIONS IN CURRENT YEAR
  LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as CASH_CURRENT_YEAR
  FROM EXPENSES WHERE EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = TRUE
GROUP BY PROJ_ID ) AS CASH_ECY
ON P.PROJ_ID= CASH_ECY.PROJ_ID

-- TOTAL CASH DEDUCTIONS FROM ALL TIME
  LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as CASH_TOTAL_EXPENSES
FROM EXPENSES WHERE CASH_DEDUCTIONS = TRUE
GROUP BY PROJ_ID ) AS CASH_E
ON P.PROJ_ID= CASH_E.PROJ_ID

-- TOTAL EXPENSES AND CASH DEDUCTIONS FROM ALL TIME
  LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as TOTAL_EXPENSES_CASH
FROM EXPENSES
GROUP BY PROJ_ID ) AS TOTAL_E
ON P.PROJ_ID= TOTAL_E.PROJ_ID

-- ADDED DURATIONS
  LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(ADDED_VALUE, 0) - COALESCE(REMOVED_VALUE, 0)) as ADDED_DURATIONS
FROM DURATIONS
GROUP BY PROJ_ID ) AS DU
ON P.PROJ_ID= DU.PROJ_ID
ORDER BY P.PROJ_YEAR, P.TYPE_ID, P.PROJ_NO 

我认为最好从费用 table 的一个子查询开始。看起来您在子查询中的合并只是在 1 列上用 0 替换空值 - 您可以 运行 一个更新来解决这个问题,但如果没有,我已经包含了一种只执行一次的方法下面的例子。

你有(例如):

-- TOTAL CASH DEDUCTIONS FROM ALL TIME
LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as CASH_TOTAL_EXPENSES
FROM EXPENSES WHERE CASH_DEDUCTIONS = TRUE
GROUP BY PROJ_ID ) AS CASH_E
ON P.PROJ_ID= CASH_E.PROJ_ID

-- TOTAL EXPENSES AND CASH DEDUCTIONS FROM ALL TIME
  LEFT JOIN (SELECT PROJ_ID, SUM(COALESCE(TOTAL_VALUE, 0)) as TOTAL_EXPENSES_CASH
FROM EXPENSES
GROUP BY PROJ_ID ) AS TOTAL_E
ON P.PROJ_ID= TOTAL_E.PROJ_ID

您可以改为这样做:

with MY_EXPENSES AS 
   (
   select COALESCE(TOTAL_VALUE, 0) as MY_TOTAL_VALUE, exp.*
   from EXPENSES),

   EXPENSES_SUMMARY AS
   (
   SELECT PROJ_ID,
       sum(case when CASH_DEDUCTIONS = TRUE 
           then MY_TOTAL_VALUE else 0 
           end) as CASH_TOTAL_EXPENSES,
       sum(MY_TOTAL_VALUE) as TOTAL_EXPENSES_CASH
    FROM MY_EXPENSES
    GROUP BY PROJ_ID )

<Big Query>
LEFT OUTER JOIN EXPENSES_SUMMARY es P.PROJ_ID= TOTAL_E.PROJ_ID

基本上从子查询中的 where 子句中取出条件,并将它们放在总和内的 case 语句中 - 然后您可以拥有全部 15 个或无论多少个项目来自该 EXPENSES table在子查询项中。只需要合并 TOTAL_VALUE 一次而不是在每个子查询中。

您还可以创建一个视图来处理汇总数据。

EXPENSES、DURATION 或 COSTS 中是否存在根本不存在的 proj_id?如果不是,那么一旦您进行了上述更改,您就不必在主查询中执行任何 COALESCE。如果是这样,那么您可能仍然需要用 0 替换一些空值 - 如果您只想从查询中获取合并,可以在客户端执行此操作。

总的来说,我发现 'with . . . select' 对于复杂的查询更具可读性,但这可能只是一种偏好。

感谢aduguid and dandarc

,这是最后一个更快更清晰的代码
WITH
EXPENSES_SUMS
AS
(
    SELECT 
          PROJ_ID
        , SUM(CASE 
                WHEN EXTRACT(YEAR FROM DOC_DATE) < EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = FALSE 
                THEN TOTAL_VALUE
          END) AS PAST_YEARS
        , SUM(CASE  
                WHEN EXTRACT(MONTH FROM DOC_DATE) < EXTRACT(MONTH FROM CURRENT_DATE) AND EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = FALSE
                THEN TOTAL_VALUE
          END) AS PAST_MONTHS
        , SUM(CASE  
                WHEN EXTRACT(MONTH FROM DOC_DATE) = EXTRACT(MONTH FROM CURRENT_DATE) AND EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = FALSE
                THEN TOTAL_VALUE 
          END) AS CURRENT_MONTH
        , SUM(CASE  
                WHEN EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = FALSE
                THEN TOTAL_VALUE
          END) AS CURRENT_YEAR
        , SUM(CASE  
                WHEN CASH_DEDUCTIONS = FALSE
                THEN TOTAL_VALUE
          END) AS TOTAL_EXPENSES
        , SUM(CASE  
                WHEN EXTRACT(YEAR FROM DOC_DATE) < EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = TRUE
                THEN TOTAL_VALUE
          END) AS CASH_PAST_YEARS
        , SUM(CASE  
                WHEN EXTRACT(MONTH FROM DOC_DATE) < EXTRACT(MONTH FROM CURRENT_DATE) AND EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE)  AND CASH_DEDUCTIONS = TRUE
                THEN TOTAL_VALUE 
          END) AS CASH_PAST_MONTHS
        , SUM(CASE  
                WHEN EXTRACT(MONTH FROM DOC_DATE) = EXTRACT(MONTH FROM CURRENT_DATE) AND EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = TRUE
                THEN TOTAL_VALUE
          END) AS CASH_CURRENT_MONTH
        , SUM(CASE  
                WHEN EXTRACT(YEAR FROM DOC_DATE) = EXTRACT(YEAR FROM CURRENT_DATE) AND CASH_DEDUCTIONS = TRUE
                THEN TOTAL_VALUE
          END) AS CASH_CURRENT_YEAR
        , SUM(CASE  
                WHEN CASH_DEDUCTIONS = TRUE
                THEN TOTAL_VALUE
          END) AS CASH_TOTAL_EXPENSES
        , SUM(TOTAL_VALUE) AS TOTAL_EXPENSES_CASH
        --you can add more columns with your conditions here in case expressions

    FROM 
        EXPENSES
    GROUP BY 
        PROJ_ID
)
, 
COSTS_SUMS
AS
(
    SELECT 
          PROJ_ID
        , SUM(COALESCE(ADDED_VALUE, 0) - COALESCE(REMOVED_VALUE, 0)) AS ADDED_COSTS
        --you can add more columns with your conditions here in case expressions

    FROM 
        COSTS
    GROUP BY 
        PROJ_ID
)
, 
DURATIONS_SUMS
AS
(
    SELECT 
          PROJ_ID
        , SUM(COALESCE(ADDED_VALUE, 0) - COALESCE(REMOVED_VALUE, 0)) AS ADDED_DURATIONS
        --you can add more columns with your conditions here in case expressions

    FROM 
        DURATIONS
    GROUP BY 
        PROJ_ID
)
SELECT 
      P.PROJ_ID
    , P.PROJ_STATUS
    , P.TYPE_ID
    , PT.TYPE_NAME
    , P.CLASS_ID
    , CLA.CLASS_NAME
    , P.PROJ_NO
    , P.PROJ_YEAR
    , P.PROJ_NAME
    , P.OLD_PROJ_NAME
    , P.AGENCY_ID
    , A.AGENCY_NAME
    , P.CONTRACT_NO
    , P.CONTRACT_DATE
    , P.MINISTRY_ID
    , M.MINISTRY_NAME
    , P.DIRECTORATE_ID
    , DIR.DIRECTORATE_NAME
    , P.COST
    , P.ESTIMATED_COST
    , COALESCE(CO.ADDED_COSTS, 0) AS ADDED_COSTS
    , (COALESCE(P.COST, 0) + COALESCE(CO.ADDED_COSTS, 0)) AS TOTAL_COST
    , P.ALLOCATION
    , COALESCE(ED.PAST_YEARS, 0) AS PAST_YEARS
    , COALESCE(ED.PAST_MONTHS, 0) AS PAST_MONTHS
    , COALESCE(ED.CURRENT_MONTH, 0) AS CURRENT_MONTH
    , COALESCE(ED.CURRENT_YEAR, 0) AS CURRENT_YEAR
    , COALESCE(ED.TOTAL_EXPENSES, 0) AS TOTAL_EXPENSES
    , COALESCE(ED.CASH_PAST_YEARS, 0) AS CASH_PAST_YEARS
    , COALESCE(ED.CASH_PAST_MONTHS, 0) AS CASH_PAST_MONTHS
    , COALESCE(ED.CASH_CURRENT_MONTH, 0) AS CASH_CURRENT_MONTH
    , COALESCE(ED.CASH_CURRENT_YEAR, 0) AS CASH_CURRENT_YEAR
    , COALESCE(ED.CASH_TOTAL_EXPENSES, 0) AS CASH_TOTAL_EXPENSES
    , COALESCE(ED.TOTAL_EXPENSES_CASH, 0) AS TOTAL_EXPENSES_CASH
    , ((COALESCE(P.COST, 0) + COALESCE(CO.ADDED_COSTS, 0)) - COALESCE(ED.TOTAL_EXPENSES, 0)) AS REMAINING
    , P.DURATION
    , COALESCE(DU.ADDED_DURATIONS, 0) AS ADDED_DURATIONS
    , (COALESCE(P.DURATION, 0) + COALESCE(DU.ADDED_DURATIONS, 0)) AS TOTAL_DURATION
    , P.START_DATE
    , P.FINISH_DATE
    , P.GOVERNORATE_ID
    , G.GOVERNORATE_NAME
    , P.PROVINCE_ID
    , PR.PROVINCE_NAME
    , P.DISTRICT_ID
    , D.DISTRICT_NAME
    , P.TOWN_ID
    , T.TOWN_NAME
    , COALESCE( (ED.TOTAL_EXPENSES / (COALESCE(P.COST, 0) + COALESCE(CO.ADDED_COSTS, 0)))/100, 0) AS FINANCIAL_ACHIEVEMENT
    , P.MATERIAL_ACHIEVEMENT
    , P.NOTES
FROM 
    PROJECTS P
    INNER JOIN PROJECTS_TYPES PT ON P.TYPE_ID = PT.TYPE_ID
    INNER JOIN CLASSES CLA ON P.CLASS_ID = CLA.CLASS_ID
    INNER JOIN AGENCIES A ON P.AGENCY_ID = A.AGENCY_ID
    LEFT JOIN MINISTRIES M ON P.MINISTRY_ID = M.MINISTRY_ID
    LEFT JOIN DIRECTORATES DIR ON P.DIRECTORATE_ID = DIR.DIRECTORATE_ID
    INNER JOIN GOVERNORATES G ON P.GOVERNORATE_ID = G.GOVERNORATE_ID
    LEFT JOIN PROVINCES PR ON P.PROVINCE_ID = PR.PROVINCE_ID
    LEFT JOIN DISTRICTS D ON P.DISTRICT_ID = D.DISTRICT_ID
    LEFT JOIN TOWNS T ON P.TOWN_ID = T.TOWN_ID
    LEFT JOIN EXPENSES_SUMS ED ON P.PROJ_ID = ED.PROJ_ID
    LEFT JOIN COSTS_SUMS CO ON P.PROJ_ID = CO.PROJ_ID
    LEFT JOIN DURATIONS_SUMS DU ON P.PROJ_ID = CO.PROJ_ID
ORDER BY 
      P.PROJ_YEAR
    , P.TYPE_ID
    , P.PROJ_NO