PHP + MSSQL: Seitenaufteilung

msycho

Lt. Junior Grade
Registriert
Juni 2004
Beiträge
423
Hallo!

Möchte eine Ausgabe einer MS SQL Datenbank auf bestimmte Datensätze pro Seite begrenzen, beispielsweise 10 Datensätze pro Seite.
Ich habe jedoch Schwierigkeiten bei der Umsetzung. Mit LIMIT kann man hier ja nicht arbeiten, sondern mit SELECT TOP.
Für MySQL gibt es dutzende Tutorials, für MS SQL jedoch nicht. Kann mir jemand weiterhelfen, bitte?
 
Oja, hast Du ne Ahnung wie lange ich da schon rumsuch!
Besonders hilfreich ist der Link, wie alle anderen, aber auch nicht.

Ich bin bisher soweit:

PHP:
[ Datenbankverbindung aufbauen ]
$zeilenProSeite = 5;

$seite = $_GET['seite']; 
if($seite == "" && $seite <= 0) {
  $seite = 0;
} else {
  $seite += $zeilenProSeite;
}

$query = "SELECT * FROM (
    SELECT TOP $zeilenProSeite * FROM (
        SELECT TOP $seite feld
        FROM tabelle
        ORDER BY tabelle.feld  ASC) as foo
    ORDER by feld DESC) as bar
ORDER by feld ASC " ;
$results = mssql_query($query);

echo "<a href=\"".$_SERVER['PHP_SELF']."?seite=$seite\">Weiter >></a>";

Zeigt aber keine Wirkung, im Gegenteil, es wird nichts angezeigt was die Abfrage betrifft.
 
... ... ... hast du die Seite denn überhaupt ganz gelesen?

I have found this workaround:
select * from (
select top 20 index from (
select top 10 index from table_blah order by index asc
) as newtbl order by index desc
) as newtbl2 order by index asc

Vereinfacht:
Code:
SELECT * FROM (
SELECT TOP [OG-UG+1] * FROM (
SELECT TOP [OG] [Spalte1, Spalte2, ...]
FROM [Tabelle]
ORDER BY [key] ASC
) ORDER BY [key] DESC
) ORDER BY [key] ASC

Und wenn dir das nicht weiterhilft - google doch nochmal:
http://www.google.de/search?hl=de&q=mssql top limit&btnG=Google-Suche&meta=lr=lang_de|lang_en


Edit: Sorry - ich hab bei dir eine Zeile überlesen ;) und dachte du hättest es falsch übernommen. Aber Kuck doch nochmal ob die UG (untergrenze) und OG (Obergrenze) nicht falsch sind.

Der Trick an der Sache ist, dass du Schnittmengen bildest und nicht ähnlich wie Limit arbeitest.
 
Zuletzt bearbeitet:
Klar hab ich die Seite gelesen, wie viele andere auch.
Ich komme nur nicht auf einen grünen Zweig. Mit dem bisschen Codeschnipsel krieg ich es nicht hin, neben der Abfrage muss ja auch das mit der Blätterfunktion noch umgesetzt werden.
 
Ich habe es nochmals versucht und zwar so:

PHP:
$ZeilenProSeite = 5;
$seite = $_GET['seite'];
if($seite == "" && $seite <= 0) {
 $seite = 0;
} else {
  $seite += $ZeilenProSeite;
}
$startRows = ($seite-1)*$ZeilenProSeite + 1;

[ Datenbankverbindung aufbauen ]

$query = "SELECT * FROM testtabelle";
$sql = mssql_query($query);
$AnzahlDs = mssql_num_rows ($sql);

$xSeiten = ceil($AnzahlDs / $ZeilenProSeite);

if($seite > 1){ 
    $zurueck = ($seite - 1);	
    echo "<a href=\"".$_SERVER['PHP_SELF']."?page=$zurueck\"><< Zur&uuml;ck</a>&nbsp;&nbsp;&nbsp;"; 
}  

$Seitenanzahl = $AnzahlDs / $ZeilenProSeite;
for($i= 1; $i < $Seitenanzahl; $i++)
{ 
if($seite == $i)
{
	print("<b>" . $i . "</b> / ");
	}
	else
	{
		print("<A HREF=\"" . $PHP_SELF . "?page=$i\">$i</A> / ");
	}
} 

if($AnzahlDs%$ZeilenProSeite != 0)
{ 
	if($seite == $i)
	{
		print("<b>" . $i . "</b>");
	}
	else
	{
		print("<A HREF=\"" . $PHP_SELF . "?page=$i\">$i</A>");
	}
	} 

if($seite < $xSeiten){ 
    $weiter = ($seite + 1);	
    echo "&nbsp;&nbsp;&nbsp;<a href=\"".$_SERVER['PHP_SELF']."?page=$weiter\">Weiter >></a>"; 
}

Die Sache ist nur die, dass zwar mehrere Seitenzahlen angezeigt werden, eine Seite mal 7 Einträge anzeigt, die andere wiederrum 13...etc...nur der aller erste Datensatz fehlt immer. Zudem ist es auch absolut wirrwarr, sprich ohne Ordnung.
Irgendwas mache ich falsch bzw. hab ich vergessen. :(
 
Probiers mal hier mit. Ist zwar MySQL aber ich denke das dürfte nicht so schwer sein auf MSSQL umzuschreiben
PHP:
if($Anzahl > $ErgebnisseProSeite)
{
    $Seiten = intval($Anzahl/$ErgebnisseProSeite);
    if($Anzahl%$ErgebnisseProSeite)
    {
        $Seiten++;
    }
    echo"Seite ";   
}
for($i = 1; $i <= $Seiten; $i++)
{
    echo"<a href=\"gaestebuch.php?AktuelleSeite=".($i-1)*$ErgebnisseProSeite."\">$i</a>&nbsp;";
}
 
msycho
grade bei diesem Problem wäre es mal interessant zu wissen welche Version vom MS-SQL-Server einsetzt
 
@tewes: Was soll ich mit den Codeschnipsel. Wenn dann solltest Du den Rest auch noch posten. Im Übrigen sind dort nirgends MySQL-Befehle vorhanden und kann somit auch nichts auf MS SQL umschreiben. ;)

@ag3nt: Das ist die normale 2000er.
 
Sry hatte nich alles kopiert^^
PHP:
if(!isset($_REQUEST['AktuelleSeite']))
{
    $AktuelleSeite = 0;
}
else
{
    $AktuelleSeite = $_REQUEST['AktuelleSeite'];
}
$ErgebnisseProSeite = 10;
$ResultPointer = mysql_query("SELECT * FROM `Gaestebuch` ORDER BY `date` DESC LIMIT $AktuelleSeite, $ErgebnisseProSeite");
$Anzahl = mysql_num_rows(mysql_query("SELECT * FROM `Gaestebuch` ORDER BY `id` DESC"));

So ich glaub das war alles was noch gefehlt hat-.-

MFG

tewes
 
@ag3nt: Zu Deinem Link: Die Query ist dort genauso beschrieben wie ich es auch versucht habe zusammen mit dem o.g. Script in Post #7. Es wird jedoch nichts ausgegeben, was die SQL-Abfrage betrifft.

Vielleicht hängt das mit meine komplizierten Selektierungsabfrage zusammen. Denn ich habe ein Formular, mit dem ich die Abfrage nachträgliche selektieren kann. Hierzu folgender Schnipsel Quellcode:

PHP:
// Alles wird selektiert
// Gleichzeitig die Grundlage fuer alle anderen Abfragen
$query = "SELECT * FROM testtabelle";

// Ist eines der Textfelder gesetzt ...
if(!empty($kdnr) || !empty($artikelnr) || !empty($lieferant) || !empty($von) || !empty($bis)){
    $query .= " WHERE ";

    // ... dann wird der Abfrage-String erweitert
    if(!empty($kdnr))
        $query .= " testspalte = '$kdnr' AND ";

    if(!empty($artikelnr))
        $query .= " testspalte_2 = '$artikelnr' AND ";

    if(!empty($lieferant))
        $query .= " testspalte_6 = '$lieferant' AND ";

    if ((!empty($von)) && (!empty($bis)))
        $query .= " testspalte_7 >= '$von' AND testspalte_7 <= '$bis' AND ";

    // Abfrage-String wird gekuerzt um das AND am Schluss zu entfernen
    $query = substr($query, 0, strlen($query)-4);
}

$sql = mssql_query($query) or die (mssql_get_last_message());
 
Also bei deinem Beispiel wäre es gut wenn du das mal etwas erläutern würdest, insbesondere "testspalte_7" irritiert mich etwas aber egal. Ich hab mir noch mal dein erstes Beispiel angeschaut, dass ist logisch das du nichts zu sehen bekommst, du kannst das ganze aber recht leicht lauffähig bekommen:

entweder so:
PHP:
$seite = $_GET['seite']; 
if($seite == "" && $seite <= 0) {
  $seite = $zeilenProSeite;
} else {
  $seite *= $zeilenProSeite;
}
...

oder so:
PHP:
$seite = $_GET['seite']; 
if($seite == "" && $seite <= 0) {
  $seite = 1;
} 

$query = 'SELECT * '
       . 'FROM ('
       
       . 'SELECT TOP ' . $zeilenProSeite . ' * '
       . 'FROM ( '
       . '    SELECT TOP ' . ($zeilenProSeite * $seite) . ' feld '
       . '    FROM tabelle'
       . '    ORDER BY feld ASC'
       . '  ) AS nested'
       . 'ORDER by feld DESC'
       
       . ' ) AS a ORDER BY feld ASC';
 
Danke Dir! Ich habe es mit Deinem ersten Codeschnipsel Deines letzten Beitrages versucht. Nun werden 5 Datensätze pro Seite ausgegeben, wie gewünscht.

Jetzt gibt es mehrere neu aufgekommene Probleme bzw. weitere Fragen.
Doch zuerst möchte ich Deiner Bitte nachgehen was das Erläutern meines o.g. Beispiels betrifft. Also ich habe ein Formular mit mehreren Inputfeldern mit dem ich die Abfrage der Datenbank selektieren kann. Sind alle Felder leer, sprich es ist nichts eingegeben, werden einfach alle Datensätze ausgegeben:

PHP:
// Alles wird selektiert
// Gleichzeitig die Grundlage fuer alle anderen Abfragen
$query = "SELECT * FROM (
    SELECT TOP $zeilenProSeite * FROM (
        SELECT TOP $seite *
        FROM testtabelle
        ORDER BY testtabelle.testspalte  ASC) as foo
    ORDER by testspalte DESC) as bar
ORDER by testspalte ASC " ;
$results = mssql_query($query);

Ist jetzt nun mind. ein, mehrere oder alle Felder gesetzt, dann wird nach den eingegeben Sachen selektiert:

PHP:
// Ist eines der Textfelder gesetzt ...
if(!empty($kdnr) || !empty($artikelnr) || !empty($lieferant) || !empty($von) || !empty($bis)){
    $query .= " WHERE ";

    // ... dann wird der Abfrage-String erweitert
    if(!empty($kdnr))
        $query .= " testspalte = '$kdnr' AND ";

    if(!empty($artikelnr))
        $query .= " testspalte_2 = '$artikelnr' AND ";

    if(!empty($lieferant))
        $query .= " testspalte_6 = '$lieferant' AND ";

    if ((!empty($von)) && (!empty($bis)))
        $query .= " testspalte_7 >= '$von' AND testspalte_7 <= '$bis' AND ";

    // Abfrage-String wird gekuerzt um das AND am Schluss zu entfernen
    $query = substr($query, 0, strlen($query)-4);
}

Jetzt zu meinen Anliegen bzw. Fragen:

Momentan wird ja die Ausgabe mit dieser Abfrage

PHP:
$query = "SELECT * FROM (
    SELECT TOP $zeilenProSeite * FROM (
        SELECT TOP $seite *
        FROM testtabelle
        ORDER BY testtabelle.testspalte  ASC) as foo
    ORDER by testspalte DESC) as bar
