简体中文
版本: v2.3.0

select

从数据表中查询数据。

select($table, $columns)
select($table, $columns, $where)
select($table, $join, $columns, $where)
返回值
[array] 返回查询到的记录数组。
可以在 columns 参数中使用 * 查询所有列,但为了性能和可读性,建议显式指定真正需要的列。
$data = $database->select("account", [
	"user_name",
	"email"
], [
	"user_id[>]" => 100
]);

// 示例结果:
// array(
// [0] => array(
//	  "user_name" => "foo",
//	  "email" => "foo@bar.com"
// ),
// [1] => array(
//	  "user_name" => "cat",
//	  "email" => "cat@dog.com"
// )
// )

foreach ($data as $item) {
	echo "用户:" . $item["user_name"] . " - 电子邮件:" . $item["email"] . "<br/>";
}

// 选择所有列。
$data = $database->select("account", "*");

// 选择单个列。
$data = $database->select("account", "user_name");

// 示例结果:
// array(
// [0] => "foo",
// [1] => "cat"
// )

使用回调逐行处理结果

默认情况下,select() 会先将完整结果集载入内存,再以数组形式返回。
当结果行数较多时,这会显著增加内存占用。如果把 function ($data) {} 这类回调函数作为 select() 的最后一个参数传入,Medoo 会在取到每一行后立刻处理,而不是先把整份结果集全部放进内存。
在处理大型数据集时,这种方法的内存效率更高。
$database->select("account", ["name"], function ($data) {
	echo $data["name"];
});

$database->select("account", [
	"name"
], function ($data) {
	echo $data["name"];
});
内存占用对比
以下示例比较从 MySQL 数据库获取和输出 1,000、5,000 和 20,000 行时的内存使用情况。内存使用情况通过 memory_get_usage() 来测量。
// 方法 1
$database->select("account", ["name"], function ($data) {
	echo $data["name"];
});

// 对比方法:

// 方法 2
$data = $database->select("account", ["name"]);

foreach ($data as $item) {
	echo $item["name"];
}
方法 1 方法 2
1,000 条记录 789 KB 1.2 MB
5,000 条记录 1.1 MB 3.3 MB
20,000 条记录 2.26 MB 11.1 MB

表关联(JOIN)

SQL 的 JOIN 子句用于把多个数据表中的记录关联起来。Medoo 提供了更直观的 JOIN 语法。
$database->select("post", [
	// 定义主表与连接表之间的关系。
	"[>]account" => ["author_id" => "user_id"]
], [
	"post.title",
	"account.city"
]);
post 表中的 author_id 列与 account 表中的 user_id 列相匹配。
"[>]account" => ["author_id" => "user_id"]
LEFT JOIN "account" ON "post"."author_id" = "account"."user_id"
如果两个表使用相同的列名,则可以使用简写形式。
"[>]album" => "user_id"
LEFT JOIN "album" USING ("user_id")
如果两个表中的多个列共享相同的名称,则可以将它们作为数组传递。
"[>]photo" => ["user_id", "avatar_id"]
LEFT JOIN "photo" USING ("user_id", "avatar_id")
如果需要多次连接同一个表,请为连接的表分配一个别名。
"[>]account (replier)" => ["replier_id" => "user_id"]
LEFT JOIN "account" AS "replier" ON "post"."replier_id" = "replier"."user_id"
您还可以通过在列前添加表名来引用先前连接的表。
"[>]account" => ["author_id" => "user_id"],
"[>]album" => ["account.user_id" => "user_id"]
LEFT JOIN "account" ON "post"."author_id" = "account"."user_id"
LEFT JOIN "album" ON "account"."user_id" = "album"."user_id"
多个 JOIN 条件
"[>]account" => [
	"author_id" => "user_id",
	"album.user_id" => "user_id"
]
LEFT JOIN "account" ON
"post"."author_id" = "account"."user_id" AND
"album"."user_id" = "account"."user_id"
附加 JOIN 条件
您还可以向连接子句添加逻辑条件。
"[>]comment" => [
	"author_id" => "user_id",
	"AND" => [
		"rate[>]" => 50
	]
]
LEFT JOIN "comment" ON "post"."author_id" = "comment"."user_id" AND "rate" > 50
使用 Raw 对象自定义 JOIN 条件
"[>]account" => Medoo::raw("ON <post.author_id> = <account.user_id>")
LEFT JOIN "account" ON "post"."author_id" = "account"."user_id"

