在我的科目表中,我按学期和月份列出了所有学生的课程,每个月的分数
[ { "id": "4", - this is the subject id "userid": "1", "name": "bio", "semester": "3", - semester "month": "5", - the month "points": "652" - points of this class "time": "2017-06-18 22:45:04" }, { "id": "3", - this is the subject id "userid": "1", "name": "math", "semester": "3", - semester "month": "4", - the month "points": "33" - points of this class "time": "2017-05-15 22:45:04" }, { "id": "2", - this is the subject id "userid": "1", "name": "chem", "semester": "1", - semester "month": "3", - the month "points": "22" - points of this class "time": "2017-04-11 22:45:04" }, { "id": "1", - this is the subject id "userid": "1", "name": "phy", "semester": "1", - semester "month": "2", - the month "points": "10" - points of this class "time": "2017-02-10 22:45:04" } ]
这就是我尝试过的
$sql = "SELECT users.id userid,users.name username,subjects.id subjectsid, subjects.name subjectname, subjects.points activepts FROM tbusers AS users INNER JOIN tbsubjects AS subjects ON users.id = subjects.userid WHERE users.id = '$userid' ORDER BY subjects.time DESC"; try { $db = new db(); $db = $db->connect(); $stmt = $db->prepare($sql); $stmt->execute(); $user = $stmt->fetchAll(PDO::FETCH_OBJ); $db = null; if(empty($user)) { $response->getBody()->write (' { "error": { "message":"Invalid" } }'); } else { $response->getBody()->write(json_encode($user)); } } catch(PDOException $e) {}
我从查询中获得的当前输出是每个查询的多个响应,因为fetchAll我可以将其更改为,fetch但不会获取其他数据
fetchAll
fetch
[ { "userid": "1", "username": "joe", "subjectid": "4", "subjectname": "bio", "activepts": "652" }, "userid": "1", "username": "joe", "subjectid": "3", "subjectname": "math", "activepts": "33" }, "userid": "1", "username": "joe", "subjectid": "2", "subjectname": "chem", "activepts": "22" }, "userid": "1", "username": "joe", "subjectid": "1", "subjectname": "phy", "activepts": "10" } ]
我的问题是我如何将它们合并为一个响应,并在预期的输出中返回以下数据(我在每个字段中添加了一些说明以对其进行解释)
预期产量
[ { "userid": "1", - from users table "username": "joe", - from users table "subjectsid": "1", - first subject id for the student in this case the one for phy "subjectname": "bio", - current subject name "activepts": "652", - points of current month "totalpts": "717", - total points of all subjects for this student "sem1": "32", - total points of all subjects for this student of semester 1 "sem2": "0", - total points of all subjects for this student of semester 2 "sem3": "685", - total points of all subjects for this student of semester 3 } ]
问题是,您想获取主题,而不是学生。因此,我反转了FROM和LEFT JOIN。因此,当您想要主题列表时,您将从SELECT … FROM主题开始。然后,如果您需要每个主题的其他详细信息(例如用户名等),请应用LEFT JOIN,这意味着:将所有必需的详细信息(用户名等)加入LEFT表的每个记录(例如主表)中您的情况是表格“主题”)。
祝好运!
<?php try { $dbAdapter = new DbAdapter(); $connection = $dbAdapter->connect(); /* * I renamed user id variable (from $userId to $userid1) in order to show you that you can * provide more users if you wish. Then you just have to extend * the WHERE clause in the sql statement and the bindings array. */ $userid1 = 1; /* * The sql statement - it will be prepared. * * ====================================================== * I'm not sure about the following fields - because you * didn't provide proper selection criteria for them: * * 1) "subjectsid": "1", - first subject id for the student in this case the one for phy * 2) "subjectname": "bio", - current subject name * ====================================================== */ $sql = 'SELECT subjects.userid, users.name AS username, ( SELECT id FROM tbsubjects WHERE userid = subjects.userid ORDER BY id ASC LIMIT 1 ) AS subjectsid, ( SELECT name FROM tbsubjects WHERE userid = subjects.userid ORDER BY time DESC LIMIT 1 ) AS subjectname, ( SELECT IFNULL(SUM(points), 0) FROM tbsubjects WHERE userid = subjects.userid AND month = DATE_FORMAT(NOW(), "%c") ) AS activepts, IFNULL(SUM(subjects.points), 0) AS totalpts, ( SELECT IFNULL(SUM(points), 0) FROM tbsubjects WHERE userid = subjects.userid AND semester = 1 ) AS sem1, ( SELECT IFNULL(SUM(points), 0) FROM tbsubjects WHERE userid = subjects.userid AND semester = 2 ) AS sem2, ( SELECT IFNULL(SUM(points), 0) FROM tbsubjects WHERE userid = subjects.userid AND semester = 3 ) AS sem3 FROM tbsubjects AS subjects LEFT JOIN tbusers AS users ON users.id = subjects.userid WHERE subjects.userid = :userid1 GROUP BY subjects.userid ORDER BY subjects.time DESC'; /* * The input parameters list for the prepared sql statement. */ $bindings = array( ':userid1' => $userid1, ); /* * Prepare and validate the sql statement. * * -------------------------------------------------------------------------------- * If the database server cannot successfully prepare the statement, PDO::prepare() * returns FALSE or emits PDOException (depending on error handling settings). * -------------------------------------------------------------------------------- */ $statement = $connection->prepare($sql); if (!$statement) { throw new UnexpectedValueException('The sql statement could not be prepared!'); } /* * Bind the input parameters to the prepared statement. * * ----------------------------------------------------------------------------------- * Unlike PDOStatement::bindValue(), when using PDOStatement::bindParam() the variable * is bound as a reference and will only be evaluated at the time that * PDOStatement::execute() is called. * ----------------------------------------------------------------------------------- */ foreach ($bindings as $key => $value) { $bound = $statement->bindValue( getInputParameterName($key) , $value , getInputParameterDataType($value) ); if (!$bound) { throw new UnexpectedValueException('An input parameter can not be bound!'); } } /* * Execute the prepared statement. * * ------------------------------------------------------------------ * PDOStatement::execute returns TRUE on success or FALSE on failure. * ------------------------------------------------------------------ */ $executed = $statement->execute(); if (!$executed) { throw new UnexpectedValueException('The prepared statement could not be executed!'); } /* * Fetch users list - array of objects. */ $users = $statement->fetchAll(PDO::FETCH_OBJ); if ($users === FALSE) { throw new UnexpectedValueException('Fetching users list failed!'); } /* * Close connection. */ $connection = NULL; /* * Handle results. */ if (empty($users)) { $response->getBody()->write( '{ "error": { "message":"Invalid" } }' ); } else { $response->getBody()->write(json_encode($users)); } } catch (PDOException $exc) { echo $exc->getMessage(); // $logger->log($exc); exit(); } catch (Exception $exc) { echo $exc->getMessage(); // $logger->log($exc); exit(); } /** * Get the name of an input parameter by its key in the bindings array. * * @param int|string $key The key of the input parameter in the bindings array. * @return int|string The name of the input parameter. */ function getInputParameterName($key) { return is_int($key) ? ($key + 1) : (':' . ltrim($key, ':')); } /** * Get the PDO::PARAM_* constant, e.g the data type of an input parameter, by its value. * * @param mixed $value Value of the input parameter. * @return int The PDO::PARAM_* constant. */ function getInputParameterDataType($value) { $dataType = PDO::PARAM_STR; if (is_int($value)) { $dataType = PDO::PARAM_INT; } elseif (is_bool($value)) { $dataType = PDO::PARAM_BOOL; } return $dataType; }
对于我的项目,我开发了一个DbAdapter类。方法名称是不言自明的。因此,每个网页内没有更多的spagetti代码:-)而是:
您问题的解决方案如下所示:
<?php //*********************************************************************************** // Put this in a php file (like db.php) to include whereever you need db data access. //*********************************************************************************** // // Db configs. define('DB_HOST', '...'); define('DB_PORT', 3306); define('DB_DBNAME', '...'); define('DB_CHARSET', 'utf8'); define('DB_USERNAME', '...'); define('DB_PASSWORD', '...'); define('DB_DRIVER_NAME', 'mysql'); // Create db adapter. $dbAdapter = new DbAdapter(DB_HOST, DB_DBNAME, DB_USERNAME, DB_PASSWORD, DB_PORT, DB_CHARSET); //*********************************************************************************** $userid1 = 1; // Sql statement. $sql = 'SELECT ... FROM ... WHERE subjects.userid = :userid1 GROUP BY ... ORDER BY ...'; // Input parameters. $bindings = array( ':userid1' => $userid1, ); // Fetch users. $users = $dbAdapter->fetchAll($sql, $bindings); // Disconnect from db. $dbAdapter->disconnect(); /* * Handle results. */ if (empty($users)) { //... } else { //... }
调用的适配器方法是public:
public
就这样 :-)
<?php /* * Database adapter. */ /** * Database adapter. */ class DbAdapter { /** * Connection configs. * * @var array */ private $connectionConfigs; /** * Database connection. * * @var PDO */ private $connection; /** * PDO statement. * * @var PDOStatement */ private $statement; /** * * @param string $host [optional] Host. * @param string $dbname [optional] Database name. * @param string $username [optional] User name. * @param string $password [optional] Password. * @param string $port [optional] Port. * @param string $charset [optional] Character set. * @param string $driverName [optional] Driver name. * @param array $driverOptions [optional] Driver options. * @return string DSN string. */ public function __construct($host = '', $dbname = '' , $username = '', $password = '', $port = 3306, $charset = 'utf8', $driverName = 'mysql' , $driverOptions = array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => FALSE, PDO::ATTR_PERSISTENT => TRUE, )) { $this->setConnectionConfigs(array( 'host' => $host, 'dbname' => $dbname, 'username' => $username, 'password' => $password, 'port' => $port, 'charset' => $charset, 'driverName' => $driverName, 'driverOptions' => $driverOptions, )); } /** * Connect to db, e.g. create a PDO instance. * * @return $this * @throws PDOException */ public function connect() { if (!isset($this->connection) || !$this->connection) { try { $this->connection = new PDO( $this->createDsn( $this->connectionConfigs['host'] , $this->connectionConfigs['dbname'] , $this->connectionConfigs['port'] , $this->connectionConfigs['charset'] , $this->connectionConfigs['driverName'] ) , $this->connectionConfigs['username'] , $this->connectionConfigs['password'] , $this->connectionConfigs['driverOptions'] ); } catch (PDOException $pdoException) { echo $pdoException->getMessage(); exit(); } } return $this; } /** * Disconnect from db. * * @return $this */ public function disconnect() { $this->connection = NULL; return $this; } /** * Create a DSN string. * * @param string $host Host. * @param string $dbname Database name. * @param string $port Port. * @param string $charset Character set. * @param string $driverName Driver name. * @return string DSN string. */ private function createDsn($host, $dbname, $port, $charset, $driverName) { switch ($driverName) { default: // mysql $dsn = sprintf('%s:host=%s;port=%s;dbname=%s;charset=%s' , $driverName , $host , $port , $dbname , $charset ); break; } return $dsn; } /** * Fetch data by executing a SELECT sql statement. * * @param string $sql Sql statement. * @param array $bindings [optional] Input parameters. * @param integer $fetchMode [optional] Fetch mode for a PDO statement. * Must be one of the PDO::FETCH_* constants. * @param mixed $fetchArgument [optional] Fetch argument for a PDO statement. * @param array $fetchConstructorArguments [optional] Constructor arguments for a PDO statement * when fetch mode is PDO::FETCH_CLASS. * @return array An array containing the rows in the result set, or FALSE on failure. * @throws UnexpectedValueException */ public function fetchAll($sql, array $bindings = array(), $fetchMode = PDO::FETCH_ASSOC, $fetchArgument = NULL, array $fetchConstructorArguments = array()) { $this ->prepareStatement($sql) ->bindInputParameters($bindings) ->executePreparedStatement() ; try { if (isset($fetchArgument)) { $data = $this->getStatement()->fetchAll($fetchMode, $fetchArgument, $fetchConstructorArguments); } else { $data = $this->getStatement()->fetchAll($fetchMode); } if ($data === FALSE) { throw new UnexpectedValueException('Fetching data failed!'); } return $data; } catch (Exception $exception) { echo $exception->getMessage(); exit(); } } /** * Fetch the next row from the result set by executing a SELECT sql statement. * The fetch mode property determines how PDO returns the row. * * @param string $sql Sql statement. * @param array $bindings [optional] Input parameters. * @param integer $fetchMode [optional] Fetch mode for a PDO statement. * Must be one of the PDO::FETCH_* constants. * @param integer $fetchCursorOrientation [optional] For a PDOStatement object representing * a scrollable cursor, this value determines which row will be returned to the caller. * @param integer $fetchCursorOffset [optional] The absolute number of the row in the result * set, or the row relative to the cursor position before PDOStatement::fetch() was called. * @return array An array containing the next row in the result set, or FALSE on failure. * @throws Exception */ public function fetchOne($sql, array $bindings = array(), $fetchMode = PDO::FETCH_ASSOC, $fetchCursorOrientation = PDO::FETCH_ORI_NEXT, $fetchCursorOffset = 0) { $this ->prepareStatement($sql) ->bindInputParameters($bindings) ->executePreparedStatement() ; try { /* * ========================================================= * NB: * ========================================================= * PDOStatement::fetch returns FALSE not only on failure, * but ALSO when no record is found! This is a BUG. That's * why I made the try-catch block: maybe on failure will * throw an exception. * * Instead, PDOStatement::fetchAll returns FALSE on failure, * but an empty array if no record is found. This is the * correct behaviour. * ========================================================= */ $data = $this->getStatement()->fetch($fetchMode, $fetchCursorOrientation, $fetchCursorOffset); return $data; } catch (Exception $exception) { echo $exception->getMessage(); exit(); } } /** * Returns a single column from the next row of a result set * or FALSE if there are no more rows. * * ================================================================= * Note: * ----- * PDOStatement::fetchColumn() should not be used to retrieve * boolean columns, as it is impossible to distinguish a value * of FALSE from there being no more rows to retrieve. * Use PDOStatement::fetch() instead. * * Warning: * -------- * There is no way to return another column from the same row if you * use PDOStatement::fetchColumn() to retrieve data. * ================================================================= * * @param string $sql Sql statement. * @param array $bindings [optional] Input parameters. * @param integer $columnNumber [optional] 0-indexed number of the * column you wish to retrieve from the row. If no value is supplied, * PDOStatement::fetchColumn() fetches the first column. * @return mixed A single column from the next row of a result set * or FALSE if there are no more rows. * @throws Exception */ public function fetchColumn($sql, array $bindings = array(), $columnNumber = 0) { $this ->prepareStatement($sql) ->bindInputParameters($bindings) ->executePreparedStatement() ; try { return $this->getStatement()->fetchColumn($columnNumber); } catch (Exception $exception) { echo $exception->getMessage(); exit(); } } /** * Store data by executing an INSERT sql statement. * * @param string $sql Sql statement. * @param array $bindings [optional] Input parameters. * @return int Last insert id. */ public function insert($sql, array $bindings = array()) { $this ->prepareStatement($sql) ->bindInputParameters($bindings) ->executePreparedStatement() ; return $this->getLastInsertId(); } /** * Update data by executing an UPDATE sql statement. * * @param string $sql Sql statement. * @param array $bindings [optional] Input parameters. * @return int Number of affected rows. */ public function update($sql, array $bindings = array()) { $this ->prepareStatement($sql) ->bindInputParameters($bindings) ->executePreparedStatement() ; return $this->getStatement()->rowCount(); } /** * Delete data by executing a DELETE sql statement. * * @param string $sql Sql statement. * @param array $bindings [optional] Input parameters. * @return int Number of affected rows. */ public function delete($sql, array $bindings = array()) { $this ->prepareStatement($sql) ->bindInputParameters($bindings) ->executePreparedStatement() ; return $this->getStatement()->rowCount(); } /** * Prepare and validate an sql statement. * * ---------------------------------------------------- * If the database server cannot successfully prepare * the statement, PDO::prepare() returns FALSE or emits * PDOException (depending on error handling settings). * ---------------------------------------------------- * * @param string $sql Sql statement. * @return $this * @throws PDOException * @throws UnexpectedValueException */ private function prepareStatement($sql) { $this->connect(); try { $statement = $this->getConnection()->prepare($sql); if (!$statement) { throw new UnexpectedValueException('The sql statement can not be prepared!'); } $this->setStatement($statement); } catch (PDOException $pdoException) { echo $pdoException->getMessage(); exit(); } catch (Exception $exception) { echo $exception->getMessage(); exit(); } return $this; } /** * Bind the input parameters to a prepared PDO statement. * * @param array $bindings Input parameters. * @return $this * @throws UnexpectedValueException */ private function bindInputParameters($bindings) { foreach ($bindings as $key => $value) { try { $bound = $this->getStatement()->bindValue( $this->getInputParameterName($key) , $value , $this->getInputParameterDataType($value) ); if (!$bound) { throw new UnexpectedValueException('A value can not be bound!'); } } catch (Exception $exception) { echo $exception->getMessage(); exit(); } } return $this; } /** * Get the name of an input parameter by its key in the bindings array. * * @param int|string $key The key of the input parameter in the bindings array. * @return int|string The name of the input parameter. */ private function getInputParameterName($key) { return is_int($key) ? ($key + 1) : (':' . ltrim($key, ':')); } /** * Get the PDO::PARAM_* constant, e.g the data type of an input parameter, by its value. * * @param mixed $value Value of the input parameter. * @return int The PDO::PARAM_* constant. */ private function getInputParameterDataType($value) { $dataType = PDO::PARAM_STR; if (is_int($value)) { $dataType = PDO::PARAM_INT; } elseif (is_bool($value)) { $dataType = PDO::PARAM_BOOL; } return $dataType; } /** * Execute a prepared PDO statement. * * @return $this * @throws UnexpectedValueException */ private function executePreparedStatement() { try { if (!$this->getStatement()->execute()) { throw new UnexpectedValueException('The statement can not be executed!'); } } catch (Exception $exception) { echo $exception->getMessage(); exit(); } return $this; } /** * Get the ID of the last inserted row or of the sequence value. * * @param string $sequenceObjectName [optional] Name of the sequence object * from which the ID should be returned. * @return string The ID of the last row, or the last value retrieved from the specified * sequence object, or an error IM001 SQLSTATE If the PDO driver does not support this. * @throws PDOException */ public function getLastInsertId($sequenceObjectName = NULL) { $this->connect(); try { return $this->getConnection()->lastInsertId($sequenceObjectName); } catch (PDOException $pdoException) { echo $pdoException->getMessage(); exit(); } } /** * Get connection configs. * * @return array */ public function getConnectionConfigs() { return $this->connectionConfigs; } /** * Set connection configs. * * @param array $connectionConfigs Connection configs. * @return $this */ public function setConnectionConfigs($connectionConfigs) { $this->connectionConfigs = $connectionConfigs; return $this; } /** * Get database connection. * * @return PDO Database connection. */ public function getConnection() { return $this->connection; } /** * Set database connection. * * @param PDO $connection Database connection. * @return $this */ public function setConnection(PDO $connection) { $this->connection = $connection; return $this; } /** * Get PDO statement. * * @return PDOStatement */ public function getStatement() { return $this->statement; } /** * Set PDO statement. * * @param PDOStatement $statement PDO statement. * @return $this */ public function setStatement(PDOStatement $statement) { $this->statement = $statement; return $this; } }