【问题标题】:sql Two columns of one table references to the same column of the other tablesql 一张表的两列引用另一张表的同一列
【发布时间】:2017-11-30 11:16:45
【问题描述】:

我有两张桌子 table : cost1_id 不为空且 cost2_id 可以为空的产品

   id    | cost1_id | cost2_id
   1     | 1        | 2
   2     | 3        | 

表格:成本

   id    | value    | currency
   1     | 15       | EUR
   2     | 20       | USD
   3     | 100      | TND

我想在不使用 join 的情况下得到这个结果(我宁愿使用 UNION,因为我有很大的数据库)

product_id | cost1_value | cost1_currency | cost2_value | cost2_currency 
     1     | 15          | EUR            |  20         |  USD
     2     | 100         | TND            |             |

【问题讨论】:

  • “我宁愿与 UNION 合作,因为我有大型数据库”这是什么意思? Join 对大型数据库没用?
  • JOINs 是关系数据库的基本组成部分...
  • 我用JOIN处理大表,性能明显下降,但是当我切换到union时,性能得到了提升(我的意思是响应时间更快)
  • 那么您没有正确的索引,或者您的连接混淆了 RDBMS 使用索引的能力。我提供了两个建议,一个使用单个连接,(但使用IN() 会减慢速度),另一个仅使用“传统”连接。我怀疑你不想使用的那个是最快的。至于你遇到的JOIN慢的情况,你已经使用了UNION来加快速度,在另一个问题中给出一个具体的例子并问Why is the non-join版本更快吗? (请记住同时包含 SQL 示例数据。)
  • 请解释“不使用联接(我宁愿使用 UNION,因为我有大型数据库)”。因为它反映了严重的误解。一方面,他们做不同的事情。 UNION 返回某事物或其他事物的行。 JOIN 返回某事和其他事如此的行。

标签: sql postgresql foreign-keys


【解决方案1】:

我能想到的最小化连接的一个选项是......

SELECT
    product.id    AS product_id,
    MAX(CASE WHEN cost.id = product.cost1_id THEN cost.value    END)   AS cost1_value,
    MAX(CASE WHEN cost.id = product.cost1_id THEN cost.currency END)   AS cost1_currency,
    MAX(CASE WHEN cost.id = product.cost2_id THEN cost.value    END)   AS cost2_value,
    MAX(CASE WHEN cost.id = product.cost2_id THEN cost.currency END)   AS cost2_currency
FROM
    product
LEFT JOIN
    cost
        ON cost.id IN (product.cost1_id, product.cost2_id)
GROUP BY
    product.id

也就是说,使用IN() 可能比只加入两次要慢...


确保您的表上有索引。 它们是优化连接的方法,而不是试图避免它们......

  • 在这种情况下,您可能只需要Cost 表中的聚集主键...

因此,我强烈建议您至少 尝试这样做的“正常”方式...

SELECT
    product.id    AS product_id,
    c1.value      AS cost1_value,
    c1.currency   AS cost1_currency,
    c2.value      AS cost2_value,
    c2.currency   AS cost2_currency
FROM
    product
LEFT JOIN
    cost    c1
        ON c1.id = product.cost1_id
LEFT JOIN
    cost    c2
        ON c2.id = product.cost2_id

编辑:

另一个深奥的选择可能是......

SELECT
    product_id,
    MAX(cost1_value)       AS cost1_value,
    MAX(cost1_currency)    AS cost1_currency,
    MAX(cost2_value)       AS cost2_value,
    MAX(cost2_currency)    AS cost2_currency
FROM
(
    SELECT
        product.id                                       AS product_id,
        cost.value                                       AS cost1_value,
        cost.currency                                    AS cost1_currency,
        CASE WHEN 1=0 THEN cost.value    ELSE NULL END   AS cost2_value,
        CASE WHEN 1=0 THEN cost.currency ELSE NULL END   AS cost2_currency
    FROM
        product
    LEFT JOIN
        cost
            ON cost.id = product.cost1_id

    UNION ALL

    SELECT
        product.id                                       AS product_id,
        NULL                                             AS cost1_value,
        NULL                                             AS cost1_currency,
        cost.value                                       AS cost2_value,
        cost.currency                                    AS cost2_currency
    FROM
        product
    INNER JOIN
        cost
            ON cost.id = product.cost2_id
)
    unioned
GROUP BY
    product_id

然后创建这些索引...

CREATE INDEX ix_product_cost1 ON product(cost1_id, id);
CREATE INDEX ix_product_cost2 ON product(cost2_id, id);

可能会稍微快一些,但代价是显着增加了复杂性,将成为维护以后头疼。

【讨论】:

    【解决方案2】:

    试试这个答案,这个答案是给SQL Server的。在PostgreSQL 中尝试相同的方法。

    CREATE table #Product(ID INT,Cost1_id INT,Cost2_id INT)
    INSERT INTO #Product VALUES(1,1,1)
    INSERT INTO #Product VALUES(2,3,0)
    
    CREATE table #Cost(ID INT,Value INT,currency VARCHAR(10))
    INSERT INTO #Cost VALUES(1,15,'EUR')
    INSERT INTO #Cost VALUES(2,20,'USD')
    INSERT INTO #Cost VALUES(3,100,'TND')
    
    SELECT ID product_id,MAX(cost1_value)cost1_value,MAX(cost1_Currency)cost1_Currency
            ,MAX(cost2_value)cost2_value,MAX(cost2_Currency)cost2_Currency
    FROM(
        SELECT P.ID,C.Value cost1_value,C.Currency cost1_Currency,0 AS cost2_value,'' AS cost2_Currency
        from #Cost C, #Product P
        WHERE P.Cost1_id=C.Id
        UNION ALL
        SELECT P.ID,0 AS cost2_value,'' AS cost2_Currency,C.Value ,C.Currency 
        from #Cost C, #Product P
        WHERE P.Cost2_id=C.Id
        )D
    GROUP BY ID
    

    希望对你有所帮助。

    【讨论】:

    • 请,请,请不要不必要地教人们使用 ANSI-84 表示法进行连接。使用 JOIN 关键字已成为 20 多年的标准做法,而教人们使用 , 是对他们的一种伤害。此外,您还需要一个LEFT JOIN,因为cost2_id 中的一些条目是NULLs。而且真的不要为此开始教授 (+) 的使用,它在 SQL Server 中已正式弃用。
    • 是的,我这样做只是为了,他说他不能使用联接。否则,这应该是一个不好的做法。
    • 使用, IS 使用连接。 假装你不是的只是糟糕的语法。这是CROSS JOIN
    • @MatBailie , 是交叉连接,其优先级低于显式连接。 , 因为问题是一个城市神话。 on 是外连接所必需的。人们声称 , 会导致比内部连接更多的遗忘条件。如果您检查差异,那就是内部连接要求您每个连接至少有 1 个条件 - 仍然不是正确数量的条件,更不用说正确的条件了。此外,它依赖于忽略条件是一个问题的假设,就好像人们没有测试他们的代码实际上做了什么特别的事情。 (如果他们不这样做,那是他们的问题。)
    • @philipxy - 在没有格式的情况下将所有代码写在一行上在技术上也是可以接受的。出于完全相同的原因,这也是糟糕的工程。无论哪种情况,我都不允许任何人的代码通过审查。可读性和易维护性不是被忽视的因素,因为测试框架应该能发现问题。我很高兴你没有以这种态度为我工作。
    猜你喜欢
    • 1970-01-01
    • 2019-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-20
    • 1970-01-01
    • 1970-01-01
    • 2010-12-09
    相关资源
    最近更新 更多