0

我正在运行一个 SQL 查询,它自连接同一个表 24 次,以便根据 24 个不同的条件“查找”表的特定行,以便我可以在计算中使用所有这些值。虽然性能非常好(表已编入索引且连接条件受到限制),但我不禁觉得这里有一股代码味道。

有没有更好的方法在 SQL 中进行查找?

(抱歉没有包括一个例子;我希望我已经以一般的方式表达了这个问题)。

编辑:无论如何尝试一个例子:

CREATE TABLE key (
    pk1 int,
    pk2 int,
    pk3 int,
    PRIMARY KEY (pk1, pk2, pk3)
);

CREATE TABLE values (
    pk1 int,
    pk2 int,
    pk3 int,
    pk4 int,
    pk5 int,
    value int,
    PRIMARY KEY (pk1, pk2, pk3, pk4, pk5)
 );

 SELECT k.pk1, k.pk2, k.pk3,
        v1.value + v2.value - v3.value * (v4.value / v5.value) + ... + v24.value as result
 FROM key k
     LEFT JOIN values v1
         on v1.pk1=k.pk1
         and v1.pk2=k.pk2
         and v1.pk3=k.pk3
         and v1.pk4=100
         and v1.pk5=200

     LEFT JOIN values v2
         on v2.pk1=k.pk1
         and v2.pk2=k.pk2
         and v2.pk3=k.pk3
         and v2.pk4=400
         and v2.pk5=800

     ...

     LEFT JOIN values v24
         on v24.pk1=k.pk1
         and v24.pk2=k.pk2
         and v24.pk3=k.pk3
         and v24.pk4=900
         and v24.pk5=700;

编辑2:这种结构的原因是值表代表(从数学上讲)5个变量的函数,预先计算的返回值存储在表中,用于各种参数。

4

6 回答 6

2

首先,这根本不是自加入。

自联接是指将表与自身联接。

这方面的例子是层次结构中的父子关系以及与其他人有关系的人(字面意思是父母,孩子)。

您在不同角色中使用表的情况并不少见。

如果表中的不同值在某种基本性质上不相关,那么作为“一个真正的查找”的情况,我会遇到设计问题,在这种情况下,可以使用类型代码存储各种实体查找 - 所以您可以在同一个查找表中获得帐单地址、客户、送货地址、产品和各种各样的东西。

在数据仓库中,也可以在不同的角色中使用维度,特别是日期或时间维度。

如果对于用作数组的列(例如 first_child、second-child、third_child)一遍又一遍地连接同一个查找表,则会产生异味,因为这通常违反规范化。

我对您在此处显示的内容的唯一担忧是:

似乎用于在所有值的 5 维空间中选择一个 3 维空间的幻数。我假设这些本身是在某处的表中定义的(pk4、pk5、描述)。

那时我会考虑将每一个都变成一个视图,以使其更具可读性。

在 SQL Server(或具有相同结构的 DB2)中,我实际上会考虑使用在 pk4 和 pk5 上参数化的内联表值函数,这将有助于防止某人意外加入不完整的联接条件 - 并最终得到一个 ITVF 而不是多个视图。

但这一切都只是简单的清理——查询和表的设计对我来说似乎很合理。

于 2012-06-21T20:20:13.593 回答
1

我已经开始用一个完整的查找表示例来回答这个问题,但我意识到还有一个更大的潜在问题。

查看这些参考资料。

http://en.wikipedia.org/wiki/Entity-attribute-value_model

关系数据库中的键值对

您正在使用的模式类型与关系数据库的想法相反。尝试展平您的表格,这样您就没有键/值关系。

如果您的方程是一个非常规聚合(是的,不要查找那个),例如 v1.val + v2.val / v3.val,那么您希望将该方程中的每个变量都放在一个单一的一个或多个(但少于 24 个)连接后在一个或多个表中的行。

实体-属性-值模式的性能很差,维护困难,而且真的很糟糕。

...

那没有回答问题,所以这里。使用 Icarus 在评论中建议的视图,或者烧掉整个东西并重建一些更规范的东西。

于 2012-06-21T19:52:22.330 回答
1

看到您正在使用 SAS,您可能会使用数据步骤合并对其进行一些清理。类似于以下内容:

data x;
  merge key 
        values(rename=value=value1 where=(pk4=100 and pk5=200))
        values(rename=value=value2 where=(pk4=400 and pk5=800))
        values(rename=value=value3 where=(pk4=900 and pk5=700))
        etc...
        ;
  by pk1 pk2 pk3;
  result = ...;
  keep pk: value: result;
