SQL - 如何聚合和计算外观

时间:2017-03-06 22:35:46

标签: sql oracle left-join

对于以下示例,VOLUME_ONVOLUME_OFFCOST_TYPE可以是一组特定值(注意VOLUMES_ON/OFF值与COST_TYPE值不同)

CREATE TABLE PROJECT (
    PROJECT_ID  VARCHAR2 (10), 
    NAME        VARCHAR2(10), 
    PRIMARY KEY (PROJECT_ID)
);

CREATE TABLE PROJECT_COSTS (
    COST_ID     VARCHAR2 (10), 
    COST_TYPE   VARCHAR2 (10), 
    PRIMARY KEY (COST_ID)
);

CREATE TABLE PROJECT_DETAILS (
    DETAIL_ID   VARCHAR2 (10), 
    VOLUME_ON   VARCHAR2(10), 
    VOLUME_OFF  VARCHAR2(10), 
    PRJ_ID VARCHAR2 (10) FOREIGN KEY 
        REFERENCES PROJECT(PROJECT_ID),
    CST_ID FOREIGN KEY 
        REFERENCES PROJECT_COSTS(COST_ID)
);

对于每个PROJECT.PROJECT_ID,我想计算VOLUME_ON中某些值VOLUME_OFFCOST_TYPE的出现次数。有点像。

SELECT COUNT(VOLUME_ON),
       COUNT(VOLUME_OFF),
       COUNT(TBC.VOLUME_ON),
       COUNT(TBC.VOLUME_OFF)
FROM PROJECT p
LEFT JOIN PROJECT_DETAILS pd ON p.PROJECT_ID = pd.PROJECT_ID
LEFT JOIN PROJECT_COSTS pc ON pd.CST.ID = pc.COST_ID
AND pc.COST_TYPE IN ('ab', 'cd')
LEFT JOIN PROJECT_COSTS pc2 ON pd.CST.ID = pc2.COST_ID
AND pc2.COST_TYPE IN ('ef', 'gh')
GROUP BY p.PROJECT_ID

如何为每个VOLUME_ON的不同COST_TYPE返回VOLUME_OFFPROJECT_ID的计数?

2 个答案:

答案 0 :(得分:0)

您可以通过在COST_TYPE子句中添加COST_TYPE为每个GROUP BY生成单独的计数。您还应该将项目和成本类型信息添加到输出中,这样您就可以区分不同的项目/成本类型。

如,

SELECT p.name, 
       pc.cost_type, 
       COUNT(VOLUME_ON), 
       COUNT(VOLUME_OFF), 
       COUNT(TBC.VOLUME_ON), 
       COUNT(TBC.VOLUME_OFF)
FROM PROJECT p
LEFT JOIN PROJECT_DETAILS pd
  ON p.PROJECT_ID = pd.PROJECT_ID
LEFT JOIN PROJECT_COSTS pc
  ON pd.CST.ID = pc.COST_ID
  AND pc.COST_TYPE IN ('ab', 'cd')
LEFT JOIN PROJECT_COSTS pc2
  ON pd.CST.ID = pc2.COST_ID
  AND pc2.COST_TYPE IN ('ef', 'gh')
GROUP BY p.PROJECT_ID, pc.cost_type

答案 1 :(得分:0)

我认为这是一个显示"左连接常数"问题。

通常,在语义上加入常量不是真正需要的。在此示例中,我们应该在COST_TYPE上过滤,而不是加入COST_TYPE。为什么这很重要?因为他们给出不同的结果!这可以通过示例来显示。让我们在这个例子中添加一些数据:

drop table project_details;
drop table project_costs;
drop table project;

CREATE TABLE PROJECT (
    PROJECT_ID  VARCHAR2 (10),
    NAME        VARCHAR2(10),
    PRIMARY KEY (PROJECT_ID)
);

CREATE TABLE PROJECT_COSTS (
    COST_ID     VARCHAR2 (10),
    COST_TYPE   VARCHAR2 (10),
    PRIMARY KEY (COST_ID)
);

