【问题标题】:Using subquery in join clause instead of column name在连接子句中使用子查询而不是列名
【发布时间】:2019-04-24 19:08:23
【问题描述】:

我正在尝试加入两个表。表中存在一列,但另一列是派生列。

event_date - exists directly in the table
deploy_date - derived using case statement

参见原始查询中的第 6 行。

   1 SELECT    ab.id, 
   2          ab.event_date, 
   3           CASE 
   4                     WHEN ab.label = 'ABC' THEN ab.event_date 
   5                     WHEN ab.label = 'DEF' THEN ab.start_date 
   6           END deploy_date, 
   7 FROM      ab_bro AB 
   8  LEFT JOIN ab_rev rv 
   9 ON        ab.bro_id = rv.bro_id 
   10 AND       ab.event_date = rv.event_date 

现在我想在第 10 行(上图)中使用 deploy_date 而不是 event_date 由于sql不允许在join中使用别名,所以我尝试使用子查询

SELECT    ab.id, 
          ab.event_date, 
          CASE 
                    WHEN ab.label = 'ABC' THEN ab.event_date 
                    WHEN ab.label = 'DEF' THEN ab.start_date 
          END deploy_date, 
FROM      ab_bro AB 
LEFT JOIN ab_rev rv 
ON        ab.bro_id = rv.bro_id 
AND 
          ( 
             SELECT 
                 CASE 
                     WHEN AC.label = 'ABC' THEN AC.event_date 
                     WHEN AC.label = 'DEF' THEN AC.start_date 
                  END deploy_date from ab_bro AC) = rv.event_date 

我的错误 -

错误:用作表达式的子查询返回多于一行

我知道我的子查询确实返回了不止一行,但我不知道如何解决这个问题。

【问题讨论】:

  • 这看起来很奇怪。如果在CASE 之前删除SELECT,是否还会出现同样的错误?
  • @GordonLinoff 导致语法错误
  • @a_horse_with_no_name 它是 Greenplum。我修复了标签。由于 GP 是基于 Postgres 之上的,我认为它会有类似的语法
  • @PirateX 去掉 from ab_bro ACSELECT 就不需要子查询了,只需要 CASE 表达式。
  • @JNevill 原来我错过了 case 语句的 END。我想我需要休息一下。非常感谢!!

标签: sql join subquery greenplum


【解决方案1】:

您需要做的就是在您的 JOIN 条件中使用该 CASE 表达式:

SELECT
    ab.id,
    ab.event_date,
    CASE
        WHEN ab.label = 'ABC'
            THEN ab.event_date
        WHEN ab.label = 'DEF'
            THEN ab.start_date
    END deploy_date,
FROM ab_bro AB
    LEFT JOIN ab_rev rv
        ON ab.bro_id = rv.bro_id 
        AND rv.event_date = CASE
                WHEN ab.label = 'ABC'
                    THEN ab.event_date
                WHEN ab.label = 'DEF'
                    THEN ab.start_date
                END

【讨论】:

    【解决方案2】:

    如果 AND ( ) 子选择返回的行数更多,则可以使用 limit1

    SELECT    ab.id, 
              ab.event_date, 
              CASE 
                        WHEN ab.label = 'ABC' THEN ab.event_date 
                        WHEN ab.label = 'DEF' THEN ab.start_date 
              END deploy_date, 
    FROM      ab_bro AB 
    LEFT JOIN ab_rev rv 
    ON        ab.bro_id = rv.bro_id 
    AND   ( 
                 SELECT 
                     CASE 
                         WHEN AC.label = 'ABC' THEN AC.event_date 
                         WHEN AC.label = 'DEF' THEN AC.start_date   
                      END deploy_date from ab_bro AC
                      limit 1 ) = rv.event_date 
    

    或使用 IN 代替 =

      SELECT    ab.id, 
              ab.event_date, 
              CASE 
                        WHEN ab.label = 'ABC' THEN ab.event_date 
                        WHEN ab.label = 'DEF' THEN ab.start_date 
              END deploy_date, 
    FROM      ab_bro AB 
    LEFT JOIN ab_rev rv 
    ON        ab.bro_id = rv.bro_id 
    AND  rv.event_date IN  ( 
                 SELECT 
                     CASE 
                         WHEN AC.label = 'ABC' THEN AC.event_date 
                         WHEN AC.label = 'DEF' THEN AC.start_date   
                      END deploy_date from ab_bro AC
                      limit 1 )
    

    【讨论】:

    • 我可以使用限制子句,但我认为它会影响逻辑。有什么想法吗?
    • 我不知道你的逻辑目标..无论如何使用限制你应该添加一个顺序来控制值返回..
    • 是的,我试过了。它弄乱了逻辑。不管怎么说,还是要谢谢你!感谢您的帮助。
    【解决方案3】:

    加入内联视图而不是在WHERE 子句中添加CASE 语句至少会更整洁一些。这将允许您避免重复 CASE 表达式。有些人可能会认为使用 CTE 而不是内联视图会更清晰:

    WITH x AS (
      SELECT
        id,
        bro_id,
        event_date,
        CASE label
          WHEN 'ABC' THEN event_date 
          WHEN 'DEF' THEN start_date 
        END deploy_date
      FROM ab_bro
    )
    SELECT
      x.id, 
      x.event_date, 
      x.deploy_date
    FROM
      x
      LEFT JOIN ab_rev rv 
        ON x.bro_id = rv.bro_id 
          AND x.deploy_date = rv.event_date
    

    您可以将这两种变体视为创建一个包含派生数据的临时表作为普通列,并将其连接到另一个表。

    【讨论】:

    • 我在帖子中分享的 sn-p 实际上是一个巨大的 sql 代码中的一小部分,将整个逻辑移动到 CTE 可能会导致过于复杂(至少对我而言)
    • @PirateX 这似乎过于复杂,但有时将查询分解为逻辑碎片有助于获得您需要的正确答案。我对Greenplum不熟悉,但是如果你想使用deploy_date,它必须在JOIN之前存在。由于FROMJOIN 通常发生在逻辑查询过程的早期,因此您的别名列需要存在,因此需要 CTE 或子查询。
    • 这是你的代码,@PirateX。不过,从我的角度来看,使用 CTE 比将 CASE 放在 where 子句中更更简单,更不用说更易于维护了。但请注意,它不是将整个逻辑放入 CTE(或内联视图)中。它将合成柱的生成放入 CTE。如果它正在命名困扰您的所有单个列,那么您应该能够将现有列的整个列表缩写为 *
    • @JohnBollinger 感谢您的意见。我一定会尝试灌输这些观点:)
    【解决方案4】:
    SELECT sub.id,
        sub.event_date,
        sub.deploy_date
    FROM (
        SELECT ab.id, 
            ab.event_date, 
            ab.bro_id,
            CASE 
                WHEN ab.label = 'ABC' THEN ab.event_date 
                WHEN ab.label = 'DEF' THEN ab.start_date END as deploy_date, 
        FROM ab_bro AB 
        ) AS sub
    LEFT JOIN ab_rev rv ON sub.bro_id = rv.bro_id 
    AND sub.deploy_date = rv.event_date; 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-31
      • 2021-10-23
      • 2018-03-27
      相关资源
      最近更新 更多