简体   繁体   中英

How can I show rows from one table that aren't in another table?

I have two tables in a database, one of them is a list of 'buildings' you could create. The other is a list of buildings that have been built by users.

On one page, (cityproduction.php), it displays a list of 'buildings' you can build. I want it to display the buildings that you can build, that you haven't already built.

Here is my code:

$sql = "SELECT * FROM [The list of built buildings] WHERE building_owner = '$user'";
$result = $conn->query($sql);

if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
    $variable = $row["building_name"];    
}

(...)

$sql = "SELECT * FROM [The list of ALL buildings] WHERE name != '$variable' ORDER BY id asc";
$result = mysqli_query($database,$sql) or die(mysqli_error($database));

while($rws = mysqli_fetch_array($result)){ 
echo $rws["name"]; (etc.)

What this is doing is only not-showing one of the buildings that the user has built, not all of them.

Without seeing the real table names or the schema it is tricky to answer accurately but you could try something along these lines:

SELECT * FROM `all_buildings` 
WHERE `id` not in ( 
    select `building_id` from `built_buildings` where `building_owner` = '$user'
    ) 
ORDER BY `id` asc;

Another translation of your question into SQL (besides NOT IN ) results in a Correlated Subquery:

SELECT * FROM `all_buildings` AS a
WHERE NOT EXISTS 
  ( 
    select * from `built_buildings` AS b 
    where a.`id` = b.`building_id`    -- Outer Select correlated to Inner
      and b.`building_owner` = '$user'
  ) 
ORDER BY `id` asc;

The main advantage over NOT IN : it's using only two-valued-logic ( NULL is ignored = false) while NOT IN uses three-valued-logic (comparison to NULL returns unknown which might no return what you expect)

Why are you using while after the first query, it suppose to be a list or just a single value? because if you use $variable in your second query it will only have the value of the last value of the list you are getting

if ($result->num_rows > 0) {

  $variable =  array();

  while($row = $result->fetch_assoc()) {

    $variable[] = $row["building_name"];    
}

Second query example:

 foreach($variable as $building) {

      $sql = "SELECT * FROM [The list of ALL buildings] WHERE name != '$building' ORDER BY id asc";
      $result = mysqli_query($database,$sql) or die(mysqli_error($database));
      $result = mysqli_fetch_assoc($result);

      echo $result["name"];
    }

Assuming both of your tables have some sort of id column to relate them, with this query:

SELECT building_name, building_owner FROM 
    test.all_buildings a 
    LEFT JOIN test.built_buildings b ON a.id = b.building_id AND b.building_owner = ?
ORDER BY building_owner DESC, building_name;

(where ? is the user), you can select all the buildings, first the ones that have been built, followed by the ones that haven't, in one query. If your tables don't have id's like that, you can join them on name instead; it should work as long as the names are distinct.

Then as you fetch the rows, you can sort them into "built" or "not built" by checking if the row has a building_owner .

if ($result->num_rows > 0) {
    while($row = $result->fetch_assoc()) {
        if ($row['building_owner']) {
            $built[] = $row['building_name'];
        } else {
            $not_built = $row['building_name'];
        }
    }
}

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