如何在不使用UNION的情况下在相关表之间执行此查询?

时间:2010-05-11 20:09:41

标签: sql doctrine union

假设我有两个单独的表供我查询。这两个表都与第三个表有关系。如何使用单个非UNION查询查询这两个表?

这是一个理论上的例子。我有一个用户表。该用户可以同时拥有CD和书籍。我希望通过匹配字符串的单个查询找到所有用户的书籍和CD(在此示例中为“awesome”)。

基于UNION的查询可能如下所示:

SELECT "book" AS model, name, ranking 
 FROM book 
WHERE name LIKE 'Awesome%' 
UNION 
SELECT "cd" AS model, name, ranking 
  FROM cd 
 WHERE name LIKE 'Awesome%' 
ORDER BY ranking DESC

如何在没有UNION的情况下执行这样的查询?如果我从用户到书籍和CD进行简单的左连接,我们最终得到的结果总数等于匹配书籍的匹配数量。是否有GROUP BY或其他一些编写查询的方法来修复此问题?

(编辑:我想避免使用Union方法的原因是因为这实际上是一个DQL查询而且Doctrine不支持UNION。如果没有UNION没有办法做到这一点,我将使用本机SQL路由。此外,真实查询包含一堆额外的列,在上面的示例中,这些列不能很好地相互映射。)

4 个答案:

答案 0 :(得分:5)

想想你如何在OO应用程序中对此进行建模。您将为书籍和CD创建一个超类,然后您的用户将拥有一组Collectibles。该集合中的任何给定对象都是书籍或CD(或其他类型的收藏品),但它只有这些子类型中的一种。

您可以通过创建与超类型相对应的来执行与SQL类似的操作:

CREATE TABLE Collectibles (
  collectible_id SERIAL PRIMARY KEY,
  user_id        INT NOT NULL,
  FOREIGN KEY (user_id) REFERENCES Users(user_id)
);

然后每个子类型都包含一个引用它以使其可收集:

CREATE TABLE Books (
  book_id   BIGINT UNSIGNED PRIMARY KEY
  book_name VARCHAR(100) NOT NULL,
  FOREIGN KEY (book_id) REFERENCES Collectibles(collectible_id)
);

CREATE TABLE CDs (
  cd_id   BIGINT UNSIGNED PRIMARY KEY
  cd_name VARCHAR(100) NOT NULL,
  FOREIGN KEY (cd_id) REFERENCES Collectibles(collectible_id)
);

现在您可以进行查询,并确保您不会获得笛卡尔积:

SELECT u.*, COALESCE(b.book_name, d.cd_name) AS media_name
FROM Users u
JOIN Collectibles c ON (u.user_id = c.user_id)
LEFT OUTER JOIN Books b ON (b.book_id = c.collectible_id)
LEFT OUTER JOIN CDs d ON (d.cd_id = c.collectible_id);

答案 1 :(得分:3)

如果你试图避免联合,一种方法是创建视图。

编辑:要create view你有两个选择

如果您的查询仅用于选择记录,则

应该没有问题
CREATE VIEW media AS
SELECT "book" AS model, name, ranking 
 FROM book 
WHERE name LIKE 'Awesome%' 
UNION
SELECT "cd" AS model, name, ranking 
  FROM cd 
 WHERE name LIKE 'Awesome%' 
ORDER BY ranking DESC

如果您需要可以更新的视图,那么如果您重构,它可能会飞:

  • 创建将保存所有数据和媒体类型的表
  • 创建两个将在媒体类型上拆分数据的视图(因为这些视图是对底层表的简单1:1查询,它们应该是可更新的,您应该能够在ORM映射或其他SQL查询中使用它们)

EDIT2:我忘了评论UNION ALL必须超过UNION,除非你希望MySQL每次运行视图/查询时都开始在磁盘上构建索引(感谢HLGEM)。

答案 2 :(得分:0)

除非有令人信服的理由不使用UNION,否则只需使用UNION。

答案 3 :(得分:0)

也许你可以试试这样的东西......这个例子假设第三个表叫做User:

$q = Doctrine_Query::create()
  ->select('c.cd, b.book')
  ->from('User u')
  ->LeftJoin('Cd c ON u.user_id = c.user_id AND c.name LIKE ?, 'Awesome%')
  ->LeftJoin('Book b ON u.user_id = b.user_id AND b.name LIKE ?, 'Awesome%');
$result = $q->execute();