简体   繁体   中英

In an SQL statement, how do you select all elements that are NOT a certain condition?

I'm sorry I formulated the question so badly--I'm new to programming.

What I'm doing is, I'm using Java @NamedQuery in a little Library manager application. I have the entity class Book with attributes bookid, title, author, etc. and Bookstaken with attributes id, clientid, bookid, datetaken. So Bookstaken.bookid is the foreign key that refers to Book.bookid. Bookstaken contains all the books that have been taken, and Book contains all the books that the library owns (but does not necessarily have at the moment).

I have fetched all Books that have been taken with this Named Query:

@NamedQuery(name = "Taken", query = "SELECT b FROM Book b, Bookstaken t WHERE b = t.bookid")

But now I want to make a Named Query called "Not taken" which will retrieve all books that have... you know, not been taken. So essentially the complementary group of the query "Taken".

I have tried this:

@NamedQuery(name = "Not taken", query = "SELECT b FROM Book b, Bookstaken t WHERE b <> t.bookid")

(and I've tried replacing <> with !=)

But that statement is giving the entire group of Books, every single one of them, and giving that exact group multiple times. Why is this happening? I was never talented at SQL... And how can I fix it?

Thanks

First off, you should be certain of what you are comparing. In:

@NamedQuery(... "SELECT b FROM Book b, Bookstaken t WHERE b <> t.bookid")

You are comparing a Book with an id ( Bookstaken.bookid ). The latter probably is an Integer , thus always different from a Book .

By that rationale, you should use: "SELECT b FROM Book b, Bookstaken t WHERE b.bookid != t.bookid" , as b != t.bookid would return true for all elements.


But that would probably not be just what you want. As rightfully pointed out in the comments, the above would result in a cartesian product. What you want is more along the lines of an SQL JOIN , a LEFT OUTER JOIN , to be more specific.

As in JPQL, LEFT JOIN has actually the semantics of SQL's LEFT OUTER JOIN , you could just use, then:

@NamedQuery(name = "Not taken", query = 
"SELECT b FROM Book b LEFT JOIN Bookstaken t ON b.bookid = t.bookid "+
"WHERE t.bookid IS NULL")

First of all, you have to realize that what you have here is not SQL, but JPQL. These are two different languages.

Second, your query shows that you have a misconception. The BookTaken entity shouldn't have a bookid field. It should have a toOne association with Book, and thus a field of type Book.

Finally, the query you have is not correct. What you want is all the books for which there doesn't exist any BookTaken entity referencing this book:

select b from Book b where not exists (
    select bt.id from BookTaken bt where bt.bookid = b.id)

You're approaching the query the wrong way. You're telling the database that you want all pairs of rows from Book and Bookstaken where the Bookstaken doesn't match the Book . As you can imagine, there are lots of pairs of rows that don't match, so you're getting lots of rows, which is not what you want.

What you really want is to select all rows in Book that don't have a corresponding rown in Bookstaken . You can do this with SELECT b from Book WHERE NOT EXISTS( SELECT * from Bookstaken t WHERE t.bookid = b.bookid ) .

Or you can use a LEFT OUTER JOIN to join Book s to their matching Bookstaken s but leave in the result set any Books that don't have a corresponding Bookstaken , and then use WHERE t.bookid = NULL to get only the ones without a corresponding Bookstaken .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM