php - 如果使用参数绑定(bind),PHP 中的查询无法在 SQL Server 中查看临时表

标签 php sql-server

以下代码按预期工作(假设变量存在):

$connectionInfo = ['Database'=>$dbName, 'UID'=>$username, 'PWD'=>$pwd, 'ReturnDatesAsStrings'=>true, 'CharacterSet'=>'UTF-8'];
$conn           = sqlsrv_connect($server, $connectionInfo);

$select  = sqlsrv_query($conn, 'SELECT * INTO #mytable_temp FROM mytable WHERE myfield = \'myvalue\'', []);
$select2 = sqlsrv_query($conn, 'SELECT * FROM #mytable_temp ', []);

if (!$select2) {
    $errors = sqlsrv_errors();
    var_dump($errors);
} else {
    $res = sqlsrv_fetch_array($select2, SQLSRV_FETCH_ASSOC);
    var_dump($res);
}

但是,如果我将 $select 更改为以下内容,它将不起作用:

$select  = sqlsrv_query($conn, 'SELECT * INTO #mytable_temp FROM mytable WHERE myfield = ?', ['myvalue']);

运行第二条语句时出现错误,提示“无效的对象名称‘#mytable_temp’”。为什么使用参数绑定(bind)会导致临时表不可见?

我知道如果我在同一个 sqlsrv_query() 语句中包含这两个语句,我可以让它工作,但这不是我的用例的选项。我也确实知道如果使用全局 (##mytable_temp) 表它会起作用,但这也不是一个选项。

我正在运行 PHP 5.4.12 并在 SQL Server 11.0.3 (2012 SP1) 和 10.50.4000 (2008 SP2) 上尝试了代码。

最佳答案

这是我对使用参数的 SELECT INTO 查询后为什么看不到临时表的解释。

考虑这个 T-SQL 代码(使用 MyTable 创建和填充,如下所示):

DECLARE @stmt nvarchar(max) = 'SELECT * INTO #mytable_temp FROM mytable WHERE myfield = @P1';
EXECUTE sp_executesql @stmt, N'@P1 varchar(50)', @P1 = 'Value1';

如果您在 SSMS 中运行它,它运行良好,并且消息窗口中的输出显示:

(2 row(s) affected)

尝试在同一个 SSMS 窗口中将上述代码添加一行并运行整个批处理:

DECLARE @stmt nvarchar(max) = 'SELECT * INTO #mytable_temp FROM mytable WHERE myfield = @P1';
EXECUTE sp_executesql @stmt, N'@P1 varchar(50)', @P1 = 'Value1';
SELECT * FROM #mytable_temp;

输出是:

(2 row(s) affected)
Msg 208, Level 16, State 0, Line 3
Invalid object name '#mytable_temp'.

原因是sp_executesql在嵌套存储过程的范围内执行带参数的语句,在存储过程中创建的临时表对该存储过程的调用者是不可见的。

Execute sp_executeSql for select...into #table but Can't Select out Temp Table Data

https://msdn.microsoft.com/en-us/library/ms174979.aspx

A local temporary table created in a stored procedure is dropped automatically when the stored procedure is finished. The table can be referenced by any nested stored procedures executed by the stored procedure that created the table. The table cannot be referenced by the process that called the stored procedure that created the table.

当您准备带有参数的 SQL 语句时,PHP 最终会调用 sp_executesql(很可能,尽管我没有跟踪它)。你会得到这个记录在案的行为——在这个存储过程中创建一个临时表作为查询的一部分,并在 sp_executesql 返回时立即删除。当您运行不带参数的 SQL 语句时,PHP 不使用 sp_executesql 将其发送到服务器。


想到的解决方法很少。

  • 将多条 SQL 语句放入一个长字符串中,并通过一次调用 sqlsrv_query 来运行它。

  • 创建一个带参数的存储过程并在其中放入几条 SQL 语句,然后通过一次调用 sqlsrv_query 来调用您的过程。 (我个人更喜欢这种方法)。

  • 显式创建(并可选择删除)临时表。

这是我用来验证最后一个解决方法是否有效的代码。通过 PHP 5.4.28、SQL Server Express 2014、Microsoft 驱动程序 PHP SQLSRV 3.2 验证。它使用额外的 CREATE TABLE 语句显式创建临时表,然后使用 INSERT INTO 而不是单个 SELECT INTO 语句。

创建测试表并填充一些数据

CREATE TABLE [dbo].[MyTable](
    [ID] [int] NOT NULL,
    [MyField] [varchar](50) NOT NULL,
CONSTRAINT [PK_MyTable] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
))

