none
Problem with sqlsrv_num_rows and SP RRS feed

  • 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?
    Montag, 30. November 2009 13:56

Alle Antworten

  • Hallo Daniel,
    ich antworte mal auf deutsch!
    Leider hast Du die SP nicht gepostet. Steht dort ein SET NOCOUNT ON am Anfang?


    Einen schönen Tag noch, Christoph Muthmann Microsoft SQL Server MVP, http://www.insidesql.org
    Mittwoch, 2. Dezember 2009 15:05
    Beantworter
  • 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
    Mittwoch, 2. Dezember 2009 17:46
  • 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
    Dienstag, 8. Dezember 2009 13:56
    Beantworter
  • 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
    Mittwoch, 9. Dezember 2009 09:25
  • Hallo Daniel,

    hast du dein Problem inzwischen lösen können? Denn ich stehe derzeit vor genau dem gleichen :-(

    Viele Grüße
    Patrick

    Dienstag, 20. April 2010 17:13
  • 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 Rows

    Mehr siehe auch: Accessing SQL Server Databases with PHP

    Gruß Elmar

     

    Dienstag, 20. April 2010 20:20
  • 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

    Mittwoch, 21. April 2010 08:01
  • 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

    Mittwoch, 21. April 2010 12:22
  • 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;

     

    Früher hatte ich unter PHP4 PEAR mit MSSQL2000 verwendet, da ging das problemlos. Jetzt habe ich PHP5.3.2 / MSSQL2008 R2 und bin auf sqlsrv angewiesen, und da geht es nicht.
    Mittwoch, 21. April 2010 12:45
  • Habs, durfte gar keinen CursorTyp bei
    sqlsrv_query()
    angeben, keine print Anweisungen und auch keine SQLs die eine Warning generiert haben innerhalb von der Prozedur.
    Mittwoch, 21. April 2010 15:54