2015-06-17 14 views
18

Il seguente codice funziona come previsto (supponendo che le variabili esistono):query in PHP non riesce a vedere tabella temporanea in SQL Server se il legame è utilizzato il parametro

$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); 
} 

Tuttavia, se cambio $ seleziono al seguente, non funziona:

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

ottengo un errore durante l'esecuzione della seconda istruzione che dice "non valido nome di oggetto '#mytable_temp". Perché utilizzare il binding di parametri non dovrebbe rendere visibile la tabella temporanea?

Sono consapevole del fatto che potrei farlo funzionare se includo entrambe le istruzioni nella stessa istruzione sqlsrv_query(), ma questa non è un'opzione per il mio caso d'uso. So anche che funziona se si utilizza una tabella globale (## mytable_temp), ma neanche quella è un'opzione.

Sto eseguendo PHP 5.4.12 e ho provato il codice su SQL Server 11.0.3 (SP1 2012) e 10.50.4000 (SP2 2008).

+0

Non è abbastanza chiaro. Ricevi questo messaggio di errore dopo il primo 'select in # mytable_temp' o il secondo' select from # mytable_temp'? [Le tabelle temporanee locali] (https://technet.microsoft.com/en-us/library/ms177399%28v=sql.105%29.aspx) sono visibili solo alla connessione corrente per l'utente e vengono eliminate quando l'utente si disconnette dall'istanza di SQL Server. Se first' select into' esegue OK e il secondo 'select from' fallisce, può significare che la connessione è chiusa tra questi due' select'. –

+0

Lo sto prendendo nella seconda selezione. Capisco cosa stai dicendo sulla connessione, ma dovrebbe essere la stessa connessione basata sul codice sopra, no? Anche questo non spiegherebbe perché fallisce con i parametri legati ma funziona bene senza parametri. – Rocket04

+0

Hai provato con [sqlsrv_prepare] (http://php.net/manual/en/function.sqlsrv-prepare.php) e [sqlsrv_execute] (http://php.net/manual/en/function.sqlsrv- execute.php)? So che questa non è la tua domanda ma a meno che non si guardino le fonti sqlsrv_ *, non si otterranno risposte ... – niconoe

risposta

7

Ecco la mia spiegazione del motivo per cui la tabella temporanea non viene visualizzata dopo la query SELECT INTO che utilizza i parametri.

Considerate questo codice T-SQL (utilizzando MyTable creato e popolato come illustrato di seguito):

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

Se lo si esegue in SSMS funziona benissimo e l'uscita nella finestra Messaggi dice:

(2 row(s) affected) 

tenta di aggiungere una riga al codice di cui sopra nella stessa finestra SSMS ed eseguire l'intera partita:

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; 

L'output è:

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

La ragione è che dichiarazione con parametri viene eseguito da sp_executesql nell'ambito della stored procedure nidificata e tabelle temporanee create all'interno stored procedure non sono visibili al chiamante di questa procedura memorizzata.

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

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

una tabella temporanea locale creata in una stored procedure è caduto automaticamente quando la stored procedure è terminata. La tabella può essere a cui fa riferimento qualsiasi stored procedure nidificata eseguita dalla procedura memorizzata che ha creato la tabella. La tabella non può essere referenziata da il processo che ha chiamato la stored procedure che ha creato la tabella.

Quando si prepara un'istruzione SQL con parametri PHP chiama infine sp_executesql (molto probabilmente, anche se non l'ho tracciato). E si ottiene questo comportamento documentato - una tabella temporanea viene creata all'interno di questa stored procedure come parte della query e viene immediatamente interrotta quando restituisce sp_executesql. Quando si esegue un'istruzione SQL senza parametri, PHP la invia al server così com'è senza utilizzare sp_executesql.


Ci sono pochi accorgimenti che vengono in mente.

  • inserire più istruzioni SQL in una stringa lunga ed eseguirlo utilizzando uno chiamata a sqlsrv_query.

  • Effettuare una stored procedure con i parametri e inserire diverse istruzioni SQL al suo interno, quindi chiamare la procedura con una singola chiamata a sqlsrv_query. (Personalmente preferisco questo approccio).

  • Creare (e facoltativamente rilasciare) la tabella temporanea in modo esplicito.

Ecco il codice che ho utilizzato per verificare che l'ultima soluzione funzioni. Verificato con PHP 5.4.28, SQL Server Express 2014, driver Microsoft per SQLSRV PHP 3.2. Crea una tabella temporanea in modo esplicito usando la dichiarazione CREATE TABLE e poi usa INSERT INTO invece della singola istruzione SELECT INTO.

Crea una tabella di prova e popolare con alcuni dati

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') 

Esegui script 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"; 

output dello script

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 
1

Per completare la risposta di Vladimir, ecco i risultati della traccia (PHP 5.6.9, MSSQL 2014 Express).

Quando non si inseriscono dei parametri

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

PHP inviare comandi semplici per MSSQL:

SELECT * INTO #mytable_temp FROM mytable WHERE myfield = 'myvalue' 
SELECT * FROM #mytable_temp 

Quando si aggiungono i parametri

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

PHP utilizzerà quindi sp_executesql :

exec sp_executesql N'SELECT * INTO #mytable_temp FROM mytable WHERE myfield = @P1',N'@P1 nvarchar(6)',N'myvalue' 
SELECT * FROM #mytable_temp 
Problemi correlati