6

我有一个讨厌的 SQL 查询问题,我希望能提供一个优雅的解决方案。我试图避免 32 个左外连接到同一个表。

数据库是 Teradata。

我有一个包含 1400 万条记录和 33 列的表。主键(我们称它为 Trans_Id)和 32 个编码字段(我们称它们为 encoded_1 ... encoded_32)。像这样的东西:

CREATE SET TABLE BigTable ,NO FALLBACK , NO BEFORE JOURNAL, NO AFTER JOURNAL, CHECKSUM = DEFAULT
     (
      TRANS_ID VARCHAR(20) CHARACTER SET LATIN NOT CASESPECIFIC,
      ENCODED_1 VARCHAR(10) CHARACTER SET LATIN NOT CASESPECIFIC,
      ENCODED_2 VARCHAR(10) CHARACTER SET LATIN NOT CASESPECIFIC,
      ENCODED_3 VARCHAR(10) CHARACTER SET LATIN NOT CASESPECIFIC,
      ...
      ENCODED_32 VARCHAR(10) CHARACTER SET LATIN NOT CASESPECIFIC )
PRIMARY INDEX ( TRANS_ID );

我还有一个带有编码/解码值的表。假设此表中有 100 条记录。

CREATE SET TABLE LookupTable ,NO FALLBACK , NO BEFORE JOURNAL, NO AFTER JOURNAL, CHECKSUM = DEFAULT
     (
      UNIQ_PK { just random numbers }
      ENCODED_VAR VARCHAR(10) CHARACTER SET LATIN NOT CASESPECIFIC,
      DECODED_DESC VARCHAR(50) CHARACTER SET LATIN NOT CASESPECIFIC)
PRIMARY INDEX ( UNIQ_PK );

我想避免像这样讨厌的连接(我使用省略号而不是显示所有 32 个外连接):

SELECT 
TRANS_ID
, a.ENCODED_1
, b1.DECODED_DESC DECODED_DESC_1
, a.ENCODED_2
, b2.DECODED_DESC DECODED_DESC_2
...
, a.ENCODED_31
, b31.DECODED_DESC DECODED_DESC_31
, a.ENCODED_32
, b32.DECODED_DESC DECODED_DESC_32
FROM BigTable a
LEFT OUTER JOIN LookupTable b1 ON a.ENCODED_1 = b1.ENCODED
LEFT OUTER JOIN LookupTable b2 ON a.ENCODED_2 = b1.ENCODED
...
LEFT OUTER JOIN LookupTable b31 ON a.ENCODED_31 = b31.ENCODED
LEFT OUTER JOIN LookupTable b32 ON a.ENCODED_32 = b32.ENCODED

任何帮助,将不胜感激。我有一种感觉,外部加入 14M 记录 32 次不是这样做的有效方法!

4

6 回答 6

3

您可以创建一个函数,将 VARCHAR(10) encoded_var 作为参数并返回 VARCHAR(50) decoded_desc,然后您的选择将是这样的:

SELECT TRANS_ID,
     ENCODED_1, somefunc(ENCODED_1) AS DECODED_DESC_1,
     ENCODED_2, somefunc(ENCODED_2) AS DECODED_DESC_2,
     etc.

根据您计划一次返回的行数,这将是可行的。

于 2011-05-12T06:27:52.523 回答
2

如果 encode_1、encoded_2 等都被用作同一张表的查找键,听起来它们都是“相同的想法”。但我的第一个想法是,在这种情况下,更好的设计是:

big_table (trans_id, var_id, encoded_var)
lookup_table (encoded_var, decoded_desc)

然后查询就变成了:

select trans_id, var_id, encoded_var, decoded_desc
from big_table
join lookup_table on lookup_table.encoded_var=big_table.encoded_var

我不知道这是否是真实的字段名称,或者您是否只是想省略不相关的细节。您可能会在此处遗漏相关详细信息。编码_1 和编码_2 等有什么区别?如果它们是可互换的,则没有理由为它们设置单独的字段。确实,它会导致很多问题。即使存在语义差异,如果它们都使用相同的查找表,它们必须都来自同一个域。

