質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.11%

内部結合 WHERE

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 371

score 23

現在phpとmysqlで掲示板を製作しています
title.phpでタイトルを書き込み、タイトルをリンクとして表示させ、リンクをクリックすると関連したtolk.phpに飛ぶ仕組みをにしています。
title.phpでboardsのidをtolk.phpに渡し、resテーブルにboards.idでINSERTしてます。
下記のSQL文でしたところ2つの処理は一緒の結果(タイトルの中でしたことは、他では表示されずそこでしか表示されない)になっているのですが、WHEREでも2つのテーブルを関連付けできているということですか?

また、このような処理?(関連した特定の場所でしか見えないこと)をなんと言いますか?

テーブル名:boards
id:int(11)
title:varchar(255)
uptime:datetime
テーブル名:res
id:int(11)
boards_id:int(11)
res_time:datetime
message:text

$stmt = $pdo->prepare('SELECT * FROM res WHERE boards_id=?');
$params = [];
  $params[] = $boards_id;
  $stmt->execute($params);
$stmt = $pdo->prepare('SELECT * FROM boards INNER JOIN res ON boards.id = res.id WHERE boards_id=?');
  $params = [];
  $params[] = $boards_id;
  $stmt->execute($params);
<?php
// title.php
ini_set('display_errors', true);
error_reporting(E_ALL);
session_start();

require 'database.php';
$username = $_SESSION['username'];
$error = [];

date_default_timezone_set('Asia/Tokyo');
$title = filter_input(INPUT_POST, 'title');
$uptime = date("Y-m-d H:i:s");
if(filter_input(INPUT_SERVER,'REQUEST_METHOD') === 'POST'){

  if($title === ''){
    $error[] = 'タイトルは入力必須です。';
  }else if(strlen($title) > 10){
    $error[] = 'タイトルは10文字以内で入力してください。';
  }

  if(count($error) === 0){
    try{
      $stmt = $pdo->prepare('INSERT INTO boards (id,title,uptime) VALUES (null,?,?)');
      $params = [];
      $params[] = $title;
      $params[] = $uptime;
      $stmt->execute($params);
      header('Location:title.php');
      exit;
    }catch(PDOException $e){
      echo $e->getMessage();
    }
  }
}
try{
  $stmt = $pdo->query('SELECT * FROM boards ORDER BY id DESC');
  $count = $stmt->rowCount();
  $rows = $stmt->fetchAll();
}catch(PDOException $e){
  echo $e->getMessage();
}
?>

<!DOCTYPE html>
<html lang="ja">
  <head>
    <!-- <link rel="stylesheet" href="/css/styles.css"> -->
    <meta charset="utf-8">
    <title>メインページ</title>
    <h1>掲示板へようこそ</h1>
  </head>
  <body>
    <?php if(count($error) > 0) : ?>
      <?php foreach($error as $e) : ?>
        <p><?php echo h($e); ?></p>
      <?php endforeach; ?>
    <?php endif; ?>
  <section>
    <h2>新規投稿</h2>
    <!-- 書き込み用フォーム -->
    <form action="" method="post">
      <label for="title">タイトル</label><br>
      <input type="text" name="title" id="title"><br>
      <button type="submit">部屋を立てる</button>
    </form>
  </section>
  <section>
    <h2>投稿一覧</h2>
    <p>現在の投稿は<span><?php echo $count; ?></span>件です</p>
    <?php if(!empty($count)): ?>
    <?php foreach($rows as $row): ?>
      <div class="linkbox">
        <a href='tolk.php?boards_id=<?php echo $row['id']; ?>'>
          <?php echo h($username); ?>
          <?php echo h($row['uptime']); ?> <br>
          <?php echo h($row['title']); ?>
          <?php var_dump($row['id']); ?>

          <a href="delete.php?delete_title_id=<?php echo $row['id'] ?>">削除</a>
          <a href="change.php?change_title_id=<?php echo $row['id'] ?>">編集</a>
        </a>
      </div>
    <?php endforeach; ?>
      </ul>
    <?php else: ?>
      <p>投稿はまだありません。</p>
    <?php endif; ?>
  </section>
  </body>
</html>
<?php
// tolk.php
ini_set('display_errors', true);
error_reporting(E_ALL);
session_start();
require 'database.php';

$username = $_SESSION['username'];
$boards_id = filter_input(INPUT_GET, 'boards_id');
// $boards_id = $_GET['id'];
$error = [];

date_default_timezone_set('Asia/Tokyo');
$message = filter_input(INPUT_POST, 'message');//POST、入力された値を格納、未定義も防ぐ
$res_time = date("Y-m-d H:i:s");
if(filter_input(INPUT_SERVER,'REQUEST_METHOD') === 'POST'){

  if($message === ''){
    $error[] = 'メッセージは入力必須です。';
  }else if(strlen($message) > 100){
    $error[] = 'メッセージは100文字以内で入力してください。';
  }

  if(count($error) === 0){
    try{
      $stmt = $pdo->prepare('INSERT INTO res (id,boards_id,message,res_time) VALUES (null,?,?,?)');
      $params = [];
      $params[] = $boards_id;
      $params[] = $message;
      $params[] = $res_time;
      $stmt->execute($params);
      header("Location:tolk.php?boards_id=$boards_id");//変数展開では、ダブルコーテーション
      exit;
    }catch(PDOException $e){
      echo $e->getMessage();
    }
  }
}
try{
  $stmt = $pdo->prepare('SELECT * FROM res WHERE boards_id=?');
  $params = [];
  $params[] = $boards_id;
  $stmt->execute($params);
  $count = $stmt->rowCount();
  $rows = $stmt->fetchAll();
}catch(PDOException $e){
  echo $e->getMessage();
}
?>

