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

发布于 2024-09-01 08:35:17 字数 659 浏览 5 评论 0原文

假设我有两个单独的表,我要观察它们来查询。这两个表都与第三个表有关系。如何使用单个非基于 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 进行简单的左连接,我们最终得到的结果总数等于匹配 CD 的数量乘以匹配书籍的数量。是否有 GROUP BY 或其他编写查询的方式来解决此问题?

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

Suppose I have two separate tables that I watch to query. Both of these tables has a relation with a third table. How can I query both tables with a single, non UNION based query?

Here's a theoretical example. I have a User table. That User can have both CDs and books. I want to find all of that user's books and CDs with a single query matching a string ("awesome" in this example).

A UNION based query might look like this:

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

How can I perform a query like this without the UNION? If I do a simple left join from User to Books and CDs, we end up with a total number of results equal to the number of matching cds timse the number of matching books. Is there a GROUP BY or some other way of writing the query to fix this?

(EDIT: The reason I would like to avoid the Union approach is because this is actually a DQL query and Doctrine does not support UNION. If there's no way to do this without UNION, I'll go the native SQL route. In addition, the real query contains a bunch of additional columns that do not map as nicely against one another in the above example.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

不打扰别人 2024-09-08 08:35:17

想想如何在面向对象的应用程序中对此进行建模。您将创建一个针对书籍和 CD 进行扩展的超类,然后您的用户将拥有一组收藏品。该集合中的任何给定对象要么是一本书,要么是一张 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);

Think of how you'd model this in an OO app. You'd create a superclass that you extend for books and CD's, and your user would then own a set of Collectibles. Any given object in that set is either a book or a CD (or other type of collectible) but it has exactly one of these subtypes.

You can do something similar with SQL, by creating a table corresponding to the supertype:

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

Then each subtype contains a reference to make it collectible:

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)
);

Now you can do your query and be assured you won't get a Cartesian product:

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);
野稚 2024-09-08 08:35:17

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

编辑:要 创建视图,您有两个

选项如果您的查询仅用于选择记录,那么应该没有问题

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)。

If you are trying to avoid union one way is to create view.

EDIT: To create view you have two options

In case your query is only for selecting records there should be no problem with

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

If you need view that can be updated then it might fly if you refactor:

  • create table that will hold all the data AND media type
  • create two views that will split data on media type (since these views are simple 1:1 queries to underlaying tables they should be updatable and you should be able to use them in ORM mapping or other SQL queries)

EDIT2: I forgot to comment on the fact that UNION ALL is a must over UNION unless you want MySQL to start building index on the disk every time you run the view/query (thanks HLGEM).

爱已欠费 2024-09-08 08:35:17

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

Unless there is a compelling reason to not use a UNION, then just use the UNION.

天赋异禀 2024-09-08 08:35:17

也许你可以尝试这样的事情......该示例假设第三个表称为 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();

Maybe you could try something like this... the example assumes the third table is called 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();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文