在 SQL 语句中,如何选择所有非特定条件的元素?
Posted
技术标签:
【中文标题】在 SQL 语句中,如何选择所有非特定条件的元素?【英文标题】:In an SQL statement, how do you select all elements that are NOT a certain condition? 【发布时间】:2014-07-28 19:39:29 【问题描述】:很抱歉,我把问题表述得如此糟糕——我是编程新手。
我正在做的是,我在一个小的库管理器应用程序中使用 Java @NamedQuery。我有实体类 Book,其属性为 bookid、title、author 等,Bookstaken 具有属性 id、clientid、bookid、datetaken。所以 Bookstaken.bookid 是引用 Book.bookid 的外键。 Bookstaken 包含所有已被占用的书籍,Book 包含图书馆拥有的所有书籍(但目前不一定拥有)。
我已经通过这个命名查询获取了所有采取的书籍:
@NamedQuery(name = "Taken", query = "SELECT b FROM Book b, Bookstaken t WHERE b = t.bookid")
但是现在我想创建一个名为“未使用”的命名查询,它将检索所有具有...您知道,未使用的书籍。所以本质上是查询“Taken”的互补组。
我试过这个:
@NamedQuery(name = "Not taken", query = "SELECT b FROM Book b, Bookstaken t WHERE b <> t.bookid")
(我尝试将 替换为 !=)
但该声明是给出整组书籍,每一本书,并多次给出确切的组。为什么会这样?我在 SQL 方面从来都不是天才……我该如何解决?
谢谢
【问题讨论】:
【参考方案1】:首先,您必须意识到,您在这里拥有的不是 SQL,而是 JPQL。这是两种不同的语言。
其次,您的查询表明您有一个误解。 BookTaken 实体不应有 bookid 字段。它应该与 Book 有一个 toOne 关联,因此应该有一个 Book 类型的字段。
最后,您的查询不正确。你想要的是所有不存在任何 BookTaken 实体引用这本书的书:
select b from Book b where not exists (
select bt.id from BookTaken bt where bt.bookid = b.id)
【讨论】:
【参考方案2】:首先,您应该确定要比较的是什么。在:
@NamedQuery(... "SELECT b FROM Book b, Bookstaken t WHERE b <> t.bookid")
您正在将 Book
与 id (Bookstaken.bookid
) 进行比较。后者可能是Integer
,因此总是不同于Book
。
根据这个理由,您应该使用:"SELECT b FROM Book b, Bookstaken t WHERE b.bookid != t.bookid"
,因为b != t.bookid
会为所有元素返回true
。
但这可能不是您想要的。正如 cmets 正确指出的那样,上述结果将产生笛卡尔积。你想要的是更具体的 SQL JOIN
,LEFT OUTER JOIN
。
在 JPQL 中,LEFT JOIN
实际上具有 SQL 的 LEFT OUTER JOIN
的语义,你可以直接使用,那么:
@NamedQuery(name = "Not taken", query =
"SELECT b FROM Book b LEFT JOIN Bookstaken t ON b.bookid = t.bookid "+
"WHERE t.bookid IS NULL")
【讨论】:
该查询不会返回所需的结果。它在两个实体之间创建一个交叉连接(笛卡尔积),并保留两个 bookid 不匹配的所有行。因此,它会多次选择所有书籍。 这不会产生笛卡尔积吗?我不熟悉普通sql中的@NamedQuery,正确的方法是左连接为null。喜欢SELECT b.* FROM Book b left join Bookstaken t on b.bookid = t.bookid WHERE t.bookid is null
。我只是好奇。
你们是对的。我必须承认,起初我只关注操作员,而不是 OP 所需的最终结果。我刚刚解决了这个问题,对于未来的读者,它可能会有所帮助,尽管我认为EXISTS
解决方案更简单。感谢您的提醒!【参考方案3】:
您以错误的方式处理查询。您告诉数据库您需要来自Book
和Bookstaken
的所有行对,其中Bookstaken
与Book
不匹配。可以想象,有很多对不匹配的行,所以你得到很多行,这不是你想要的。
您真正想要的是选择Book
中在Bookstaken
中没有对应行的所有行。您可以使用SELECT b from Book WHERE NOT EXISTS( SELECT * from Bookstaken t WHERE t.bookid = b.bookid )
来执行此操作。
或者您可以使用LEFT OUTER JOIN
将Book
s 加入到匹配的Bookstaken
s,但在结果集中保留没有对应Bookstaken
的任何Books
,然后使用@987654333 @ 只获取没有相应Bookstaken
的那些。
【讨论】:
以上是关于在 SQL 语句中,如何选择所有非特定条件的元素?的主要内容,如果未能解决你的问题,请参考以下文章