SQL Server Express LocalDB 是 SQL Server Express 的一种轻量级用户实例模式,仅支持 Windows。 两个驱动程序连接到 LocalDB 实例的方式,与连接任何命名实例的方式相同,都是使用 (localdb)\<instance-name> 服务器名称格式。
备注
LocalDB 运行在 Windows 用户账户下。 这些程序使用Windows集成认证,而非Microsoft Entra认证。 当每个用户的数据库实例适合你的工作流程时,可以用它进行本地 Windows 开发。 它不是容器镜像,也不是 Azure 服务。
Note
Linux和macOS上都没有LocalDB。 改为连接到受支持主机上的 SQL Server 实例。 对于容器,请查看受支持的 Linux 平台上的 SQL Server;不要假设每个 macOS 或 Linux 体系结构都能运行 SQL Server 容器映像。
连接到 LocalDB 实例
安装一个支持的 SQL Server Express LocalDB 版本,以及你选择的 ODBC 前置条件和 PHP 扩展。 LocalDB 安装未加载 PHP 扩展。
- 在 Windows 终端里运行
sqllocaldb.exe i,查看你的实例列表。 - 如果
MSSQLLocalDB不存在,则运行sqllocaldb.exe create MSSQLLocalDB。 - 运行
sqllocaldb.exe start MSSQLLocalDB,然后运行sqllocaldb.exe info MSSQLLocalDB以检查该实例。 以运行PHP程序的Windows账户执行这些步骤。 - 保存并使用命令行 PHP 运行其中一个程序。 每个实例默认连接到
master并验证目标是否真的是 LocalDB。 不需要示例数据库或SQL密码。
实例名称仅对该 Windows 计算机及其所属账户有效。 它不是 DNS 主机名、TCP 端口,也不是与另一台计算机的连接。
SQLSRV
将该程序保存为 connect-localdb-sqlsrv.php,并运行 php connect-localdb-sqlsrv.php。
<?php
$instance = getenv('LOCALDB_INSTANCE') ?: 'MSSQLLocalDB';
$database = getenv('LOCALDB_DATABASE');
if ($database === false || $database === '') {
$database = 'master';
}
$file = getenv('LOCALDB_MDF');
$databaseValue = '{' . str_replace('}', '}}', $database) . '}';
$options = ['Database' => $databaseValue, 'LoginTimeout' => 30, 'CharacterSet' => 'UTF-8'];
if ($file !== false && $file !== '') {
if ($database === 'master' || !is_file($file)) {
throw new RuntimeException('Set a non-master database name and an accessible MDF path.');
}
$options['AttachDBFileName'] = '{' . str_replace('}', '}}', $file) . '}';
}
$conn = sqlsrv_connect('(localdb)\\' . $instance, $options);
if ($conn === false) {
throw new RuntimeException(print_r(sqlsrv_errors(), true));
}
$stmt = null;
try {
$stmt = sqlsrv_query(
$conn,
"SELECT DB_NAME() AS DatabaseName, CAST(SERVERPROPERTY('IsLocalDB') AS int) AS IsLocalDB"
);
if ($stmt === false) {
throw new RuntimeException(print_r(sqlsrv_errors(), true));
}
$row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
if ($row === false) {
throw new RuntimeException(print_r(sqlsrv_errors(), true));
}
if ($row !== ['DatabaseName' => $database, 'IsLocalDB' => 1]) {
throw new RuntimeException('Expected the selected LocalDB database.');
}
echo 'LocalDB database: ', $row['DatabaseName'], PHP_EOL;
} finally {
if (is_resource($stmt)) {
sqlsrv_free_stmt($stmt);
}
sqlsrv_close($conn);
}
PDO_SQLSRV
将这个独立程序保存为 connect-localdb-pdo.php,并运行 php connect-localdb-pdo.php。
<?php
$instance = getenv('LOCALDB_INSTANCE') ?: 'MSSQLLocalDB';
$database = getenv('LOCALDB_DATABASE');
if ($database === false || $database === '') {
$database = 'master';
}
$file = getenv('LOCALDB_MDF');
$databaseValue = '{' . str_replace('}', '}}', $database) . '}';
$dsn = "sqlsrv:Server=(localdb)\\$instance;Database=$databaseValue;LoginTimeout=30";
if ($file !== false && $file !== '') {
if ($database === 'master' || !is_file($file)) {
throw new RuntimeException('Set a non-master database name and an accessible MDF path.');
}
$dsn .= ';AttachDBFileName={' . str_replace('}', '}}', $file) . '}';
}
$pdo = new PDO($dsn, null, null, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::SQLSRV_ATTR_FETCHES_NUMERIC_TYPE => true,
PDO::SQLSRV_ATTR_ENCODING => PDO::SQLSRV_ENCODING_UTF8,
]);
$stmt = null;
try {
$stmt = $pdo->query(
"SELECT DB_NAME() AS DatabaseName, CAST(SERVERPROPERTY('IsLocalDB') AS int) AS IsLocalDB"
);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($row !== ['DatabaseName' => $database, 'IsLocalDB' => 1]) {
throw new RuntimeException('Expected the selected LocalDB database.');
}
echo 'LocalDB database: ', $row['DatabaseName'], PHP_EOL;
} finally {
$stmt = null;
$pdo = null;
}
默认配置下的预期输出:
LocalDB database: master
SQLSRV 会返回一个连接资源,并将连接失败报告为 false。 PDO 会返回一个对象,如果构造失败,则会抛出 PDOException。 PDO 程序显式启用数值获取,因此 int 值是 PHP int,与 SQLSRV 的结果一致。 两者都对字符数据使用 UTF-8,并且即使查询失败,也会释放语句和连接句柄。
选择现有数据库或附加文件
程序接受以下可选的过程变量:
| 变量 | Meaning |
|---|---|
LOCALDB_INSTANCE |
不带 (localdb)\ 前缀的实例名。 默认值为 MSSQLLocalDB. |
LOCALDB_DATABASE |
现有数据库名称,或要为附加数据库指定的名称。 默认值为 master. |
LOCALDB_MDF |
要附加的现有且可访问的 .mdf 文件的绝对路径。 连接已注册数据库时省略它。 |
要使用已在该实例中注册的数据库,请设置 LOCALDB_DATABASE,并保持 LOCALDB_MDF 未设置。 要附加现有数据库文件,请同时设置这两项。 例如,在 PowerShell 中:
$env:LOCALDB_DATABASE = 'myData'
$env:LOCALDB_MDF = 'C:\Data\myData.mdf'
使用为此目的预编译数据库文件,而不是仍附加到另一个正在运行的实例的文件。 授予Windows账户数据库文件及其目录的访问权限。
AttachDBFileName 不会创建缺失的MDF文件、迁移数据库或解决不兼容的数据库文件版本。 附加操作会将数据库注册到该实例;关闭 PHP 并不会分离或删除它。 如果你需要分别验证两个 API,请从分离状态测试每个 API 的附加路径。
服务器字符串的反斜线在PHP单引号字符串中转义为 \\。 PHP 双引号字符串还会解释反斜杠和变量,因此 PDO 程序会有意构造实例字符串。 这两个程序都会在连接时对数据库名称进行转义,同时保留原始名称以用于验证和结果检查。 它们还会在转义附加操作的值之前,先检查实际的文件系统路径。 对于每个连接值,它们都会将右花括号写成两个,并用花括号将该值括起来。 SQLSRV 要求即使是在其连接数组中提供的字符串选项,也必须进行这种转义。 将 LOCALDB_DATABASE 和 LOCALDB_MDF 设置为实际名称和路径,不要自行添加或双写花括号。
这些连接使用本地实例,而非远程服务器。 对于远程SQL Server、Azure SQL 数据库、Azure SQL 托管实例、Azure Synapse Analytics、Fabric中的SQL数据库或Fabric中的Warehouse,使用实际的服务端点。 明确设置认证、加密和证书验证。 更多信息请参见“连接服务器”。
诊断账户和实例问题
你也可以用命令行通过 sqlcmd 管理 LocalDB 实例的数据库,例如 sqlcmd -S "(localdb)\MSSQLLocalDB"。 你的交互式 Windows 帐户所拥有的 LocalDB 实例,不会自动授予 Internet Information Services (IIS) 应用程序池标识访问权限。 更改运行PHP的账户时,检查实例所有权和文件权限。 更多信息请参见 SQL Server Express LocalDB。
| 症状 | 检查 |
|---|---|
sqllocaldb.exe 未找到 |
确认 LocalDB 已安装,且其工具可在终端中使用。 仅仅安装PHP驱动是不够的。 |
| 该实例未被发现 | 以运行 PHP 的同一 Windows 帐户列出实例,并检查 LOCALDB_INSTANCE。 |
| 初始连接时间太长 | 明确启动实例,并使用文档中记录的登录超时时间重试。 调查实例启动失败,而不是无限期重试。 |
master 能用,但 myData 不行 |
确认该实例中数据库是否存在,或者配置一个可访问的附件文件和数据库名称。 |
| 文件附加失败 | 检查准确路径、文件所有权、目录权限、文件是否已被使用以及 LocalDB 版本兼容性。 |
| 终端PHP能用,但IIS不行 | 比较账户、实例所有权、文件访问、运行时配置和扩展加载情况。 不要假设交互式测试能证明有 Web 托管访问权限。 |
LocalDB 的启动和按用户所有权属于 SQL Server LocalDB 和 Windows。 PHP 扩展通过 ODBC 提供连接选项。 不要添加SQL凭证或Microsoft Entra选项来弥补实例所有权问题。