例如,几年前,我开发了一个系统来管理我们组织制作和使用的技术手册。每本手册有 3 名经理。(处理预算和时间表的行政经理,跟踪谁需要副本并确保他们得到副本的库存经理,以及负责实际文本的内容经理。)但他们都来自同一个人员名单,通常,同一个人可能拥有多个角色,或者在不同的手册中可能拥有不同的角色。因此,我们制作了一个包含 id、姓名、电子邮件地址等的“人员”表,然后在基本的手动记录中,我创建了 3 列,用于每种经理类型。

这是一个巨大的错误。我应该做的是创建一个单独的表,其中包含手动 ID、经理类型 ID 和人员 ID,然后为 3 种经理类型创建 3 个记录,而不是在一个记录中包含 3 个字段。

为什么?使用三列时,我遇到了您描述的相同问题,尽管规模较小:我不得不从手动表连接到人员表三次。诸如“Bob Smith 负责哪些书?”之类的查询。需要一个令人惊讶的复杂查询,例如

select ... whatever ...
from manual
join person p1 on p1.person_id=manual.admin_id
join person p2 on p2.person_id=manual.stockmanager_id
join person p3 on p3.person_id=manual.contentmanager_id
where p1.name='Bob Smith'
 or p2.name='Bob Smith'
 or p3.name='Bob Smith'

如果只有一列,那就很简单

select ... whatever ...
from manual
join manual_manager on manual_manager.manual_id=manual.manual_id
join person on person.person_id=manual_manager.person_id
where person.name='Bob Smith'"

由于所有的重复,有几次程序员不小心只检查了 2 个字段而不是全部 3 个字段,这并不奇怪。对于 1 个字段,这个错误是不可能的。对于 3 个字段,如果我们添加第 4 种类型的经理,我们将不得不添加另一列,然后更改查看这些字段的每个查询。对于 1 个字段,我们可能不会。等等。

对于 3 个字段,我们需要 3 个索引,并且还有其他性能影响。

我怀疑同样的想法也适用于你。

如果您的 32 个字段都是完全可互换的,那么该表只需要一个序列号即可生成唯一的 pk。如果它们之间存在一些差异,那么您可以创建一个代码来区分它们。

于 2011-05-12T06:18:53.580 回答
1

对于初学者,我会将 LookUp 表上的 PI 更改为 Encoded_Var。您已经必须在每个 Encoded_Var 列上重新分配大表才能加入 LookUp 表。为什么还要每次都重新分配 LookUp 表。

你的桌子设计不是更接近的东西有什么原因吗?