<!DOCTYPE html>
<html lang="ja">
  <head>
    <link rel="stylesheet" href="/css/styles.css">
    <meta charset="utf-8">
    <title>メインページ</title>
    <h1>掲示板へようこそ</h1>
  </head>
  <body>
    <?php if(count($error) > 0) : ?>
      <?php foreach($error as $e) : ?>
        <p><?php echo h($e); ?></p>
      <?php endforeach; ?>
    <?php endif; ?>
  <section>
    <h2>新規投稿</h2>
    <form action="" method="post">
      <label for="message">メッセージ</label><br>
      <input type="text" name="message" id="message"><br>
      <button type="submit">投稿</button>
    </form>
  </section>
  <section>
    <h2>投稿一覧</h2>
    <p>現在の投稿は<span><?php echo $count; ?></span></p>
    <?php if(!empty($rows)): ?>
    <?php foreach($rows as $row): ?>
      <div class="linkbox">
        <?php echo h($username); ?>
        <?php echo h($row['res_time']); ?><br>
        <?php echo h($row['message']); ?>
        <a href="delete.php?id=<?php echo $row['id'] ?>">削除</a>
        <a href="change.php?id=<?php echo $row['id'] ?>">編集</a>
      </div>
      <?php var_dump($row['id']); ?>
    <?php endforeach; ?>
      </ul>
    <?php else: ?>
      <p>投稿はまだありません。</p>
    <?php endif; ?>
  </section>
  </body>
</html>
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Takumiboo

    2020/01/31 10:56

    本題と全く関係ないですが、会話などを意味する「トーク」であれば「talk」ですよ。

    キャンセル

  • yambejp

    2020/01/31 10:56

    ちょっと何をききたいのか把握できません

    「このような処理?(関連した特定の場所でしか見えないこと)をなんと言いますか? 」

    SQL文だけ抽出して、そのどの部分に対する質問なのかをはっきり
    した方が良いと思います

    キャンセル

回答 2

+2

2つの処理は一緒の結果(タイトルの中でしたことは、他では表示されずそこでしか表示されない)になっているのですが、WHEREでも2つのテーブルを関連付けできているということですか?

Whereで関連付けされているのではなく、from 句で結合により関連付けられています。
Whereでは関連付けられた結果に対しての条件を指定しています。

また、このような処理?(関連した特定の場所でしか見えないこと)をなんと言いますか?

関連付けたものを取り出す項目はSelect句で指定します。
selectで指定していない項目は、selectの結果としては見えない事になります。

質問内容からは、見えないのではなく、単にその項目を取り出していないだけだと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/01/31 12:27 編集

    回答ありがとうございます
    なるほどです。勘違いしていました
    FROM boards INNER JOIN resで結合により関連づけているのですね
    それぞれの項目のみ表示したい場合はそれぞれに関連付いている項目を取り出すということですね

    キャンセル

checkベストアンサー

0

質問の趣旨とズレますけど、
resテーブルのidってなんのidですか?
resテーブル内のユニークなidを保持するためなのか、それともboard.idと紐付けるためのidなのか。

boards.id = res.id
って条件でJOINしてしまう前提で話をすすめると、
じゃぁテーブルres内で一つ一つの書き込みのユニーク性を
res_timeでみるのでしょうか、
全く同じタイミングで複数のboard宛に書き込みが生じた場合、
idとres_timeを組み合わせて削除できなくもないけど、
あまり良くない設計と言えます。

テーブル名:res
id:int(11)
boards_id:int(11)  ←これを追加しましょう
res_time:datetime
message:text 

INNER JOIN res ON boards.id = res.boards_id

などとして連結します。
こうすることで、res個別の削除はres.idを指定して簡単にできます。


FROM句とJOIN句で、テーブルの結合をON句に書かれた条件で行ったものに対して、
WHERE句の条件で絞り込みを行うものです。

例えば

SELECT * FROM boards, res
WHERE boards.id = res.boards_id AND boards.id = ?
ORDER BY res.res_time;


のようにも書けなくもないですが、
テーブルboardsとテーブルresの全行を総当りで結合させたものの中から、
WHERE boards.id = res.boards_id AND boards.id = ?
の条件で絞り込みを行うため、得られる結果は一見同じに見えますが、
将来テーブルの行数が何千、何万、何十万同士を総当りで結合させる無駄な処理をした後に
絞り込みを行うと、総当たりの無駄な処理+膨大な量からの無駄な絞り込みということになり、
そういう無駄を省くためにも、
FROM句にJOIN句をつないでテーブルの結合を効率良く行うのがセオリーです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/01/31 17:01

    クエリーを工夫することで、やろうと思えば、boards上で削除されたidを持つres上のデータも一括で削除できたりします。
    についてですが、boards上で削除されたidを持つres上のデータも一括で削除できたりします。は、外部キーの設定をしないとできないのですか?

    キャンセル

  • 2020/01/31 17:22

    クエリーを工夫することで、やろうと思えば、boards上で削除されたidを持つres上のデータも一括で削除できたりします。
    とは、boards.idに外部キーを設定して、CASCADEを設定することですか?

    キャンセル

  • 2020/01/31 17:39 編集

    FOREIGN KEYの指定を与えるのはあくまでres.boards_idに対してであり、
    boards.idに与えるものではありません。
    テーブルの主従関係の「従」側につけます。
    https://dev.mysql.com/doc/refman/5.6/ja/create-table-foreign-keys.html
    ON DELETE CASCADE と ON UPDATE CASCADE の指定ができるようですね。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.11%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る