Fragensteller
Problem with sqlsrv_num_rows and SP

Frage
-
Hi everyone.
I have a problem with sqlsrv_num_rows in combination with a call of a SP. The SP returns a table and I want to determine the Rowcount.
This is my PHP-code:
$SQLParams = array(array($_SESSION['userid'], SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_INT, SQLSRV_SQLTYPE_INT));
$SQLOptions = array("Scrollable" => SQLSRV_CURSOR_STATIC);
$SQLResult = sqlsrv_query($SQLConn, 'EXEC dbo.getUserDetailMissingFields @BenutzerID=?', $SQLParams2, $SQLOptions);
print_r(sqlsrv_errors());
$SQLRows = sqlsrv_num_rows($SQLResult);
echo "Rows: ".$SQLRows;
Instead of a table I get an error message:
Array ( [0] => Array ( [0] => 01000 [SQLSTATE] => 01000 [1] => 16954 [code] => 16954 [2] => [Microsoft][SQL Server Native Client 10.0][SQL Server]SQL wird direkt ausgeführt, ohne Cursor. [message] => [Microsoft][SQL Server Native Client 10.0][SQL Server]SQL wird direkt ausgeführt, ohne Cursor. ) [1] => Array ( [0] => 01S02 [SQLSTATE] => 01S02 [1] => 0 [code] => 0 [2] => [Microsoft][SQL Server Native Client 10.0]Cursor type changed [message] => [Microsoft][SQL Server Native Client 10.0]Cursor type changed ) )
The above message translates to something like SQL executed directly, no cursor.
The variable $SQLResult is false, so the sqlsrv_rows_affected($SQLResult) fails too with an "boolean instead of statement was passed" error.
I am using the EXEC-style because it is better suited for passing optional parameters to a SP. But the CALL-method fails too. Using sqlsrv_query and sqlsrv_num_rows with something like 'SELECT ID FROM dbo.T_Users WHERE ID=?' works perfectly. Is it possible to get this working with SPs too?
Alle Antworten
-
So, hier der Quelltext meiner SP. Davon müsste ich die Anzahl der Reihen wissen...
Ein SET NOCOUNT ON habe ich am Anfang. Irgendwie dachte ich, daß ich mit meinem Posting in der internationalen Sektion lande. :)
CREATE PROCEDURE [dbo].[getUserDetailMissingFields] (
@BenutzerID int
)
WITH ENCRYPTION
AS BEGIN
SET NOCOUNT ON
SELECT
bdw.Bezeichnung
FROM
dbo.T_BenutzerdetailWerte AS bdw
LEFT JOIN dbo.T_BenutzerdetailEingaben AS bde
ON bde.BenutzerdetailWertID = bdw.ID AND
bde.BenutzerID = @BenutzerID
WHERE
bdw.Pflicht = 1 AND
bdw.Aktiv = 1 AND
bde.ID IS NULL
ORDER BY
bdw.Reihenfolge ASC
END -
Hallo Daniel,
PHP ist leider nicht gerade meine Baustelle, aber ich habe das hier gefunden:
http://articles.sitepoint.com/article/php-microsoft-sql-server
Ich könnte mir vorstellen, dass man beim Zugriff auf Stored Procedures die SqlCommand Class benötigt.
HTH!
Einen schönen Tag noch, Christoph Muthmann Microsoft SQL Server MVP, http://www.insidesql.org -
Hallo Christoph.
Leider ist die von dir angegebene Seite im Moment nicht erreichbar. Werde das aber nachher nochmal probieren.
Der Hinweis mit der SQLCommand-Class ist mir neu. Ich werde da mal weiter recherchieren. Ich melde mich wieder, wenn ich näheres weiß.
Bis dahin!
Gruß
Daniel -
Hallo Patrick,
welchem gleichen genau?
Im Ausgangsposting von Daniel ist das Kernproblem, dass der SQL Server
im Standard (nicht nur bei PHP) ein Forward Only Resultset (Cursor) liefert,
weil das die effizienteste und schnellste Methode ist.
Nur kann dort man die Zeilenzahl nicht im vorhinein ermitteln,
sondern sie steht erst am Ende der Verarbeitung fest.
Wo immer möglich sollte man den Code so strukturieren,
dass man damit klarkommt.
Ansonsten mußt Du einen Cursortyp festlegen, der es ermöglicht,
das wäre ein Keyset oder Static Cursor, siehe dazu:
Specifying a Cursor Type and Selecting RowsMehr siehe auch: Accessing SQL Server Databases with PHP
Gruß Elmar
-
Hallo Elmar,
also ich habe eine Prozedur. Darin lege ich in der Prozedur eine physische [Tabelle] mit Timestamp im Namen an, deren Spaltenanzahl ebenfalls dynamisch ist. Das Anlegen erfolgt per SELECT ... INTO [Tabelle]. Diese wird mit 5 dynamischen Inserts gefüllt. Danach mit Select * FROM [Tabelle] und ein DROP [Tabelle] ... sprich ich bekomme ein Resultset mit vorher unbekannter Spaltenanzahl und Zeilenanzahl zurück. Im Management Studio funktioniert das wunderbar, in PHP bekomme ich die gleichen Fehler wie Daniel.
$la_connectionInfo = array("Database" => "name", "UID" => "user", "PWD" => "password"); $this->io_link = sqlsrv_connect("localhost", $la_connectionInfo); [...] $ls_sql = "EXEC dbo.prozedurname ?,? "; $la_param = array(1,2); // Methode 1 $lo_result = sqlsrv_query($this->io_link,$ls_sql,$la_param,array( "Scrollable" => SQLSRV_CURSOR_KEYSET)); // Methode 2 $lo_result = sqlsrv_query($this->io_link,$ls_sql,$la_param,array( "Scrollable" => SQLSRV_CURSOR_STATIC));
$lo_result ist sowohl bei Methode 1 als auch bei Methode 2 false, und var_dump(sqlsrv_errors(SQLSRV_ERR_ALL)) liefert:
Array ( [0] => Array ( [0] => 01000 [SQLSTATE] => 01000 [1] => 16954 [code] => 16954 [2] => [Microsoft][SQL Server Native Client 10.0][SQL Server]SQL wird direkt ausgeführt, ohne Cursor. [message] => [Microsoft][SQL Server Native Client 10.0][SQL Server]SQL wird direkt ausgeführt, ohne Cursor. ) [1] => Array ( [0] => 01S02 [SQLSTATE] => 01S02 [1] => 0 [code] => 0 [2] => [Microsoft][SQL Server Native Client 10.0]Cursor type changed [message] => [Microsoft][SQL Server Native Client 10.0]Cursor type changed ) )
Ich habe das ganze auch schon mit CALL und den 2.0 Treiber vom 19.04. versucht, bringt aber keine Änderung.Viele Dank für deine Hilfe, Patrick
-
Hallo Patrick,
ich habe hier kein PHP am laufen, somit kann ich dazu nichts testen.
Aber das Problem ist das gleiche, was auch bei allen anderen
Schnittstellen mit dynamische Ergebnisse und einem Cursor passiert.
(und ein anderer Treiber bringt da keine Besserung)
Dabei versucht der SQL Server Treiber, die Spalten des Resultsets
zu ermitteln, in dem er die Prozedur mit Optionen wie
NO_BROWSETABLE, FMTONLY aufruft, siehe u. a.:
http://support.microsoft.com/kb/885146/
Dabei werden keine gültigen Prozedur-Parameter übergeben,
sondern NULL bzw. gesetzte Standardwerte.
Und wenn Du, wie zu vermuten, daraus die Art des zu erstellenden
SELECTs ermittelst, kommt dabei nichts (oder was falsches) heraus.
Ich kann davon nur abraten.
Trenne das ganze in verschiedene Prozeduren auf, die jeweils
eins oder mehrere definierte Ergebnisse enthalten, aber sich
die Spalten nicht in Anzahl, Datentyp oder Reihenfolge unterscheiden.
Die Inhalte können auf der anderen Seite variabel sein -
anderum mal ein paar NULL-Spalten mehr wären akzeptabel.Ausserdem sollten alle Anweisungen, die die Tabelle befüllen
mit SET NOCOUNT ON gekapselt werden, angedeutet in etwa:
CREATE PROC dbo.Test AS SET NOCOUNT ON; SELECT * FROM xyz INTO dbo.Tabelle; INSERT INTO dbo.Tabelle (...) SELECT ... -- Am Schluß SET NOCOUNT OFF SELECT ... FROM dbo.Tabelle GO
denn ansonsten wird jede Anweisung (auch INSERT/UPDATE/DELETE/SELECT INTO)
als (leeres) Resultset zurückgeliefert.
Im übrigen ist solltest Du anstatt fester Tabellen auf lokale temporäre Tabellen
einsetzen, falls Du es nicht bereits tust. Damit minimierst Du das Risiko,
bei parallelen (Web)Zugriffen, auf Blockierungen, Deadlocks oder falsche Ergebnisse
zu erhalten.Gruß Elmar
-
Ich verstehe nicht warum folgendes Funktioniert:
Create PROCEDURE [dbo].[test_proc] AS select * from kunden;
und das hier nicht:
CREATE PROCEDURE [dbo].[test_proc] AS DECLARE @ls_sql1 int select * from kunden;