【问题标题】:Multi Column CASE WHEN in TeradataTeradata 中的多列 CASE WHEN
【发布时间】:2017-02-28 07:47:54
【问题描述】:

我正在尝试合并一些子查询,以避免多次访问大型表(42B 行)并获得

"[3771] CASE 表达式的 WHEN 子句中的非法表达式。"

,SUM(CASE
    WHEN (oh.LOCN_NBR,oh.WK_NBR) IN (SELECT LOCN_NBR,START_WK FROM VT_STORES)
    THEN oh.TTL_UN_QT
    ELSE NULL
    END) AS BEGINNING_OH

有什么方法可以在 CASE 语句中执行多列 IN 语句,还是我坚持将它们放在子查询中的连接/位置中?

编辑:根据要求进行完整查询:

SELECT
            oh.LOCN_NBR AS LOCN_NBR
            ,item.ITEM_ID AS ITEM_ID
            ,SUM(CASE
                WHEN oh.WK_NBR = (SELECT WK_NBR FROM ALEX_ARP_VIEWS_PRD.REF_CUSTOM_TIME WHERE cust_time_id=2 )
                THEN oh.TTL_UN_QT
                ELSE NULL 
            END) AS SALEABLE_QTY
            ,SUM(CASE
                WHEN oh.WK_NBR = (SELECT LY_WK_NBR FROM ALEX_ARP_VIEWS_PRD.REF_CUSTOM_TIME WHERE cust_time_id=2 )
                THEN oh.TTL_UN_QT
                ELSE NULL 
            END) AS SALEABLE_QTY_LY
            ,SUM(CASE
                WHEN (oh.LOCN_NBR,oh.WK_NBR) IN (SELECT LOCN_NBR,PRI_START_WK FROM VT_STORES)
                THEN oh.TTL_UN_QT
                ELSE NULL
            END) AS BEGINNING_OH_LY
            ,SUM(CASE
                WHEN (oh.LOCN_NBR,oh.WK_NBR) IN (SELECT LOCN_NBR,START_WK FROM VT_STORES)
                THEN oh.TTL_UN_QT
                ELSE NULL
            END) AS BEGINNING_OH

FROM 
            ALEX_ARP_VIEWS_PRD.FACT_WKLY_OPR_INS oh
            INNER JOIN VT_STORES stores ON oh.LOCN_NBR = stores.LOCN_NBR
            INNER JOIN VT_ITEM item ON oh.VEND_PACK_ID = item.VEND_PACK_ID
WHERE               
            INS_TYP_CD='H'
            AND TTL_UN_QT <> 0
            AND WK_NBR >= (SELECT MIN(PRI_START_WK) FROM VT_STORES)
GROUP BY
    oh.LOCN_NBR
    ,item.ITEM_ID

【问题讨论】:

  • VT_STORES的key是什么?
  • locn_nbr 是 VT_STORES 的键
  • 查询逻辑是否需要读取所有 42B 行,或者您会期望某些 where 条件显着限制数字?如果是这样,从计划中你看到这个限制有效吗?奇怪的是,大表是仅由我们看到的 5-6 个字段组成还是有很多额外的列?您需要在单个查询中完成所有逻辑,或者您可能会在临时/易失性表中使用一些步骤?
  • 最大的问题是 INS_TYPE_CD 列没有被索引,鉴于它的大小和重要性,我无法说服 DBA 更改该表的结构 - 这消除了表中大约 50% 的数据马上。我对其他解决方案持开放态度,但我在温度/vt 方面所做的一切尝试都超过了我的线轴限制 (100G)。
  • 在 50% 的情况下,我认为索引不会产生如此大的差异。也许分区会更有用。但是,如果你连数据都不能放在一个 vt/gtt 中,恐怕你真的需要增加你的 spool。

标签: sql teradata


【解决方案1】:

您不需要使用IN。你可以使用exists:

SUM(CASE WHEN EXISTS (SELECT 1 FROM VT_STORES v WHERE oh.LOCN_NBR = v.LOCN_NBR AND oh.WK_NBR = v.START_WK)
        THEN oh.TTL_UN_QT
     END) AS BEGINNING_OH

但是,我不能 100% 确定问题出在 IN。许多数据库不允许子查询作为聚合函数的参数。我不确定 Teradata 是否允许此功能。

【讨论】:

  • 我已按要求添加了完整的查询(处于非工作状态)。如果我注释掉第三种和第四种情况的函数,则查询有效,因此允许子查询......它似乎是多列部分不起作用。
【解决方案2】:

我正在尝试合并一些子查询以避免多次碰到一个大表

多列子查询只允许在WHERE 中使用,但在CASE 中不允许使用。重写为EXISTS 可能不会提高性能,计划实际上可能更复杂。

您只是尝试隐藏复杂性,但它仍然是后台的 Outer Join,如下所示:

,Sum(CASE WHEN stores.LOCN_NBR IS NOT NULL THEN oh.TTL_UN_QT END) AS BEGINNING_OH
...
FROM oh LEFT JOIN 
 (
   SELECT LOCN_NBR,START_WK FROM VT_STORES
 ) AS stores
 ON stores.LOCN_NBR = oh.LOCN_NBR
AND stores.START_WK  = oh.WK_NBR

您能否展示您当前的查询(至少是您尝试优化的那些部分)?

编辑:

当 VT_STORES.locn_nbr 是唯一的时,这应该返回相同的结果:

SELECT
            oh.LOCN_NBR AS LOCN_NBR
            ,item.ITEM_ID AS ITEM_ID
            ,Sum(CASE
                WHEN oh.WK_NBR = (SELECT Min(WK_NBR) FROM ALEX_ARP_VIEWS_PRD.REF_CUSTOM_TIME WHERE cust_time_id=2 )
                THEN oh.TTL_UN_QT
                ELSE NULL 
            END) AS SALEABLE_QTY
            ,Sum(CASE
                WHEN oh.WK_NBR = (SELECT Min(LY_WK_NBR) FROM ALEX_ARP_VIEWS_PRD.REF_CUSTOM_TIME WHERE cust_time_id=2 )
                THEN oh.TTL_UN_QT
                ELSE NULL 
            END) AS SALEABLE_QTY_LY
            ,Sum(CASE
                WHEN oh.WK_NBR= stores.PRI_START_WK 
                THEN oh.TTL_UN_QT
            END) AS BEGINNING_OH_LY
            ,Sum(CASE
                WHEN oh.WK_NBR = stores.START_WK 
                THEN oh.TTL_UN_QT
            END) AS BEGINNING_OH

FROM 
            ALEX_ARP_VIEWS_PRD.FACT_WKLY_OPR_INS oh
            INNER JOIN VT_STORES stores ON oh.LOCN_NBR = stores.LOCN_NBR
            INNER JOIN VT_ITEM item ON oh.VEND_PACK_ID = item.VEND_PACK_ID
WHERE               
            INS_TYP_CD='H'
            AND TTL_UN_QT <> 0
            AND WK_NBR >= (SELECT Min(PRI_START_WK) FROM VT_STORES)
GROUP BY
    oh.LOCN_NBR
    ,item.ITEM_ID

其他标量子查询应该没问题,因为它们只返回一行并且优化器知道这一点。

【讨论】:

  • 我已按要求添加了完整的查询(处于非工作状态)。如果我注释掉第三种和第四种情况的函数,则查询有效,因此允许子查询......它似乎是多列部分不起作用。
  • 这行得通,但不幸的是,我现在遇到了假脱机空间限制。我想我已经决定将过去的 4 个查询(每个 SUM 操作)合并为 2 个查询——前两个,然后是后两个。感谢您的帮助 - 我的案例陈述使问题过于复杂。
  • 如果有什么不好的地方,你应该检查解释。标量子查询应显示为调度程序检索步骤或基于增量计划。您可能需要与您的 DBA 交谈以增加假脱机...
【解决方案3】:

re: 有没有办法在 CASE 语句中做多列 IN 语句

没有。

re:避免多次碰到一个大表(42B 行)

哪个大?我看不出,即使复合 IN 子句有效,与使用连接/添加列到 where 子句相比,您也可以避免多次访问大表。

您是否尝试过类似的方法:

SELECT
    ...
        ,SUM(CASE
            WHEN SELECT COUNT(*) FROM VT_STORES 
                 WHERE LOCN_NBR = oh.LOCN_NBR AND 
                 PRI_START_WK = oh.WK_NBR > 1
                 THEN oh.TTL_UN_QT
            END) AS BEGINNING_OH_LY
        ,SUM(CASE
             WHEN SELECT COUNT(*) FROM VT_STORES 
                  WHERE LOCN_NBR = oh.LOCN_NBR AND 
                  START_WK = oh.WK_NBR > 1
                  THEN oh.TTL_UN_QT
             END) AS BEGINNING_OH
        FROM 
            ...

?

如果VT_STORES 很大,试试:

ALEX_ARP_VIEWS_PRD.FACT_WKLY_OPR_INS oh INNER JOIN 
(select
   LOCN_NBR,
   PRI_START_WK,
   START_WK
 from
   VT_STORES) stores ON 
oh.LOCN_NBR = stores.LOCN_NBR and
(PRI_START_WK = oh.WK_NBR OR
START_WK = oh.WK_NBR)

虽然我已经看到在联接中使用 OR 子句会降低性能,但最好使用两个内部联接,每周一个。

【讨论】:

  • 抱歉,我的问题应该更具体一些 - 大表是 ALEX_ARP_VIEWS_PRD.FACT_WKLY_OPR_INS 表。最初的设计将每个 SUM 操作作为自己独特的查询。
猜你喜欢
  • 2019-03-31
  • 2015-09-17
  • 2013-06-13
  • 2015-09-03
  • 2020-11-29
  • 2020-01-13
  • 2021-03-26
  • 2014-05-15
  • 2021-10-31
相关资源
最近更新 更多