CREATE TABLE PROJECT_DETAILS (
    DETAIL_ID   VARCHAR2 (10),
    VOLUME_ON   VARCHAR2(10),
    VOLUME_OFF  VARCHAR2(10),
    PRJ_ID VARCHAR2 (10),
    CST_ID VARCHAR2(10),
    FOREIGN KEY (PRJ_ID) REFERENCES PROJECT(PROJECT_ID),
    FOREIGN KEY (CST_ID ) REFERENCES PROJECT_COSTS(COST_ID)
);

insert into project values( '1', 'Proj1' );
insert into project values( '2', 'Proj2' );
insert into project values( '3', 'Proj3' );
insert into project values( '4', 'Proj4' );

commit;

insert into project_costs values( 'C1', 'ab' );
insert into project_costs values( 'C2', 'cd' );
insert into project_costs values( 'C3', 'ef' );
insert into project_costs values( 'C4', 'gh' );
insert into project_costs values( 'C5', 'zz' );

commit;

insert into project_details values( 'D1', 'V1', 'V2', '1','C1' );
insert into project_details values( 'D2', 'V1', 'V2', '1','C2' );
insert into project_details values( 'D3', 'V1', 'V2', '1','C3' );
insert into project_details values( 'D4', 'V1', 'V2', '2','C1' );
insert into project_details values( 'D5', 'V1', 'V2', '3','C1' );
insert into project_details values( 'D6', 'V1', 'V2', '1','C1' );

commit;

暂时忽略聚合,只执行连接...

SELECT  p.name
      , pc.cost_id
      , pc.cost_type
      , pd.detail_id
FROM PROJECT p
LEFT JOIN PROJECT_DETAILS pd
  ON p.PROJECT_ID = pd.PRJ_ID
LEFT JOIN PROJECT_COSTS pc
  ON pd.CST_ID = pc.COST_ID
  AND pc.COST_TYPE IN ('ab', 'cd')


NAME       COST_ID    COST_TYPE  DETAIL_ID
---------- ---------- ---------- ----------
Proj1      C1         ab         D6
Proj3      C1         ab         D5
Proj2      C1         ab         D4
Proj1      C1         ab         D1
Proj1      C2         cd         D2
Proj4
Proj1                            D3

7 rows selected.

您可以看到我们在"加入"之后得到左连接生成的其他行。在COST_TYPE上(' ab',' cd')。如果我们将JOIN更改为过滤器...

SELECT  p.name
      , pc.cost_id
      , pc.cost_type
      , pd.detail_id
FROM PROJECT p
LEFT JOIN PROJECT_DETAILS pd
  ON p.PROJECT_ID = pd.PRJ_ID
LEFT JOIN PROJECT_COSTS pc
  ON pd.CST_ID = pc.COST_ID
  WHERE pc.COST_TYPE IN ('ab', 'cd')   <--- Now a filter


  NAME       COST_ID    COST_TYPE  DETAIL_ID
---------- ---------- ---------- ----------
Proj1      C1         ab         D1
Proj1      C2         cd         D2
Proj2      C1         ab         D4
Proj3      C1         ab         D5
Proj1      C1         ab         D6

由于我们已经定义了PROJECT_DETAILSPROJECT_COSTS之间的关系,因此我们知道每个PROJECT_DETAIL记录必须具有PROJECT_COST,因此不需要LEFT OUTER连接。实际上,优化器会将计划转换为使用内连接。

所以最后,我认为查询看起来应该像

SELECT p.name
        , pc.cost_type
        , count(pd.volume_on)
        , count(pd.volume_off)
FROM PROJECT p
LEFT JOIN PROJECT_DETAILS pd
  ON p.PROJECT_ID = pd.PRJ_ID
LEFT JOIN PROJECT_COSTS pc
  ON pd.CST_ID = pc.COST_ID
  where pc.COST_TYPE IN ('ab', 'cd', 'ef', 'gh')
group by p.name
        , pc.cost_type
order by 1,2


NAME       COST_TYPE  COUNT(PD.VOLUME_ON) COUNT(PD.VOLUME_OFF)
---------- ---------- ------------------- --------------------
Proj1      ab                           2                    2
Proj1      cd                           1                    1
Proj1      ef                           1                    1
Proj2      ab                           1                    1
Proj3      ab                           1                    1

但是,我在这里要强调的重要一点是,OUTER连接中ON子句和WHERE子句之间的语义差异。