CREATE SET TABLE BigTable ,NO FALLBACK , NO BEFORE JOURNAL, NO AFTER JOURNAL, CHECKSUM = DEFAULT
     (
      TRANS_ID    VARCHAR(20) CHARACTER SET LATIN NOT CASESPECIFIC NOT NULL,
      ENCODED_VAR VARCHAR(10) CHARACTER SET LATIN NOT CASESPECIFIC NOT NULL
UNIQUE PRIMARY INDEX ( TRANS_ID, ENCODED_VAR );

这将在 trans_id 和 encoded_var 之间建立更合适的 1:M 关系。除非有相关的细节被遗漏来解释为什么这不起作用。事实上,如有必要,您可以将此表构建为关系表,并拥有另一个表,如下所示:

    CREATE SET TABLE BigTable2 ,NO FALLBACK , NO BEFORE JOURNAL, NO AFTER JOURNAL, CHECKSUM = DEFAULT
         (
          TRANS_ID    VARCHAR(20) CHARACTER SET LATIN NOT CASESPECIFIC NOT NULL,
          OtherData1  VARCHAR(10) CHARACTER SET LATIN NOT CASESPECIFIC NOT NULL,
          OtherData2  SMALLINT NOT NULL,
          ....,
          OtherDataN  VARCHAR(10) CHARACTER SET LATIN NOT CASESPECIFIC NOT NULL
   UNIQUE PRIMARY INDEX ( TRANS_ID );

希望有帮助。

于 2011-05-12T14:15:21.140 回答
1

如果您不想重复编写相同的查询,我建议将其放在视图中。

对于性能,我建议如下:

  • 正如 Rob Paller 建议的那样,将 LookupTable 上的主索引更改为 ENCODED_VAR。
  • 在 LookupTable 中,使用 DECODED_DESC = null 和 ENCODED_VAR = 一些您永远不会使用的值添​​加新记录。更新 BigTable 以将所有 null ENCODED_* 替换为此值。然后,您可以更改查询以使用所有内部联接并获得相同的结果。
于 2011-05-13T06:15:05.370 回答
0

我在 Teradata 上也遇到过同样的问题。一位同事告诉我,使用单个 LEFT OUTER JOIN 和许多 CASE 语句为您提供了一个优雅的解决方案。

但是,您的示例有点令人困惑,因为您要加入一个不存在的列(“LookupTable”中的“ENCODED”列,我认为应该是“ENCODED_VAR”?)。

SELECT     TRANS_ID    
           , a.ENCODED_1    
           , MAX(CASE WHEN b.ENCODED_VAR = a.ENCODED_1
                 THEN b.DECODED_DESC
                 ELSE NULL
             END) DECODED_DESC_1    
           , a.ENCODED_2    
           , MAX(CASE WHEN b.ENCODED_VAR = a.ENCODED_2
                 THEN b.DECODED_DESC
                 ELSE NULL
             END) DECODED_DESC_2    
           ...    
           , a.ENCODED_31    
           , MAX(CASE WHEN b.ENCODED_VAR = a.ENCODED_31
                 THEN b.DECODED_DESC
                 ELSE NULL
             END) DECODED_DESC_31    
           , a.ENCODED_32    
           , MAX(CASE WHEN b.ENCODED_VAR = a.ENCODED_32
                 THEN b.DECODED_DESC
                 ELSE NULL
             END) DECODED_DESC_32    
FROM BigTable a
LEFT OUTER JOIN LookupTable b 
    ON a.ENCODED_1 = b.ENCODED_VAR
    OR a.ENCODED_2 = b.ENCODED_VAR
    ...
    OR a.ENCODED_31 = b.ENCODED_VAR
    OR a.ENCODED_32 = b.ENCODED_VAR
GROUP BY a.TRANS_ID 

这确实依赖于 BigTable 中的 ENCODED_n 和 LookupTable 中的 ENCODED_VAR 之间存在 1:1 的关系。

此外,顺便说一句,您不应在 Teradata 表中使用随机数作为主索引。虽然这将为您提供出色的表分布,但在进行表查找时它将完全没用。如果您为 PI 使用公共连接字段,则数据库可以直接转到存储数据的 AMP。但是,如果没有这个,DBMS 必须每次都进行全表扫描。只要分布仍然合理,您可能会使用 ENCODED_VAR 作为您的 PRIMARY INDEX 并看到性能大大提高。

我希望这行得通。我认为这将适用于您的情况。我还没有验证我的代码是否正确,但它与我自己的解决方案非常相似。

于 2011-07-29T01:56:14.487 回答
0

你不能这样:

SELECT 
TRANS_ID
, a.ENCODED_1
, CASE a.ENCODED_1 WHEN b.ENCODED THEN b.DECODED_DESC END DECODED_DESC_1
, a.ENCODED_2
, CASE a.ENCODED_2 WHEN b.ENCODED THEN b.DECODED_DESC END DECODED_DESC_2
...
, a.ENCODED_31
, CASE a.ENCODED_31 WHEN b.ENCODED THEN b.DECODED_DESC END DECODED_DESC_31
, a.ENCODED_32
, CASE a.ENCODED_32 WHEN b.ENCODED THEN b.DECODED_DESC END DECODED_DESC_32
FROM BigTable a
 LEFT JOIN LookupTable b ON (
   a.ENCODED_1 = b.ENCODED OR
   a.ENCODED_2 = b.ENCODED OR
   ...
   a.ENCODED_31 = b.ENCODED OR
   a.ENCODED_32 = b.ENCODED
 )

?

我也可能想重写加入条件如下:

...ON b.ENCODED IN (a.ENCODED_1, a.ENCODED_2, ... a.ENCODED_31, a.ENCODED_32)

但是我不确定它是否不能比以前的版本慢。实际上,我的猜测是,它确实会变慢,但我仍然会确认这一点。

于 2011-05-13T05:09:20.143 回答