PHP MySQL 创建数据库

Running CREATE DATABASE via PDO, choosing a charset, and least privilege.

Beginner
Key PointsAlways specify `utf8mb4` — the default charset is often `latin1`.

Creating a database

PHP
<?php
// 建库时还没指定 dbname,所以 DSN 里不能带
$dsn = "mysql:host=127.0.0.1;charset=utf8mb4";
$pdo = new PDO($dsn, "root", "secret", [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);

$sql = "CREATE DATABASE IF NOT EXISTS demo
        DEFAULT CHARACTER SET utf8mb4
        COLLATE utf8mb4_unicode_ci";

$pdo->exec($sql);
echo "数据库 demo 已就绪\n";

$dbs = $pdo->query("SHOW DATABASES")->fetchAll(PDO::FETCH_COLUMN);
echo in_array("demo", $dbs, true) ? "已确认存在" : "不存在", "\n";
Output
数据库 demo 已就绪
已确认存在

Charset and collation

SettingBytes/charEmojiRecommendation
utf8(别名 utf8mb3)最多 3不支持不要用
utf8mb4最多 4支持推荐
latin11不支持只适合纯英文
CollationCaseAccentUse
utf8mb4_general_ci不敏感不敏感旧默认,够快
utf8mb4_unicode_ci不敏感不敏感Unicode 规则更准
utf8mb4_0900_ai_ci不敏感不敏感MySQL 8 默认,最快
utf8mb4_bin敏感敏感需要严格区分时
_ci means case-insensitive: WHERE name = 'tom' matches Tom. For tokens or invite codes use utf8mb4_bin or add BINARY.

Checking existing databases

PHP
<?php
$pdo = new PDO($dsn, $user, $pass, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);

// 查库是否存在(用预处理,别拼字符串)
$stmt = $pdo->prepare("SELECT SCHEMA_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = ?");
$stmt->execute(["demo"]);
$exists = $stmt->fetchColumn();

echo $exists ? "库已存在: $exists" : "库不存在", "\n";

// 查库的字符集
$stmt = $pdo->prepare("SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
                       FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = ?");
$stmt->execute(["demo"]);
print_r($stmt->fetch());
Output
库已存在: demo
Array
(
    [DEFAULT_CHARACTER_SET_NAME] => utf8mb4
    [DEFAULT_COLLATION_NAME] => utf8mb4_unicode_ci
)

Database names cannot be bound

CREATE DATABASE ? is a syntax error — DDL identifiers cannot be bound. Database, table and column names must be interpolated, so validate against an allowlist.

PHP
<?php
function safeIdent(string $name): string {
    if (!preg_match('/^[A-Za-z_][A-Za-z0-9_]{0,63}$/', $name)) {
        throw new InvalidArgumentException("非法标识符: $name");
    }
    return "`" . $name . "`";
}

foreach (["demo", "my_db2", "bad-name", "1abc", "x`; DROP TABLE users; --"] as $n) {
    try {
        echo safeIdent($n) . "\n";
    } catch (InvalidArgumentException $e) {
        echo "拒绝: " . $e->getMessage() . "\n";
    }
}
Output
`demo`
`my_db2`
拒绝: 非法标识符: bad-name
拒绝: 非法标识符: 1abc
拒绝: 非法标识符: x`; DROP TABLE users; --
The last input is identifier injection. Only a strict allowlist regex stops it — letters, digits, underscore, max 64 characters.

Least privilege

SQL
-- 应用账号:只给需要的权限,不给 DROP
CREATE USER 'app'@'127.0.0.1' IDENTIFIED BY '强密码';

GRANT SELECT, INSERT, UPDATE, DELETE
    ON demo.* TO 'app'@'127.0.0.1';

-- 需要建表时再加
-- GRANT CREATE, ALTER, INDEX ON demo.* TO 'app'@'127.0.0.1';

FLUSH PRIVILEGES;

Never connect as root. A successful injection with root means reading every database, writing files, even executing commands via INTO OUTFILE and UDFs.

Friends & Partners