【问题标题】:Hive: work around for non equi left joinHive:解决非 equi left join
【发布时间】:2014-04-16 14:27:18
【问题描述】:

Hive 不支持非 equi 连接:常见的解决方法是将连接条件移动到 where 子句,当您需要内部连接时它可以正常工作。但是左连接呢?

人为的例子。假设我们有一个 orderLineItem 表,我们需要加入一个 ProductPrice 表,该表具有产品 ID、价格和价格适用的日期范围。我们想加入这个,其中 ProductID=ProductID & OrderDate 在开始日期和结束日期之间。如果 productID 或有效日期范围不匹配,我仍然希望查看所有 orderLineItems。

这个 SQL 小提琴是我们如何在 MSSQL 中执行此操作的一个示例: http://sqlfiddle.com/#!6/fb877/7

问题 如果我应用典型的解决方法,并将非 equi 过滤器移动到 where 子句,它就会变成内部连接。在上述情况下,在 sql fiddle & 下面,我有一个不在查找中的产品 ID。

问题: 如果 hive 不支持非 eqi-joins,如何实现左非 eqi?

[SQLFiddle 内容]

表格:

CREATE TABLE OrderLineItem(
  LineItemIDId int IDENTITY(1,1),
  OrderID int  NOT NULL,
  ProductID int NOT NULL,
  OrderDate Date
);


CREATE TABLE ProductPrice(
  ProductID int,
  Cost float,
  startDate  Date,
  EndDate  Date


);

加载数据以及我们如何加入 MSSQL:

--Old Price. Should be ignored
INSERT INTO ProductPrice(ProductID, COST,startDate,EndDate) VALUES  (1, 50,'12/1/2012','1/1/2013');
INSERT INTO ProductPrice(ProductID, COST,startDate,EndDate) VALUES (2, 55,'12/1/2012','1/1/2013');

--Price for Order 2. Should be applied to Order 1
INSERT INTO ProductPrice (ProductID, COST,startDate,EndDate) VALUES(1, 20,'12/1/2013','1/1/2014');
INSERT INTO ProductPrice (ProductID, COST,startDate,EndDate) VALUES(2, 25,'12/1/2013','1/1/2014');

--Price for Order 2. Should be applied to Order 2
INSERT INTO ProductPrice (ProductID, COST,startDate,EndDate) VALUES(1, 15,'1/2/2014','3/1/2014');
INSERT INTO ProductPrice (ProductID, COST,startDate,EndDate) VALUES(2, 20,'1/2/2014','3/1/2014');


--January 1st 2014 Order
INSERT INTO OrderLineItem(OrderID,ProductID,OrderDate) VALUES (1, 1,'1/1/2014') ;
INSERT INTO OrderLineItem(OrderID,ProductID,OrderDate) VALUES (1, 2,'1/1/2014');

--Feb 1st 2014 Order
INSERT INTO OrderLineItem(OrderID,ProductID,OrderDate) VALUES (2, 1,'2/1/2014');
INSERT INTO OrderLineItem (OrderID,ProductID,OrderDate) VALUES(2, 2,'2/1/2014');
INSERT INTO OrderLineItem (OrderID,ProductID,OrderDate) VALUES(2, 3,'2/1/2014'); -- no price

SELECT * FROM OrderLineItem;

SELECT * FROM OrderLineItem li LEFT OUTER JOIN  ProductPrice p on
p.ProductID=li.ProductID AND  OrderDate BETWEEN  startDate AND  EndDate;

【问题讨论】:

    标签: join hive left-join


    【解决方案1】:

    不确定是否可以避免使用双重连接:

    SELECT * 
    FROM OrderLineItem li 
    LEFT OUTER JOIN  (
      SELECT p.*
      FROM ProductPrice p
      JOIN OrderLineItem li 
      ON p.ProductID=li.ProductID 
      WHERE OrderDate BETWEEN StartDate AND EndDate ) p
    ON p.ProductId = li.ProductID
    WHERE StartDate IS NULL OR 
      OrderDate BETWEEN StartDate AND EndDate;
    

    这样,如果存在匹配且 StartDate 不为空,则必须存在有效​​的开始/结束日期匹配。

    【讨论】:

    • 这应该是公认的答案。它通过保留与产品 ID 匹配但订单日期在开始和结束日期窗口之外的行来保留真正的左连接(这应该在右表中产生空条件)。它也比序列行号解决方案更高效。
    【解决方案2】:
    1. 使用添加的序列行号创建左表的副本:

      CREATE TABLE OrderLineItem_serial AS
      SELECT ROW_NUMBER() OVER() AS serial, * FROM OrderLineItem;
      

      备注:这可能对某些表格格式更有效(必须是 WITHOUT COMPRESSION):

      CONCAT(INPUT__FILE__NAME, BLOCK__OFFSET__INSIDE__FILE) AS serial
      
    2. 做一个内连接:

      CREATE TABLE OrderLineItem_inner AS
      SELECT * FROM OrderLineItem_serial li JOIN ProductPrice p
      on p.ProductID = li.ProductID WHERE OrderDate BETWEEN startDate AND EndDate;
      
    3. 序列号左连接:

      SELECT * FROM OrderLineItem_serial li
      LEFT OUTER JOIN OrderLineItem_inner i on li.serial = i.serial;
      

    【讨论】:

    • 抱歉耽搁了——我有几个问题:我在 Hive .10 上——没有窗口函数。连接中使用的第二个查询,我的配置单元版本不支持(我原来的问题)。不过谢谢!
    • 1.尝试使用 concat...as serial 而不是 row_number...as serial,如第 1 项下面的备注中所述 2. 我更改了第二个查询。因为它是一个内部连接,你可以将 between 条件放在 where 子句中
    【解决方案3】:

    Hive 0.10 支持交叉连接,因此您可以在 WHERE 子句中处理所有“theta join”(非等连接)条件。

    【讨论】:

      【解决方案4】:

      为什么不使用单独允许 NULL 情况的 WHERE 子句?

      SELECT * FROM OrderLineItem li 
      LEFT OUTER JOIN  ProductPrice p 
      ON p.ProductID=li.ProductID 
      WHERE ( StartDate IS NULL OR OrderDate BETWEEN startDate AND EndDate);
      

      这应该处理它 - 如果左连接匹配,它将使用日期逻辑,如果不匹配,它将保持 NULL 值不变,因为左连接应该。

      【讨论】:

      • 如果产品 id 匹配但所有匹配的开始/结束范围都在订单日期之外,在这种情况下,您将过滤掉该行,从而转换理论外部加入一个可以输出比 OrderLineItem 少的行的查询
      • 继@fd8s0 所指出的,这不是真正的左连接,可能会导致左表中的行数减少。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-07
      • 2015-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-10
      相关资源
      最近更新 更多