混合隐式和显式JOIN

时间:2009-04-17 18:06:32

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

我遇到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 clause'中。

问题(S)

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

3 个答案:

答案 0 :(得分:13)

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

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

我在研究Hibernate时请等待,并了解是否可以说服它在所有情况下都使用JOIN


嗯。 Hibernate.org上的所有内容似乎都会重定向到jboss.org。因此,现在无法在线阅读HQL文档。我相信他们最终会找出他们的名字。

答案 1 :(得分:0)

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是什么?像“从员工e1,员工e2中选择e1.name,e2.name,e1.manager.name”这样的东西?

答案 2 :(得分:0)

这可能有点偏离主题,因为它根本不涉及休眠,但来自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