ORDER by testspalte ASC " ;

nach der ersten Spalte geordnet.
Besteht nun die Möglichkeit die Abfrage garnicht zu ordnen, sprich die Datensätze einfach so auszugeben, wie sie in der Datenbank gespeichert sind?

Desweiteren funktioniert jetzt die Selektionsabfrage nicht mehr. Ich bekomme nun nach der geänderten Abfrage folgende Fehlermeldung wenn ich ein input-Feld gesetzt habe:

Warning: mssql_query() [function.mssql-query]: message: Falsche Syntax in der Nähe des WHERE-Schlüsselwortes. (severity 15) in ** on line 184

Warning: mssql_query() [function.mssql-query]: Query failed in ** on line 184
Falsche Syntax in der Nähe des WHERE-Schlüsselwortes.

Zeile 184 ist folgende:
PHP:
$sql = mssql_query($query) or die (mssql_get_last_message());

Ich vermute, dass dieses neu aufgekommene Problem mit der Anordnung von SELECT, SELECT TOP, FROM, WHERE und ORDER BY zusammen hängt. Weisst Du wie es richtig heissen muss, bitte?

Ich hoffe Du kannst mir weiterhelfen.
 
Zuletzt bearbeitet:
Also ersteinmal etwas zu dem bereits mehrfach angesprochenen "Hack". MS-Sql unterstützt bis einschließlich der Version 2000 keine limitierte Datenselektion (ab der 2005er sogar komplett nach Standard -- und das bei Microsoft) bzw. nur eine die sich auf die ersten N bzw N% bezieht.
Das heisst also es ist nur möglich von dem ersten Datensatz an das Ergebnis abzurufen. Um jetzt aber die ersten 50 auszulassen um dann von dort an 10 Datensätze zu sehen musst du etwas tricksen.
1. Du lässt dir die ersten 60 Datensätze geben (50 + 10) sortiert nach einem beliebigen Wert sonst kann es durchaus sein das du jedesmal eine komplett andere Reihenfolge erhälst und das ist der einzige Grund warum du das Sortieren nicht weglassen kannst.
2. Du sortierst das Ergebnis nun genau anders herum. Nun kannst du mit der TOP 10
die letzten 10 Datensätze bekommen (die ersten 50 kannst du ja nicht bebrauchen).
3. Ansich wärst du jetzt schon fertig, allerdings braucht man im Normalfall die Ergebnisse ja in der richtigen Reihenfolge.

Betrachtet man nun deinen Fall, musst du dir ersteinmal ein sinnvolles Sortierkriterium überlegen. Wie das aussieht ist egal muss aber für eindeutige Ergebnisse den kompletten Primärschlüssel enthalten (oder halt den Primärschlüssel-Kandidaten).

Dein Beispiel funktionert vermutlich deshalb nicht mehr weil du die "WHERE-Klausel" an der falschen Stelle eingesetzt hast.
So sollte es aussehen (die Platzhalter entsprechend füllen)

PHP:
// Ist eines der Textfelder gesetzt ...
if(!empty($kdnr) || !empty($artikelnr) || !empty($lieferant) || !empty($von) || !empty($bis)){
    $where = ' WHERE ';

    // ... dann wird der Abfrage-String erweitert
    if(!empty($kdnr))
        $where .= " testspalte = '$kdnr' AND ";

    if(!empty($artikelnr))
        $where .= " testspalte_2 = '$artikelnr' AND ";

    if(!empty($lieferant))
        $where .= " testspalte_6 = '$lieferant' AND ";

    if ((!empty($von)) && (!empty($bis)))
        $where .= " testspalte_7 BETWEEN '$von' AND '$bis' AND ";

    // Abfrage-String wird gekuerzt um das AND am Schluss zu entfernen
    $where = substr($where, 0, strlen($where) - 4);
} 

$query = 'SELECT * ' 
       . 'FROM (' 
        
       . 'SELECT TOP ' . $zeilenProSeite . ' * ' 
       . 'FROM ( ' 
       . '    SELECT TOP ' . ($zeilenProSeite * $seite) . ' <<benötigte Felder>> ' 
       . '    FROM tabelle' 
       . '    ' . $where
       . '    ORDER BY <<PK: Sortierreihenfolge normal>>' 
       . '  ) AS nested' 
       . 'ORDER BY <<~PK: Sortierreihenfolge umgekehrt>>' 
        
       . ' ) AS a ORDER BY <<PK: Sortierreihenfolge normal>>';

Von Vorteil ist es außerdem - gerade um auch SQL-Injections vorzubeugen - noch sämtliche Eingabewerte auf Gültigkeit zu überprüfen und entsprechend zu escapen. Ich hoffe ich hab dir damit ersteinmal ein Großteil der Fragen beantworten können.
 
Dank Dir. Ich werde mir die Überprüfung der Eingabewerte auf Gültigkeit zu Herzen nehmen. Ebenso ein sinnvolles Sortierkriterium.

Zuvor jedoch nochmal zu der Abfrage bzw. zur WHERE-Klausel.

Ich habe es jetzt so gemacht:

PHP:
$zeilenProSeite = 5;

$seite = $_GET['seite']; 
if($seite == "" && $seite <= 0) {
  $seite = 1;
}  

// Ist eines der Textfelder gesetzt ...
if(!empty($kdnr) || !empty($artikelnr) || !empty($lieferant) || !empty($von) || !empty($bis)){
    $where = ' WHERE ';

    // ... dann wird der Abfrage-String erweitert
    if(!empty($kdnr))
        $where .= " testspalte = '$kdnr' AND ";

    if(!empty($artikelnr))
        $where .= " testspalte_2 = '$artikelnr' AND ";

    if(!empty($lieferant))
        $where .= " testspalte_6 = '$lieferant' AND ";

    if ((!empty($von)) && (!empty($bis)))
        $where .= " testspalte_7 BETWEEN '$von' AND '$bis' AND ";

    // Abfrage-String wird gekuerzt um das AND am Schluss zu entfernen
    $where = substr($where, 0, strlen($where)-4);
}

// Alles wird selektiert
// Gleichzeitig die Grundlage fuer alle anderen Abfragen
$query = 'SELECT * ' 
       . 'FROM (' 
        
       . 'SELECT TOP ' . $zeilenProSeite . ' * ' 
       . 'FROM ( ' 
       . '    SELECT TOP ' . ($zeilenProSeite * $seite) . ' * ' 
       . '    FROM testtabelle' 
       . '    ' . $where
       . '    ORDER BY testspalte ASC' 
       . '  ) AS foo' 
       . 'ORDER BY testspalte DESC' 
        
       . ' ) AS bar ORDER BY testspalte ASC';

Nun tauchen folgende Fehler auf und es wird nichts ausgegeben:

Warning: mssql_query() [function.mssql-query]: message: Falsche Syntax in der Nähe des BY-Schlüsselwortes. (severity 15) in ** on line 187

Warning: mssql_query() [function.mssql-query]: Query failed in ** on line 187

Warning: mssql_query() [function.mssql-query]: message: Falsche Syntax in der Nähe des BY-Schlüsselwortes. (severity 15) in ** on line 188

Warning: mssql_query() [function.mssql-query]: Query failed in ** on line 188
Falsche Syntax in der Nähe des BY-Schlüsselwortes.

Zeile 187 und 188 sind folgende:

PHP:
$results = mssql_query($query);
$sql = mssql_query($query) or die (mssql_get_last_message());

Ich weiß echt nicht was an der ORDER BY-Klausel falsch sein soll. Kannst Du mir bitte nochmals helfen?
 
Moin,
kein Problem hast ein Leerzeichen vergessen
PHP:
. '  ) AS foo'

. '  ) AS foo '
 
Puh, diese kleinen Fehler. Dankeschön!

Ich habe jetzt ja auch ein "Weiter-Link":

PHP:
echo "<a href=\"".$_SERVER['PHP_SELF']."?seite=$seite\">Weiter >></a>";

Nur der wird immer angezeigt, auch wenn es nur eine Seite gibt bzw. man an der letzten Seite ist. Wie kann ich jetzt sagen, dass dieser nicht erscheinen soll, wenn man an der letzten Seite ist, bitte?

Ein "Zurück-Link" soll auch noch angezeigt werden. Wie mache ich das? Muss ich da dann den Wert wieder abziehen, also so?

PHP:
echo "<a href=\"".$_SERVER['PHP_SELF']."?seite=$seite-$zeilenProSeite\"><<  Zur&uuml;ck</a>";

Was ich auch noch nicht so recht verstehe, wann nimmt man einfache und wann doppelte Anführungszeichen? Also ' und " ?

Hoffe Du kannst mir etwas weiterhelfen.


/EDIT: Ich sehe gerade die Blätterfunktion klappt nicht. Angenommen ich habe 20 Datensätze, lasse 5 pro Seite anzeigen, habe ich ja 4 Seiten. Bin ich auf der ersten Seite und klicke auf "Weiter", komme ich aber nicht zu nächsten Seite.
Das liegt doch an dem Code, oder?

PHP:
$seite = $_GET['seite']; 
if($seite == "" && $seite <= 0) {
  $seite = 1;
}

Sprich der ist unvollständig?
 
Zuletzt bearbeitet:
Also um erstmal die einfachen Fragen zu beantworten ... :-)

... der Unterschied zwischen ' und " ist eigentlich recht gering und ist auch in der PHP-Referenz gut erläutert von daher verweise ich jetzt mal auf selbige.

Zu der Frage mit der "Blätterfunktion" hat Enigma ja schon eine recht schöne Erklärung gegeben wie es funktioniert.

In deinem Fall sieht es so aus:
  • Standardmäßig soll die erste Seite angezeigt werden
  • Blätter-Funktion nur anzeigen wenn dies Sinnvoll ist
  • unlogische Seitennummer ignorieren

OK alles simpel und schon millionenfach irgendwo auf zig verschiedene Arten implementiert.;)

PHP:
echo '<a href="' . $_SERVER['PHP_SELF'] . '"?seite=' . ($seite + 1) .'">Weiter &gt;&gt;</a>'; 
echo '<a href="' . $_SERVER['PHP_SELF'] . '"?seite=' . ($seite - 1) .'">&lt;&lt; Zur&uuml;ck</a>';

Soweit so simpel, um jetzt noch zu überprüfen ob die Links überhaupt erforderlich sind brauchst du logischer Weise erstmal die Info ob es überhaupt genügend Einträge gibt.
Dazu könntest du zum Beispiel eine SQL-Abfrage verwenden und das Ergebnis anschließend aufbereiten.

PHP:
$query = 'SELECT '
       . '      COUNT(*) AS seite_anzahl ' 
       . '  ,   1 AS seite_erste ' 
       . '  ,   CEILING( COUNT(*) / ' . $zeilenProSeite . ' ) AS seite_letzte ' 
       . 'FROM testtabelle ' 
       . '' . $where;

/* ... hier Abrufen der Ergebnisse ... */

$seite = $_GET['seite']; 
if( empty( $seite ) || && $seite < $row['seite_erste'] || $seite > $row['seite_letzte'] ) 
{
  $seite = $row['seite_erste'];
} 

$zeigeWeiter  = false;
$zeigeZurueck = false;

if ( $seite > $row['seite_erste'] )
	$zeigeZurueck = true;
	
if ( $seite < $row['seite_letzte'] )
	$zeigeWeiter  = true;
	
/* weitere Verarbeitungschritte */

if ( $zeigeWeiter )
{
	echo '<a href="' . $_SERVER['PHP_SELF'] . '"?seite=' . ($seite + 1) .'">Weiter &gt;&gt;</a>';  
}

if ( $zeigeZurueck )
{
	echo '<a href="' . $_SERVER['PHP_SELF'] . '"?seite=' . ($seite - 1) .'">&lt;&lt; Zur&uuml;ck</a>'; 
}
 
Zurück
Oben