【问题标题】:Is there a way to do this in a simple query?有没有办法在一个简单的查询中做到这一点?
【发布时间】:2020-02-16 12:35:14
【问题描述】:

我有一个 BIG(2300 万行)表 CONTRATOS,其中包含以下列:

SELECT
    CONTRATO,
    CODIGO_ORIGEN,
    ORIGEN
FROM CONTRATOS;

CODIGO_ORIGEN 列有多行具有相同的值:

CODIGO_ORIGEN     CONTRATO       ORIGEN 
-------------    ----------     --------
     1               345          CONT
     1               363          BKP
     1               645          BKP
     1               365          CONT 

我只需要为每个 CODIGO_ORIGEN 获取一个 CONTRATO,但始终优先考虑 'CONT' 值。所以在这个示例中,它将是值为 365

的 CONTRATO

我正在尝试这样的事情:

SELECT  
   CODIGO_ORIGEN,
   CASE
    WHEN ORIGEN = 'CONT' THEN
        FIRST_VALUE (CONTRATO) OVER (PARTITION BY CODIGO_ORIGEN ORDER BY CONTRATO DESC) 
    WHEN ORIGEN = 'BKP' THEN
        FIRST_VALUE (CONTRATO) OVER (PARTITION BY CODIGO_ORIGEN ORDER BY CAMPO1 DESC, CAMPO2 DESC, CONTRATO DESC) 
   END AS CONTRATO
FROM CONTRATOS;

如果 ORIGEN 是“CONTR”,我应该按最高的 CONTRATO 排序并获得 CONTRATO 列。 如果 ORIGEN 是“BKP”,我应该按不同的列排序并获得 CONTRATO 列。

最后,我应该能够使用 CODIGO_ORIGEN -> CONTRATO (1:1) 拥有一个唯一的行。

有没有一种简单的方法可以做到这一点(1 个查询)?

提前致谢!

【问题讨论】:

  • 仅添加一个示例数据表可能会替换所有文本,您应该这样做。
  • 我添加了 2 个简单的查询来澄清这个案例。你明白这个问题吗?谢谢!
  • 编辑问题并按照@TimBiegeleisen 的要求添加示例数据。这样得到好答案的机会就会增加。无论如何,回答您的问题(在评论中询问),否
  • 大家好,我编辑了我的问题,希望您现在可以理解。谢谢!
  • 您的需求仍然不明确。首先,您只显示 1 个 codigo_origen 值(一个糟糕的数据宇宙),并且复杂地优先考虑 origen -> cont。这是否意味着如果origen -> cont 然后排除origen -> bkp。从结果。此外,您的数据不完整,缺少您在查询中使用的 campo1 和 campo2 列。最后,您请求“带有 codigo_origen -> contrato (1:1) 的唯一行”这是什么意思?数据中似乎没有任何内容可以产生 (1:1) 的结果。

标签: sql oracle group-by subquery


【解决方案1】:

您可以使用ROW_NUMBER 来根据codigo_origen 对您的行进行排名。这是编写其ORDER BY 子句的一种方法。还有其他的。

select *
from
(
  select  
     c.*,
     row_number() over (
       partition by codigo_origen 
       order by 
         case when origen 'BKP' then campo1 end desc,
         case when origen 'BKP' then campo2 end desc,
         origen desc
     ) as rn
  from contratos c
) ranked
where rn = 1
order by codigo_origen;

【讨论】:

    【解决方案2】:

    我想我明白了:

          SELECT 
            C.contrato, C.codigo_origen,  NVL2(MX.CONTRATO, NULL, 'F') AS ESTADO, NVL2(MX.CONTRATO, NULL, '4008') AS ERROR
          FROM MGR_CUENTA_CTL C
          LEFT JOIN
          (
              select /*+ PARALLEL */
                DISTINCT CODIGO_ORIGEN,
                CASE
                  WHEN ORIGEN_TABLA = 'CONT' THEN FIRST_VALUE(CONTRATO) OVER (PARTITION BY CODIGO_ORIGEN ORDER BY CONTRATO DESC) 
                  WHEN ORIGEN_TABLA = 'BKP' THEN FIRST_VALUE(CONTRATO) OVER (PARTITION BY CODIGO_ORIGEN ORDER BY CAMPO1 DESC, CAMPO2 DESC) 
                END AS CONTRATO
              FROM
              (  
                 SELECT /*+ PARALLEL */ ctl.*
                 FROM mgr_cuenta_ctl ctl
                 LEFT JOIN 
                 (
                       select /*+ PARALLEL */ CODIGO_ORIGEN
                       from mgr_cuenta_ctl
                       group by codigo_origen having count(distinct origen) > 1
                 ) CTL2 ON ctl.CODIGO_ORIGEN = ctl2.CODIGO_ORIGEN
                 WHERE (CTL2.CODIGO_ORIGEN IS NOT NULL AND CTL.ORIGEN <> 'BKP') OR (CTL2.CODIGO_ORIGEN IS NULL)
              )
          ) MX ON MX.CONTRATO = C.CONTRATO;
    

    我认为它符合我的解释。

    这对你有意义吗?

    谢谢!

    【讨论】:

      【解决方案3】:

      检查是否有origen = 'CONT',使用解析count()。如果是 - 使用第一排序方法的解析函数,如果不是 - 第二排序:

      select mgr_cuenta_ctl.*,
             case 
             when count(case origen when 'CONT' then 1 end) over (partition by codigo_origen) > 0
             then first_value(contrato) over (
                  partition by codigo_origen
                  order by case origen when 'CONT' then 1 end, contrato desc)
             else first_value(contrato) over (
                  partition by codigo_origen
                  order by case origen when 'BKP' then 1 end, campo1 desc, campo2 desc, contrato desc)
             end as best
        from mgr_cuenta_ctl
      

      如果您只想要分组值而不需要详细信息,请删除 partition by 子句:

      select codigo_origen,
             case when count(case origen when 'CONT' then 1 end) > 0
             then max(contrato) keep (dense_rank first 
                      order by case origen when 'CONT' then 1 end, contrato desc) 
             else max(contrato) keep (dense_rank first 
                      order by case origen when 'BKP' then 1 end, campo1 desc, campo2 desc) 
             end as best
        from mgr_cuenta_ctl
        group by codigo_origen
      

      dbfiddle demo

      顺便说一句。我试图分析你的查询,但它抛出了一些奇怪的错误,我放弃了。在这里你只碰表一次,没有自连接,所以它应该更快。

      【讨论】:

      • 感谢您的提问!它与我发布的最新查询相同吗?
      猜你喜欢
      • 2011-05-05
      • 2011-10-04
      • 2021-07-09
      • 1970-01-01
      • 1970-01-01
      • 2017-05-13
      • 2020-11-23
      • 2011-06-28
      • 1970-01-01
      相关资源
      最近更新 更多