我正在尝试创建一个查询,但我不知道如何按照我设置数据库的方式选择多个表。
在我的查询中,我试图显示哪些电影有哪些 Actor ,但每部电影都有多个 Actor ,所以我必须从三个表中进行选择。 这是我的电影表:
这是我的 Actor 信息表
我的最后一个表是 actor_movie 表。此表将 movie_id 与 actor_id 相关联
我有以下 php 连接到数据库,但我不确定如何编写查询部分。
<?php
$databaseName = 'movie_database';
$databaseUser = 'root';
$databasePassword = 'root';
$databaseHost = '127.0.0.1';
$conn = new mysqli($databaseHost, $databaseUser, $databasePassword, $databaseName);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
echo "Connected successfully <br>";
// this code is wrong, but i get the general idea of how to select from my db.
$sql = "SELECT actor_movie, movies, actor_information FROM actor_movie INNER
JOIN movies ON actor_movie.movie_id = movies.movie_id
INNER JOIN actor_information ON actor_movie.actor_id =
actor_information.actor_id"
$result = mysqli_query($conn, $sql);
if (mysqli_num_rows($result) > 0) {
// output data of each row
while($row = mysqli_fetch_assoc($result)) {
echo " Title " . $row["title"]. "". $row["actor_id"]. "<br>";
}
} else {
echo "0 results";
}
mysqli_close($conn);
?>
最佳答案
- 正如@tadman 所建议的,面向对象的 mysqli 更好。不过,我的建议是使用 PDO 而不是 mysqli。阅读this .
- 阅读here和 here正确处理错误/异常。
- 注意 prepared statement 的使用(也请参阅 here)以避免 SQL 注入(inject)。
- 您也可以只获取一部电影。
- 选择使用
LEFT JOIN
:获取所有电影并使用actor_movie
将 Actor 详细信息“附加”到每部电影。还有已获取的电影,尚未分配任何 Actor (例如,结果集中的actor_name = NULL
)。 - 获取结果集后,我构建了第二个数组 (
$movies
) 来准备要在 html 表中正确显示的数据。请注意,可以使用多个查询从数据库中获取数据来代替此步骤:一个查询用于获取电影列表,一个查询用于获取 Actor 。 - 注意数据获取代码(php,页面上部)与显示代码(html,页面下部)的分离。例如:所有数据都是从数据库中获取的数组(php,页面上部),然后仅迭代这些数组以显示数据(html,页面下部)。例如:没有数据获取代码与数据显示代码混合。
- 数据库结构与您提供的结构相对应。
- 请注意 Actor “罗伯特·德尼罗”(
actor_id = 1
) 在两部电影中的“使用”(参见actor_movie
表)。
祝你好运。
注意:所有三个解决方案都是相同的,从以下行开始:$movies = [];
方案一:mysqli使用get_result()
+ fetch_all()
只有安装了 mysqlnd 驱动程序(“MySQL Native Driver”)才能工作!
<?php
// Db configs.
define('HOST', 'localhost');
define('PORT', 3306);
define('DATABASE', 'db');
define('USERNAME', 'user');
define('PASSWORD', 'pass');
// Error reporting.
error_reporting(E_ALL);
ini_set('display_errors', 1); // Set it to 0 on a live server!
/**
* Enable internal report functions. This enables the exception handling,
* e.g. mysqli will not throw PHP warnings anymore, but mysqli exceptions
* (mysqli_sql_exception). They are catched in the try-catch block.
*
* MYSQLI_REPORT_ERROR: Report errors from mysqli function calls.
* MYSQLI_REPORT_STRICT: Throw a mysqli_sql_exception for errors instead of warnings.
*
* See: http://php.net/manual/en/class.mysqli-driver.php
* See: http://php.net/manual/en/mysqli-driver.report-mode.php
* See: http://php.net/manual/en/mysqli.constants.php
*/
$mysqliDriver = new mysqli_driver();
$mysqliDriver->report_mode = (MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
/**
* Create a new db connection.
*
* @see http://php.net/manual/en/mysqli.construct.php
*/
$connection = new mysqli(HOST, USERNAME, PASSWORD, DATABASE, PORT);
// Get movie id. If NULL, display all movies. Read from $_POST for example.
$movieId = NULL;
/*
* The SQL statement to be prepared. Notice the so-called markers,
* e.g. the "?" signs. They will be replaced later with the
* corresponding values when using mysqli_stmt::bind_param.
*
* See: http://php.net/manual/en/mysqli.prepare.php
*/
$sql = sprintf(
'SELECT
mv.movie_id,
mv.title,
mv.rating,
mv.Runtime,
mv.movie_rating,
mv.release_date,
acin.actor_name
FROM movies AS mv
LEFT JOIN actor_movie AS acmv ON acmv.movie_id = mv.movie_id
LEFT JOIN actor_information AS acin ON acin.actor_id = acmv.actor_id
%s'
, isset($movieId) ? 'WHERE mv.movie_id = ?' : ''
);
/*
* Prepare the SQL statement for execution - ONLY ONCE.
*
* See: http://php.net/manual/en/mysqli.prepare.php
*/
$statement = $connection->prepare($sql);
/*
* Bind variables for the parameter markers (?) in the
* SQL statement that was passed to prepare(). The first
* argument of bind_param() is a string that contains one
* or more characters which specify the types for the
* corresponding bind variables.
*
* See: http://php.net/manual/en/mysqli-stmt.bind-param.php
*/
if (isset($movieId)) {
$statement->bind_param('i', $movieId);
}
/*
* Execute the prepared SQL statement.
* When executed any parameter markers which exist will
* automatically be replaced with the appropriate data.
*
* See: http://php.net/manual/en/mysqli-stmt.execute.php
*/
$executed = $statement->execute();
/*
* Get the result set from the prepared statement.
*
* NOTA BENE:
* Available only with mysqlnd ("MySQL Native Driver")! If this
* is not installed, then uncomment "extension=php_mysqli_mysqlnd.dll" in
* PHP config file (php.ini) and restart web server (I assume Apache) and
* mysql service. Or use the following functions instead:
* mysqli_stmt::store_result + mysqli_stmt::bind_result + mysqli_stmt::fetch.
*
* See:
* http://php.net/manual/en/mysqli-stmt.get-result.php
* https://stackoverflow.com/questions/8321096/call-to-undefined-method-mysqli-stmtget-result
*/
$result = $statement->get_result();
/*
* Fetch data and save it into $fetchedData array.
*
* See: http://php.net/manual/en/mysqli-result.fetch-all.php
*/
// Fetch all rows at once...
$fetchedData = $result->fetch_all(MYSQLI_ASSOC);
// ... OR fetch one row at a time.
// while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
// $fetchedData[] = $row;
// }
//
// Just for testing. Display fetched data.
//echo '<pre>' . print_r($fetchedData, TRUE) . '</pre>';
/*
* Free the memory associated with the result. You should
* always free your result when it is not needed anymore.
*
* See: http://php.net/manual/en/mysqli-result.free.php
*/
$result->close();
/*
* Close the prepared statement. It also deallocates the statement handle.
* If the statement has pending or unread results, it cancels them
* so that the next query can be executed.
*
* See: http://php.net/manual/en/mysqli-stmt.close.php
*/
$statementClosed = $statement->close();
// Prepare a list with each movie and its corresponding actors - for display in a html table.
$movies = [];
foreach ($fetchedData as $item) {
$movieId = $item['movie_id'];
$title = $item['title'];
$rating = $item['rating'];
$runtime = $item['Runtime'];
$movieRating = $item['movie_rating'];
$releaseDate = $item['release_date'];
$actorName = $item['actor_name'];
if (!array_key_exists($movieId, $movies)) {
$movies[$movieId] = [
'title' => $title,
'rating' => $rating,
'Runtime' => $runtime,
'movie_rating' => $movieRating,
'release_date' => $releaseDate,
];
}
if (isset($actorName)) {
$movies[$movieId]['actors'][] = $actorName;
} else {
$movies[$movieId]['actors'] = [];
}
}
// Just for testing. Display movies list.
//echo '<pre>' . print_r($movies, TRUE) . '</pre>';
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes" />
<meta charset="UTF-8" />
<!-- The above 3 meta tags must come first in the head -->
<title>Demo</title>
<style type="text/css">
body { padding: 10px; font-family: "Verdana", Arial, sans-serif; }
.movies { border-collapse: collapse; border: 1px solid #ccc; }
.movies td, th { padding: 5px; }
.movie-record { color: white; background-color: #00b3ee; }
.actor-record { background-color: #f3f3f3; }
</style>
</head>
<body>
<h3>
Movies list
</h3>
<table class="movies">
<thead>
<tr>
<th>Movie ID</th>
<th>Title</th>
<th>Rating</th>
<th>Runtime</th>
<th>Movie Rating</th>
<th>Release Date</th>
</tr>
</thead>
<tbody>
<?php
if ($movies) {
foreach ($movies as $movieId => $movie) {
$title = $movie['title'];
$rating = $movie['rating'];
$runtime = $movie['Runtime'];
$movieRating = $movie['movie_rating'];
$releaseDate = $movie['release_date'];
?>
<tr class="movie-record">
<td><?php echo $movieId; ?></td>
<td><?php echo $title; ?></td>
<td><?php echo $rating; ?></td>
<td><?php echo $runtime; ?></td>
<td><?php echo $movieRating; ?></td>
<td><?php echo $releaseDate; ?></td>
</tr>
<?php
foreach ($movie['actors'] as $actorName) {
?>
<tr class="actor-record">
<td colspan="6"><?php echo $actorName; ?></td>
</tr>
<?php
}
}
} else {
?>
<tr>
<td colspan="6">
<?php echo 'No movies found'; ?>
</td>
</tr>
<?php
}
?>
</tbody>
</table>
</body>
</html>
方案二:mysqli使用store_result()
+ bind_result()
+ fetch()
很难相处。但即使未安装 mysqlnd 驱动程序(“MySQL Native Driver”)它也能正常工作。
<?php
// Db configs.
define('HOST', 'localhost');
define('PORT', 3306);
define('DATABASE', 'db');
define('USERNAME', 'user');
define('PASSWORD', 'pass');
// Error reporting.
error_reporting(E_ALL);
ini_set('display_errors', 1); // Set it to 0 on a live server!
/**
* Enable internal report functions. This enables the exception handling,
* e.g. mysqli will not throw PHP warnings anymore, but mysqli exceptions
* (mysqli_sql_exception). They are catched in the try-catch block.
*
* MYSQLI_REPORT_ERROR: Report errors from mysqli function calls.
* MYSQLI_REPORT_STRICT: Throw a mysqli_sql_exception for errors instead of warnings.
*
* See: http://php.net/manual/en/class.mysqli-driver.php
* See: http://php.net/manual/en/mysqli-driver.report-mode.php
* See: http://php.net/manual/en/mysqli.constants.php
*/
$mysqliDriver = new mysqli_driver();
$mysqliDriver->report_mode = (MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
/**
* Create a new db connection.
*
* @see http://php.net/manual/en/mysqli.construct.php
*/
$connection = new mysqli(HOST, USERNAME, PASSWORD, DATABASE, PORT);
// Get movie id. If NULL, display all movies. Read from $_POST for example.
$movieId = NULL;
/*
* The SQL statement to be prepared. Notice the so-called markers,
* e.g. the "?" signs. They will be replaced later with the
* corresponding values when using mysqli_stmt::bind_param.
*
* See: http://php.net/manual/en/mysqli.prepare.php
*/
$sql = sprintf(
'SELECT
mv.movie_id,
mv.title,
mv.rating,
mv.Runtime,
mv.movie_rating,
mv.release_date,
acin.actor_name
FROM movies AS mv
LEFT JOIN actor_movie AS acmv ON acmv.movie_id = mv.movie_id
LEFT JOIN actor_information AS acin ON acin.actor_id = acmv.actor_id
%s'
, isset($movieId) ? 'WHERE mv.movie_id = ?' : ''
);
/*
* Prepare the SQL statement for execution - ONLY ONCE.
*
* See: http://php.net/manual/en/mysqli.prepare.php
*/
$statement = $connection->prepare($sql);
/*
* Bind variables for the parameter markers (?) in the
* SQL statement that was passed to prepare(). The first
* argument of bind_param() is a string that contains one
* or more characters which specify the types for the
* corresponding bind variables.
*
* See: http://php.net/manual/en/mysqli-stmt.bind-param.php
*/
if (isset($movieId)) {
$statement->bind_param('i', $movieId);
}
/*
* Execute the prepared SQL statement.
* When executed any parameter markers which exist will
* automatically be replaced with the appropriate data.
*
* See: http://php.net/manual/en/mysqli-stmt.execute.php
*/
$executed = $statement->execute();
/*
* Transfer the result set resulted from executing the prepared statement.
* E.g. store, e.g. buffer the result set into the (same) prepared statement.
*
* See:
* http://php.net/manual/en/mysqli-stmt.store-result.php
* https://stackoverflow.com/questions/8321096/call-to-undefined-method-mysqli-stmtget-result
*/
$resultStored = $statement->store_result();
/*
* Bind the result set columns to corresponding variables.
* E.g. these variables will hold the column values after fetching.
*
* See: http://php.net/manual/en/mysqli-stmt.bind-result.php
*/
$varsBound = $statement->bind_result(
$boundMovieId
, $boundTitle
, $boundRating
, $boundRuntime
, $boundMovieRating
, $boundReleaseDate
, $boundActorName
);
/*
* Fetch results from the result set (of the prepared statement) into the bound variables.
*
* See: http://php.net/manual/en/mysqli-stmt.fetch.php
*/
$fetchedData = [];
while ($row = $statement->fetch()) {
$fetchedData[] = [
'movie_id' => $boundMovieId,
'title' => $boundTitle,
'rating' => $boundRating,
'Runtime' => $boundRuntime,
'movie_rating' => $boundMovieRating,
'release_date' => $boundReleaseDate,
'actor_name' => $boundActorName,
];
}
// Just for testing. Display fetched data.
//echo '<pre>' . print_r($fetchedData, TRUE) . '</pre>';
/*
* Frees the result memory associated with the statement,
* which was allocated by mysqli_stmt::store_result.
*
* See: http://php.net/manual/en/mysqli-stmt.store-result.php
*/
$statement->free_result();
/*
* Close the prepared statement. It also deallocates the statement handle.
* If the statement has pending or unread results, it cancels them
* so that the next query can be executed.
*
* See: http://php.net/manual/en/mysqli-stmt.close.php
*/
$statementClosed = $statement->close();
// Prepare a list with each movie and its corresponding actors - for display in a html table.
$movies = [];
foreach ($fetchedData as $item) {
$movieId = $item['movie_id'];
$title = $item['title'];
$rating = $item['rating'];
$runtime = $item['Runtime'];
$movieRating = $item['movie_rating'];
$releaseDate = $item['release_date'];
$actorName = $item['actor_name'];
if (!array_key_exists($movieId, $movies)) {
$movies[$movieId] = [
'title' => $title,
'rating' => $rating,
'Runtime' => $runtime,
'movie_rating' => $movieRating,
'release_date' => $releaseDate,
];
}
if (isset($actorName)) {
$movies[$movieId]['actors'][] = $actorName;
} else {
$movies[$movieId]['actors'] = [];
}
}
// Just for testing. Display movies list.
//echo '<pre>' . print_r($movies, TRUE) . '</pre>';
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes" />
<meta charset="UTF-8" />
<!-- The above 3 meta tags must come first in the head -->
<title>Demo</title>
<style type="text/css">
body { padding: 10px; font-family: "Verdana", Arial, sans-serif; }
.movies { border-collapse: collapse; border: 1px solid #ccc; }
.movies td, th { padding: 5px; }
.movie-record { color: white; background-color: #00b3ee; }
.actor-record { background-color: #f3f3f3; }
</style>
</head>
<body>
<h3>
Movies list
</h3>
<table class="movies">
<thead>
<tr>
<th>Movie ID</th>
<th>Title</th>
<th>Rating</th>
<th>Runtime</th>
<th>Movie Rating</th>
<th>Release Date</th>
</tr>
</thead>
<tbody>
<?php
if ($movies) {
foreach ($movies as $movieId => $movie) {
$title = $movie['title'];
$rating = $movie['rating'];
$runtime = $movie['Runtime'];
$movieRating = $movie['movie_rating'];
$releaseDate = $movie['release_date'];
?>
<tr class="movie-record">
<td><?php echo $movieId; ?></td>
<td><?php echo $title; ?></td>
<td><?php echo $rating; ?></td>
<td><?php echo $runtime; ?></td>
<td><?php echo $movieRating; ?></td>
<td><?php echo $releaseDate; ?></td>
</tr>
<?php
foreach ($movie['actors'] as $actorName) {
?>
<tr class="actor-record">
<td colspan="6"><?php echo $actorName; ?></td>
</tr>
<?php
}
}
} else {
?>
<tr>
<td colspan="6">
<?php echo 'No movies found'; ?>
</td>
</tr>
<?php
}
?>
</tbody>
</table>
</body>
</html>
方案三(推荐):PDO
绝对的赢家。
<?php
// Db configs.
define('HOST', 'localhost');
define('PORT', 3306);
define('DATABASE', 'db');
define('USERNAME', 'user');
define('PASSWORD', 'pass');
define('CHARSET', 'utf8');
// Error reporting.
error_reporting(E_ALL);
ini_set('display_errors', 1); // Set it to 0 on a live server!
/*
* Create a PDO instance as db connection to db.
*
* See: http://php.net/manual/en/class.pdo.php
* See: http://php.net/manual/en/pdo.constants.php
* See: http://php.net/manual/en/pdo.error-handling.php
* See: http://php.net/manual/en/pdo.connections.php
*/
$connection = new PDO(
sprintf('mysql:host=%s;port=%s;dbname=%s;charset=%s', HOST, PORT, DATABASE, CHARSET)
, USERNAME
, PASSWORD
, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => FALSE,
PDO::ATTR_PERSISTENT => TRUE
]
);
// Get movie id. If NULL, display all movies. Read from $_POST for example.
$movieId = NULL;
/*
* The SQL statement to be prepared. Notice the so-called named markers.
* They will be replaced later with the corresponding values from the
* bindings array when using PDOStatement::bindValue.
*
* When using named markers, the bindings array will be an associative
* array, with the key names corresponding to the named markers from
* the sql statement.
*
* You can also use question mark markers. In this case, the bindings
* array will be an indexed array, with keys beginning from 1 (not 0).
* Each array key corresponds to the position of the marker in the sql
* statement.
*
* See: http://php.net/manual/en/mysqli.prepare.php
*/
$sql = sprintf(
'SELECT
mv.movie_id,
mv.title,
mv.rating,
mv.Runtime,
mv.movie_rating,
mv.release_date,
acin.actor_name
FROM movies AS mv
LEFT JOIN actor_movie AS acmv ON acmv.movie_id = mv.movie_id
LEFT JOIN actor_information AS acin ON acin.actor_id = acmv.actor_id
%s'
, isset($movieId) ? 'WHERE mv.movie_id = :movie_id' : ''
);
/**
* The bindings array, mapping the named markers from the sql
* statement to the corresponding values. It will be directly
* passed as argument to the PDOStatement::execute method.
*
* See: http://php.net/manual/en/pdostatement.execute.php
*/
$bindings = [];
if (isset($movieId)) {
$bindings[':movie_id'] = $movieId;
}
/*
* Prepare the sql statement for execution and return a statement object.
*
* See: http://php.net/manual/en/pdo.prepare.php
*/
$statement = $connection->prepare($sql);
/*
* Execute the prepared statement. Because the bindings array
* is directly passed as argument, there is no need to use any
* binding method for each sql statement's marker (like
* PDOStatement::bindParam or PDOStatement::bindValue).
*
* See: http://php.net/manual/en/pdostatement.execute.php
*/
$executed = $statement->execute($bindings);
/*
* Fetch data (all at once) and save it into $fetchedData array.
*
* See: http://php.net/manual/en/pdostatement.fetchall.php
*/
$fetchedData = $statement->fetchAll(PDO::FETCH_ASSOC);
// Just for testing. Display fetched data.
echo '<pre>' . print_r($fetchedData, TRUE) . '</pre>';
// Prepare a list with each movie and its corresponding actors - for display in a html table.
$movies = [];
foreach ($fetchedData as $item) {
$movieId = $item['movie_id'];
$title = $item['title'];
$rating = $item['rating'];
$runtime = $item['Runtime'];
$movieRating = $item['movie_rating'];
$releaseDate = $item['release_date'];
$actorName = $item['actor_name'];
if (!array_key_exists($movieId, $movies)) {
$movies[$movieId] = [
'title' => $title,
'rating' => $rating,
'Runtime' => $runtime,
'movie_rating' => $movieRating,
'release_date' => $releaseDate,
];
}
if (isset($actorName)) {
$movies[$movieId]['actors'][] = $actorName;
} else {
$movies[$movieId]['actors'] = [];
}
}
// Just for testing. Display movies list.
//echo '<pre>' . print_r($movies, TRUE) . '</pre>';
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes" />
<meta charset="UTF-8" />
<!-- The above 3 meta tags must come first in the head -->
<title>Demo</title>
<style type="text/css">
body { padding: 10px; font-family: "Verdana", Arial, sans-serif; }
.movies { border-collapse: collapse; border: 1px solid #ccc; }
.movies td, th { padding: 5px; }
.movie-record { color: white; background-color: #00b3ee; }
.actor-record { background-color: #f3f3f3; }
</style>
</head>
<body>
<h3>
Movies list
</h3>
<table class="movies">
<thead>
<tr>
<th>Movie ID</th>
<th>Title</th>
<th>Rating</th>
<th>Runtime</th>
<th>Movie Rating</th>
<th>Release Date</th>
</tr>
</thead>
<tbody>
<?php
if ($movies) {
foreach ($movies as $movieId => $movie) {
$title = $movie['title'];
$rating = $movie['rating'];
$runtime = $movie['Runtime'];
$movieRating = $movie['movie_rating'];
$releaseDate = $movie['release_date'];
?>
<tr class="movie-record">
<td><?php echo $movieId; ?></td>
<td><?php echo $title; ?></td>
<td><?php echo $rating; ?></td>
<td><?php echo $runtime; ?></td>
<td><?php echo $movieRating; ?></td>
<td><?php echo $releaseDate; ?></td>
</tr>
<?php
foreach ($movie['actors'] as $actorName) {
?>
<tr class="actor-record">
<td colspan="6"><?php echo $actorName; ?></td>
</tr>
<?php
}
}
} else {
?>
<tr>
<td colspan="6">
<?php echo 'No movies found'; ?>
</td>
</tr>
<?php
}
?>
</tbody>
</table>
</body>
</html>
用于测试的数据:
Table movies:
movie_id title rating Runtime movie_rating release_date
----------------------------------------------------------------------------
1 Blade Runner 2049 R 164 8.5 2017-10-06
2 The Godfather R 178 9.2 1972-04-24
3 Pulp Fiction R 178 8.9 1994-10-14
Table actor_information:
actor_id actor_name
--------------------------
1 Robert de Niro
2 Ryan Gosling
3 Ana de Armas
4 Marlon Brando
5 John Travolta
6 Julia Roberts
Table actor_movie:
movie_id actor_id
--------------------
1 1
1 2
1 3
2 4
2 1
2 6
结果:
关于php - 为查询 MySQLi PHP 选择多个表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47359284/