结果映射

您可以自定义返回数据的结构。映射键不必与原始列名一致,也支持嵌套输出。
$data = $database->select("post", [
	"[>]account" => ["user_id"]
], [
	"post.content",

	"userData" => [
		"account.user_id",
		"account.email",

		"meta" => [
			"account.location",
			"account.gender"
		]
	]
], [
	"LIMIT" => [0, 2]
]);

echo json_encode($data);
[
	{
		"content": "Hello world!",
		"userData": {
			"user_id": "1",
			"email": "foo@example.com",
			"meta": {
				"location": "New York",
				"gender": "male"
			}
		}
	},
	{
		"content": "Hey everyone",
		"userData": {
			"user_id": "2",
			"email": "bar@example.com",
			"meta": {
				"location": "London",
				"gender": "female"
			}
		}
	}
]

索引映射

如果您使用列名作为列定义中的第一个键,则结果将按该列建立索引。
$data = $database->select("post", [
	"user_id" => [
		"nickname",
		"location",
		"email"
	]
]);
{
	"10": {
		"nickname": "foo",
		"location": "New York",
		"email": "foo@example.com"
	},
	"12": {
		"nickname": "bar",
		"location": "New York",
		"email": "bar@medoo.in"
	}
}

数据类型声明

您可以显式声明所选字段的输出类型。
// 支持的数据类型:[String | Bool | Int | Number | Object | JSON]
// [String] 是所有输出值的默认类型。
// [Object] 表示使用 serialize() 序列化并用 unserialize() 还原的 PHP 数据。
// [JSON] 表示有效的 JSON 数据,并会使用 json_decode() 解码。

$data = $database->select("post", [
	"[>]account" => ["user_id"]
], [
	"post.post_id",

	"profile" => [
		"account.age [Int]",
		"account.is_locked [Bool]",
		"account.userData [JSON]"
	]
]);

echo json_encode($data);
[
	{
		"post_id": "1",
		"profile": {
			"age": 20,
			"is_locked": true,
			"userData": ["foo", "bar", "tim"]
		}
	},
	{
		"post_id": "2",
		"profile": {
			"age": 25,
			"is_locked": false,
			"userData": ["mydata1", "mydata2"]
		}
	}
]
// 先将对象存入数据库,之后再取出。
class Foo {
	var $bar = "cat";

	public function __wakeup()
	{
		$this->bar = "dog";
	}
}

$object_data = new Foo();

$database->insert("account", [
	"data" => $object_data
]);

$data = $database->select("account", [
	"data [Object]"
]);

echo $data[0]["data"]->bar;

// 在 unserialize() 期间会调用对象的 __wakeup() 方法。
// 因此,输出为“dog”。

别名

您可以为列名或表名指定别名。这在连接查询中特别有用,可以避免名称冲突或使输出更清晰。
$data = $database->select("account", [
	"user_id",
	"nickname (my_nickname)"
]);

// 示例结果:
// array(
// [0] => array(
//	  "user_id" => "1",
//	  "my_nickname" => "foo"
// ),
// [1] => array(
//	  "user_id" => "2",
//	  "my_nickname" => "bar"
// )
// )

$data = $database->select("post (content)", [
	"[>]account (user)" => "user_id",
], [
	"content.user_id (author_id)",
	"user.user_id"
]);

// 示例结果:
// array(
// [0] => array(
//	  "author_id" => "1",
//	  "user_id" => "321"
// ),
// [1] => array(
//	  "author_id" => "2",
//	  "user_id" => "322"
// )
// )
SELECT
	"content"."user_id" AS "author_id",
	"user"."user_id"
FROM
	"post" AS "content"
LEFT JOIN "account" AS "user" USING ("user_id")

DISTINCT

要将 DISTINCT 关键字添加到选定列,请在列名称前加上 @ 前缀。
$data = $database->select("account", [
	// DISTINCT 将应用到这一列。
	"@location",
	
	"id",
	"name",
]);
SELECT DISTINCT "location", "id", "name"
FROM "account"
要对不同值进行计数,请使用原生 SQL 表达式。
$data = $database->select("account", [
	"unique_locations" => Medoo::raw("COUNT(DISTINCT <location>)")
]);
SELECT COUNT(DISTINCT "location") AS "unique_locations"
FROM "account"