【问题标题】:SQL optimization GROUP BY same table, same columnSQL优化GROUP BY同表同列
【发布时间】:2019-05-02 10:11:40
【问题描述】:

我有这个GROUP BY 查询,我需要从TABLE_1 中选择一些记录并聚合它们。

SELECTs 相似,但我需要分别聚合 LONBHAM,因为它们是两个不同的概念,但位于同一个表中。

我的问题是我可以在 oracle 中以不同的方式编写以下代码来优化查询的性能吗?

SELECT  *
FROM (
        (    SELECT  /*+ full(t1) */ 
                t3.custId AS ID,       
                t2.secID AS SEC_ID, 
                t1.org_date AS SETT_DATE,
                SUM(t1.amount) AS TOTAL
            FROM test.TABLE_1 t1 
            INNER JOIN test.TABLE_2 t2 on t2.a_code = t1.a_code  and t2.c_code = t1.c_code and t2.expiry_date > trunc(sysdate)
            INNER JOIN test.TABLE_3 t3 on t3.account_id = t1.account_id 
            WHERE t1.city = 'LON'         
                AND t1.amount < 50000 and t1.amount > -50000
            GROUP BY t3.custId,  t2.secID, t1.org_date

        )

          UNION ALL

        (    SELECT  /*+ full(t1) */ 
                t3.custId AS ID,      
                t2.secID AS SEC_ID,
                t1.org_date AS SETT_DATE,
                SUM(t1.amount) AS TOTAL
            FROM test.TABLE_1 t1 
            INNER JOIN test.TABLE_2 t2 on t2.a_code = t1.a_code  and t2.c_code = t1.c_code and t2.expiry_date > trunc(sysdate)
            INNER JOIN test.TABLE_3 t3 on t3.account_id = t1.account_id 
            WHERE t1.city = 'BHAM'
                AND t3.alias = 'ABC' 
                AND t1.amount < 50000 and t1.amount > -50000
            GROUP BY t3.custId,  t2.secID, t1.org_date
        )   
    )
 ORDER BY ID,  SEC_ID, 
          CASE WHEN SETT_DATE < TRUNC(sysdate) THEN trunc(sysdate) ELSE TRUNC(SETT_DATE) end       

【问题讨论】:

  • 您需要将它们放在单独的行中,还是可以接受单独的列?
  • @Boneist 不接受单独的列,因为我的进程正在使用查询返回并映射到目标的列 ID、SEC_ID、SETT_DATE。我需要将它们放在单独的行中,因为我正在处理每一行
  • 好的。顺便说一句,您的查询将失败,因为所选列列表中的 t1.org_date as sett_date 与 group by 中的 case when t1.org_date &lt; .... 不匹配。你的问题是不是打错字了?
  • 还有,你怎么知道哪一行属于哪个城市?
  • @Boneist 正确,案例最初在选择时出现,我错过了输入 SO。为了暂时保持查询简短,我在 select + group by 中保持它们的一致性

标签: sql oracle query-optimization


【解决方案1】:

删除union all 和之后的所有内容,删除外部select,像这里一样写where 子句:

where -50000 < t1.amount and t1.amount < 50000 
  and (t1.city = 'LON' or (t1.city = 'BHAM' and t3.alias = 'ABC'))

【讨论】:

  • 我认为这不是 OP 想要的 - 这将产生单行,而不是为不同的城市/别名组合生成两行。
  • @Boneist - 外部选择仅使用 id 和 sec_id 对行进行分组。
  • 外部查询不进行分组或区分。 OP 需要不同城市/别名组合的行,这将根据您的建议压缩成一行,因为城市和别名不属于 group by。
  • 你是对的。所以将城市添加到group by 就足够了。
  • 好的,所以从 OP 对我的答案的评论来看,您的答案似乎是正确的!哦!
【解决方案2】:

您需要将city列添加到group by中,并更新where子句以获取两组行,例如:

SELECT custid,
       sec_id,
       sett_date,
       total
FROM   (SELECT t3.custid AS id,
               t2.secid AS sec_id,
               CASE
                 WHEN t1.org_date < trunc(SYSDATE) THEN
                  trunc(SYSDATE)
                 ELSE
                  trunc(t1.org_date)
               END AS sett_date,
               t1.city,
               SUM(t1.amount) AS total
        FROM   test.table_1 t1
        INNER  JOIN test.table_2 t2
        ON     t2.a_code = t1.a_code
        AND    t2.c_code = t1.c_code
        AND    t2.expiry_date > trunc(SYSDATE)
        INNER  JOIN test.table_3 t3
        ON     t3.account_id = t1.account_id
        WHERE  (t1.city = 'LON' OR (t1.city = 'BHAM' AND t3.alias = 'ABC'))
        AND    t1.amount < 50000
        AND    t1.amount > -50000
        GROUP  BY t3.custid,
                  t2.secid,
                  CASE
                    WHEN t1.org_date < trunc(SYSDATE) THEN
                     trunc(SYSDATE)
                    ELSE
                     trunc(t1.org_date)
                  END)
ORDER  BY id,
          sec_id,
          sett_date;

但是,我很惊讶您需要这两行,因为您无法分辨哪个属于哪个城市。我怀疑您需要将城市(可能还有别名)列包含在最终结果中。

【讨论】:

  • LON 中找不到针对BHAMcustId 设置
  • 好的,如果一个 custid 只能在一个城市中,那么您就不需要将它们包括在组中 - 根据 Ponder 的回答!
  • 嗯,如果你把城市从group by中排除,那么Ponder的答案就是正确答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多