INSERT INTO [dbo].[MyTable] ([ID],[MyField]) VALUES
(1, 'Value1'),
(2, 'Value2'),
(3, 'Value3'),
(4, 'Value1')

运行 php 脚本

$connectionInfo = array("Database" => "tempdb");
$conn = sqlsrv_connect($serverName, $connectionInfo);

if ($conn)
{
     echo "Connection established.\n";
}
else
{
     echo "Connection could not be established.\n";
     die( print_r( sqlsrv_errors(), true));
}

echo "Running CREATE TABLE ...\n";
$sql_create = "CREATE TABLE #mytable_temp([ID] [int] NOT NULL, [MyField] [varchar](50) NOT NULL)";
$stmt_create = sqlsrv_query($conn, $sql_create);
if( $stmt_create === false )
{
    echo "CREATE TABLE failed\n";
    die( print_r( sqlsrv_errors(), true));
}
else
{
    echo "CREATE TABLE result set:\n";
    while ($row = sqlsrv_fetch_array($stmt_create))
    {
        var_dump($row);
    }
}
sqlsrv_free_stmt($stmt_create);


echo "Running INSERT INTO with param ...\n";
$select_into = "INSERT INTO #mytable_temp(ID, MyField) SELECT ID, MyField FROM MyTable WHERE MyField = ?";

$search = "Value1";
$params = array
    (
    array(&$search, SQLSRV_PARAM_IN)
    );
$stmt_into = sqlsrv_query($conn, $select_into, $params);
if( $stmt_into === false )
{
    echo "INSERT INTO failed\n";
    die( print_r( sqlsrv_errors(), true));
}
else
{
    echo "INSERT INTO result set:\n";
    while ($row = sqlsrv_fetch_array($stmt_into))
    {
        var_dump($row);
    }
}
sqlsrv_free_stmt($stmt_into);


echo "Running SELECT FROM ...\n";
$select_from = "SELECT * FROM #mytable_temp";
$stmt_from = sqlsrv_query($conn, $select_from);
if( $stmt_from === false )
{
    echo "SELECT FROM failed\n";
    die( print_r( sqlsrv_errors(), true));
}
else
{
    echo "SELECT FROM result set:\n";
    while ($row = sqlsrv_fetch_array($stmt_from))
    {
        var_dump($row);
    }
}

echo "end\n";

脚本的输出

Connection established.
Running CREATE TABLE ...
CREATE TABLE result set:
Running INSERT INTO with param ...
INSERT INTO result set:
Running SELECT FROM ...
SELECT FROM result set:
array(4) {
  [0]=>
  int(1)
  ["ID"]=>
  int(1)
  [1]=>
  string(6) "Value1"
  ["MyField"]=>
  string(6) "Value1"
}
array(4) {
  [0]=>
  int(4)
  ["ID"]=>
  int(4)
  [1]=>
  string(6) "Value1"
  ["MyField"]=>
  string(6) "Value1"
}
end

关于php - 如果使用参数绑定(bind),PHP 中的查询无法在 SQL Server 中查看临时表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30893902/

相关文章:

sql - 如何在 MS SQL Server 2008 中使用 XQuery 获取属性值

sql-server - 使用 ASP.NET MVC 和 SQL Server 实现家谱的最佳方法是什么

javascript - 无法读取 null bootstrap 崩溃的属性 'querySelectorAll'

php - 定位 Apache httpd 进程中的内存泄漏,基于 PHP/Doctrine 的应用程序

sql-server - T-SQL 如何通过 row_number SQL Server 将列转置为行

mysql - SQL 将行内容连接到另一个表的列名称

SQL - 仅显示具有最新日期的行

php - MySQL排名如果为空则显示空白页

php 数组转换成 javascript 数组 document.write javascript_array

php - 如何执行函数直到成功?