run;

现在我面前没有 SAS,所以我不打算输入所有代码并对其进行测试,但你明白了。对我来说,我认为它看起来比 SQL 干净得多。当 datastep 合并提供比 SQL 方法更好的替代方案时,这是一个非常罕见的发现。

于 2012-06-22T06:47:11.510 回答
0

如果我了解您要查找的内容,则使用起来会更容易:

SELECT field1, field2 from Table1 t
where exists (SELECT 1 from LookupTable l where l.fieldX=t.fieldX) 
于 2012-06-21T19:15:23.707 回答
0

我已将 pk4、pk5 的参数对枚举为 num(我将实际数字替换为 4*6):

CREATE TABLE zparams
        ( num INTEGER
        , pk4 INTEGER
        , pk5 INTEGER
        , PRIMARY KEY (pk4,pk5)
        , CONSTRAINT ze_other UNIQUE (num)
        );
INSERT INTO zparams(num,pk4,pk5) VALUES
 (1,1,1), (2,1,2), (3,1,3), (4,1,4), (5,1,5), (6,1,6)
, (7,2,1), (8,2,2), (9,2,3), (10,2,4), (11,2,5), (12,2,6)
, (13,3,1), (14,3,2), (15,3,3), (16,3,4), (17,3,5), (18,3,6)
, (19,4,1), (20,4,2), (21,4,3), (22,4,4), (23,4,5), (24,4,6)
        ;

CTE 的收益是徒劳的:

EXPLAIN ANALYZE
WITH zzz AS (
        SELECT v.pk1 AS pk1
        , v.pk2 AS pk2
        , v.pk3 AS pk3
        , p.num AS num
        , v.value AS value
        FROM zparams p
        JOIN zvalues v ON v.pk4 = p.pk4 AND v.pk5=p.pk5
        )
 SELECT k.pk1, k.pk2, k.pk3,
        v1.value + v2.value - v3.value * (v4.value / v5.value) + v24.value as result
 FROM zkeys k
     LEFT JOIN zzz v1
         ON v1.pk1=k.pk1 AND v1.pk2=k.pk2 AND v1.pk3=k.pk3
         AND v1.num=1

     LEFT JOIN zzz v2
         ON v2.pk1=k.pk1 AND v2.pk2=k.pk2 AND v2.pk3=k.pk3
         AND v2.num=2

     LEFT JOIN zzz v3 ON v3.pk1=k.pk1 AND v3.pk2=k.pk2 AND v3.pk3=k.pk3
         AND v3.num=3

     LEFT JOIN zzz v4
         ON v4.pk1=k.pk1 AND v4.pk2=k.pk2 AND v4.pk3=k.pk3
         AND v4.num=4

     LEFT JOIN zzz v5
         ON v5.pk1=k.pk1 AND v5.pk2=k.pk2 AND v5.pk3=k.pk3
         AND v5.num=5

     LEFT JOIN zzz v24
         ON v24.pk1=k.pk1 AND v24.pk2=k.pk2 AND v24.pk3=k.pk3
         AND v24.num=24
        ;

而且,在 100K*24 变量(6used)上进行测试,-实际上- CTE 在 {pk1,pk2,pk3,pk4=constant,pk5=constant} 上的表现比普通连接(1 秒)差(4.5 秒)。但是,至少它看起来更干净。

于 2012-06-21T21:40:58.493 回答
0

作为我上述答案的替代方案,您还可以执行以下操作,我相信:

CREATE TABLE XX AS 
SELECT k.pk1, k.pk2, k.pk3, v1.pk4, v1.pk5, v1.value
     FROM key k
 LEFT JOIN values v1
     on v1.pk1=k.pk1
     and v1.pk2=k.pk2
     and v1.pk3=k.pk3
     and ( 
          (v1.pk4=100 and v1.pk5=200) or
          (v1.pk4=400 and v1.pk5=800) or
          (v1.pk4=700 and v1.pk5=900) 
         )


proc transpose data=xx out=trans;
  by pk1 pk2 pk3;
  var value;
run;

data result;
  set trans;
  result = ...;
run;

再说一次,我面前没有 SAS,所以我无法测试它,但我相信你明白了。这样,您只需进行一次连接,然后将其余部分分解为其他步骤。我不太确定这将如何执行,因为它可能不再使用索引。只是抛出一些想法在那里......

于 2012-06-22T07:08:38.967 回答