简体   繁体   中英

Negation verb in sql query

My question is simple. How can I compose a query in which I negate something? And I'm not refer to NOT condition. For example I have to select all the tourists that booked at least one pension(acc_type_code) whose rating is greater than 9, but did not book any 3 star hotel with a rating less than 9?

My tables:

Tourist

id (pk)
name
email

Booking

id_tourist (pk)
id_accomodation (pk)

Accomodation

id_accomodation (pk)
acc_type_code (fk)
nr_of_stars
rating
price

Accomodation type

acc_type_cde (pk)
acc_type_name

What I've tried:

SELECT t.id as id_tourist, t.name, t.email from Tourist t,
JOIN Booking b on (t.id = b.id_tourist)
JOIN Accomodation a on (b.id_accomodation = a.id_accomodation)
JOIN AccType at on (a.acc_type_code = at.acc_type_code)
WHERE a.rating > 9 AND .. ?

And here I'm blocked. Any suggestions?

You can use a LEFT OUTER JOIN to locate the records you want to exclude from your query, and then filter them out in the WHERE condition

SELECT t.id as id_tourist, t.name, t.email 
FROM Tourist t
INNER JOIN Booking b on (t.id = b.id_tourist)
INNER JOIN Accomodation a on (b.id_accomodation = a.id_accomodation)
INNER JOIN AccType at on (a.acc_type_code = at.acc_type_code)
LEFT OUTER JOIN Accomodate a2 on (b.id_accomodation = a2.id_accomodation) AND a2.nr_of_stars = 3 AND a2.rating < 9
WHERE a.rating > 9 AND a2.id_accomodation IS NULL

NOTE: The above syntax assumes MSSQL is your SQL flavor.

Update

based on further info from OP in comments, NOT EXISTS would likely provide a simpler approach. The concept is similar: Locate the records you want to exclude, and wrap then in a NOT EXISTS clause

SELECT t.id as id_tourist, t.name, t.email 
FROM Tourist t
INNER JOIN Booking b on (t.id = b.id_tourist)
INNER JOIN Accomodation a on (b.id_accomodation = a.id_accomodation)
INNER JOIN AccType at on (a.acc_type_code = at.acc_type_code)
WHERE a.rating > 9 
AND NOT EXISTS ( 
     SELECT b2.id_tourist FROM Booking b2 
     INNER JOIN Accomodation a2 on (b2.id_accomodation = a2.id_accomodation)
     WHERE a2.nr_of_stars = 3 AND a2.rating < 9
)

NOTE: You may need to add more joins inside the NOT EXISTS clause to locate the records that should be excluded. I don't know your data.

should be easy as using a NOT EXISTS

SELECT t.id as id_tourist, t.name, t.email from Tourist t,
JOIN Booking b on (t.id = b.id_tourist)
JOIN Accomodation a on (b.id_accomodation = a.id_accomodation)
JOIN AccType at on (a.acc_type_code = at.acc_type_code)
WHERE a.rating > 9 AND 
NOT EXISTS (
your query to get the same tourist_Id and has booked a 3 start hotel and under 9 ratings
)

Here is a LEFT JOIN , anti-join method:

SELECT t.id as id_tourist, t.name, t.email 
FROM Tourist t,
JOIN Booking b ON t.id = b.id_tourist
JOIN Accomodation a ON b.id_accomodation = a.id_accomodation
                   AND a.rating > 9
JOIN AccType at ON a.acc_type_code = at.acc_type_code
LEFT JOIN (SELECT t.id
           FROM Tourist t
           JOIN Booking b ON t.id = b.id_tourist
           JOIN Accomodation a ON b.id_accomodation = a.id_accomodation
                              AND a.rating < 9
                              AND a.nr_of_stars = 3
          ) a2 ON a2.id = t.id
WHERE a2.id IS NULL

The outer query finds only tourists ( t.id ) that booked a hotel above a 9 rating. The inner query finds only tourists ( t.id ) that booked a 3 star hotel, below a 9 rating.

By doing a LEFT JOIN between them, and limiting our results only to those that do not match ( WHERE a2.id IS NULL ) we are left with only those that booked a hotel above a 9 rating, and did not book a 3 star hotel with a rating below 9.

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