【问题标题】:Mixing implicit and explicit JOINs混合隐式和显式 JOIN
【发布时间】:2010-10-20 04:18:07
【问题描述】:

我遇到了 Hibernate 生成无效 SQL 的问题。具体来说,混合和匹配隐式和显式连接。这似乎是open bug

但是,我不确定为什么这是无效的 SQL。我想出了一个生成相同语法异常的小玩具示例。

架构

CREATE TABLE Employee (
    employeeID INT,
    name VARCHAR(255),
    managerEmployeeID INT   
)

数据

INSERT INTO Employee (employeeID, name) VALUES (1, 'Gary')
INSERT INTO Employee (employeeID, name, managerEmployeeID) VALUES (2, 'Bob', 1)

工作 SQL

这两个查询都有效。我意识到有一个笛卡尔积;这是故意的。

显式连接:

SELECT e1.name,
       e2.name,
       e1Manager.name
  FROM Employee e1
 CROSS JOIN Employee e2
 INNER JOIN Employee e1Manager
    ON e1.managerEmployeeID = e1Manager.employeeID

隐式连接:

SELECT e1.name,
       e2.name,
       e1Manager.name
  FROM Employee e1,
       Employee e2,
       Employee e1Manager
 WHERE e1.managerEmployeeID = e1Manager.employeeID

无效的 SQL

此查询不适用于 MSSQL 2000/2008 或 MySQL:

SELECT e1.name, 
       e2.name, 
       e1Manager.name
  FROM Employee e1,
       Employee e2
 INNER JOIN Employee e1Manager 
    ON e1.managerEmployeeID = e1Manager.employeeID

在 MS2000 中,我收到错误:

列前缀“e1”不匹配 使用了表名或别名 在查询中。

在 MySQL 中,错误是:

未知列“e1.managerEmployeeID” 在'on 子句'中。

问题

  1. 为什么这个语法无效?
  2. 奖励:有没有办法强制 Hibernate 只使用显式 JOIN?

【问题讨论】:

标签: sql hibernate join cartesian-product cross-join


【解决方案1】:

这会导致错误,因为根据 SQL 标准,JOIN 关键字的优先级高于逗号。关键点是表别名在之后FROM 子句中评估了相应的表之后才可用。

因此,当您在 JOIN...ON 表达式中引用 e1 时,e1 尚不存在。

在我研究 Hibernate 时请稍候,看看你能否说服它在所有情况下都使用 JOIN


嗯。 Hibernate.org 上的所有内容似乎都重定向到了 jboss.org。所以现在无法在线阅读 HQL 文档。我相信他们最终会弄清楚他们的名字。

【讨论】:

    【解决方案2】:

    这可能有点跑题了,因为它根本不涉及休眠,但来自Bill Karwin 的评论真的让我大开眼界。您需要先进行显式连接,而不是先编写隐式连接。如果您有多个隐式连接,则此语法特别有趣。

    在 MS SQL 中检查以下示例。并非所有联系人都定义了国家代码,但所有联系人都有一个属性 val,该属性将在表 Tbl 中查找。所以直观的解决方案行不通:

    SELECT * FROM 
    contacts, Tbl
    LEFT OUTER JOIN country ON CtryCod = country.CtryCod 
    WHERE val = Tbl.val
    

    您可能更愿意使用以下语法:

    SELECT * FROM 
    contacts LEFT OUTER JOIN country ON CtryCod = country.CtryCod, 
    Tbl
    WHERE val = Tbl.val
    

    【讨论】:

      【解决方案3】:

      PostgreSQL 也报错:

      ERROR:  invalid reference to FROM-clause entry for table "e1"
      LINE 7:     ON e1.managerEmployeeID = e1Manager.employeeID;
                     ^
      HINT:  There is an entry for table "e1", but it cannot be referenced from this part of the query.
      

      我认为问题在于,当您连接两个表 a 和 b(在本例中为 e2 和 e1Manager)时,您只能在“ON”子句中引用这两个表。所以你可以在这个 ON 子句中引用 e2 和 e1Manager,但不能引用 e1。

      我认为这可以扩展,如果您有一个“JOIN”语句链,您可以在“ON”子句中引用同一链中的其他表,但不能跨越“,”。所以像 `a JOIN b ON a.a_id = b.a_id JOIN c ON c.b_id = b.b_id AND c.a_id = a.a_id" 是允许的。

      您用于生成此 SQL 的 HQL 是什么?类似“select e1.name, e2.name, e1.manager.name from Employee e1, Employee e2”?

      【讨论】:

      • SELECT a.id, a.category.id, a.category.name, a.owner FROM Candidate a,其中 category 和 owner 是关联。真正重要的是,如果您将所有者放在首位,它会起作用,因为幸运地重新排序了 JOIN。
      • 基于问题的快速重现,您可能希望尝试不混合选择实体和属性:选择 a.id、a.category、a.owner 或 a.id、a.category .id、a.category.name、a.owner.id——这两种选择都为我生成了有效的 SQL(但你的 HQL 生成了无效的 SQL,正如你所说)对于我自己,我通常只获取候选对象并让 Hibernate根据需要自动加载类别和所有者。或者使用“left join fetch”急切地获取它们。
      猜你喜欢
      • 2013-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-11
      • 2020-06-30
      • 2016-02-27
      相关资源
      最近更新 更多