2013-01-15 14 views
10

Ich habe eine Datenbank mit mehr als 2 Millionen Datensätze und ich muss eine Seitennummerierung auf meiner Webanwendung, die 10 Datensätze pro Seite in einem DataGrid haben müssen, führen.Welcher ist der beste Weg, um Seitenumbrüche auf SQL Server durchzuführen?

Ich habe bereits versucht, ROW_NUMBER() zu verwenden, aber dieser Weg wird alle 2 Millionen Datensätze auswählen und dann nur 10 Datensätze erhalten. Ich versuchte auch, TOP 10 zu verwenden, aber ich würde die erste und letzte Identifikation speichern müssen, um die Seiten zu steuern. Und ich habe gelesen, dass mit DataAdapter.Fill() wählen Sie den gesamten Inhalt und dann die 10 Datensätze, die ich brauche.

Welches ist der beste Weg? Sollte ich DataAdapter.Fill() verwenden? Oder verwenden Sie die SQL Server-Funktion ROW_NUMBER()? Oder versuchen Sie TOP 10 zu verwenden?

+4

siehe diese http://blog.sqlauthority.com/2010/12/15/sql-server-server-side-paging-in-sql-server-2011-a-better-alternative/ –

+0

Lesen Sie nur dies: http://www.asp.net/web-forms/tutorials/data-access/paging-and-sorting/efficiently-paging-through-large-amounts-of-data-vb Es ist ein sehr komplettes Tutorial (VB.NET aber das ist in diesem Fall nicht wichtig). –

+0

@Guilherme bist du * wirklich * immer noch mit sql-server-2000? (Tags) Wenn ja, wird das Ihre Möglichkeiten ein wenig einschränken. –

Antwort

4
ALTER PROCEDURE [dbo].[SP_tblTest_SelectSpecificRecordsWithCTE] 
    @FromRow int = 1000000, 
    @PgSize int = 10 
AS 
BEGIN 
    ;WITH RecordsRN AS 
    (
     select ID, colValue, ROW_NUMBER() over(order by colvalue) as Num from tblTest 
    ) 
    SELECT ID Value, colValue Text FROM RecordsRN WHERE Num between @FromRow AND (@[email protected]) 
END 

das ist die Abfrage, die ich für Paging verwende. benutze es und du bekommst 10 gewünschte Datensätze in 4-5 Sekunden. Ich bekomme 10 Datensätze in 3 Sekunden und insgesamt Datensätze in meinem db sind 10 Millionen, verwenden Sie nicht Top 10 wird es immer nur die gleichen 10 Datensätze jedes Mal. In meinem Fall behalte ich die Seitengröße und die Startzeilennummer (@FromRow) in der Sitzung und übergebe diese beiden Werte an die unten angegebene gespeicherte Prozedur und erhalte das Ergebnis. Weiter, wenn Sie SQL 2012 verwenden, möchten Sie möglicherweise OFFSET und holen nächste 10 Zeilen Art der Sache. Suchen Sie bei Google nach dem Keyword OFFSET und Sie werden das gewünschte Ergebnis oben sehen.

dank

+0

Alle Antworten sind wirklich nett, aber Sie sind ähnlicher dem, was ich getan habe, und ich denke immer noch, dass das der beste Weg ist. Leider ist mein SQL Server 2008, daher habe ich die Anweisung OFFSET nicht zu verwenden. Vielen Dank. –

1

Ich benutze das folgende Muster (automatisch) erzeugen ausgelagerten Unterabfragen:

select top (@takeN) <your-column-list> 
from (
    select qSub2.*, _row=row_number() over (order by SomeColumn Asc, SomethingElse Desc) 
    from (
     select top (@takeN + @skipN) <your-column-list> 
     from ( 
      select <your-subquery-here> 
     ) as qSub1 
     order by SomeColumn Asc, SomethingElse Desc 
    ) as qSub2 
) qSub3 
where _row > @skipN 
order by _row 

Hinweise zu diesen Mustern:

  • Sub eine Abfrage konzeptionell @skipN Reihen Springt und dann nehmen die nächsten @takeN Reihen.
  • Wenn Sie die zusätzliche Spalte _row im Ergebnis nicht interessiert, können Sie <your-column-list> durch * ersetzen; Ich benutze die explizite Spaltenliste, weil es mir erlaubt, den Satz von Spalten zur Laufzeit zu unterteilen, was z.B. um nur Hauptschlüsselspalten und dergleichen zu finden.
  • Ihre order by Klauseln sollten identisch sein; Der Optimierer von SQL Server ist im Allgemeinen intelligent genug, um das zu verstehen. Die Duplizierung ist ein Nebeneffekt der Klausel top, mit der die Ergebnisse abgeschnitten wurden. top ist für unsortierte Unterabfragen nicht zulässig. Und oben ist nützlich, um dem Abfrageoptimierer zu helfen, zu verstehen, dass diese Abfrage wahrscheinlich nur wenige Zeilen zurückgibt.
  • Die Gründe, @takeN und @skipN im Gegensatz zu Seitenzahl + größenbasierten Parametern zu verwenden, sind ziemlich gering. Zum einen ist es ein wenig flexibler und ein wenig einfacher in der Abfrage, zum anderen spielt es den Sql-Server-Stärken ein bisschen besser: Die DB ist nicht besonders brillant bei der Optimierung dieser Art von Abfragen an erster Stelle, und die Hoffnung ist dass ein äußerer, einfacher Top-Satz wie dieser es für den Optimierer trivial macht, die maximale Anzahl der möglichen Reihen zu verstehen. Im Allgemeinen versuche ich zu vermeiden, Berechnung in SQL zu tun, ich könnte genauso gut in Code tun, da es den Optimierer zu verwirren neigt (obwohl in dem speziellen Fall von @ pagecount * @ pagesize Experimente haben gezeigt, dass es kein großes Problem ist)

Beachten Sie, dass SQL Server 2012 eine neue offset...fetch clause für genau dieses Szenario unterstützt, die viel einfacher ist.

2

Verwenden Sie ROW_NUMBER() und implementieren Sie eine statische Dienstprogrammfunktion (wie GetPaginatedSQL in meinem Code), die Ihre ursprüngliche SQL-Abfrage automatisch in eine begrenzte/paginierte SQL-Abfrage umschließt.

Dies ist diejenige, die ich verwende:

namespace Persistence.Utils 
{ 
    public class SQLUtils 
    { 
     /// <summary> 
     /// Builds a paginated/limited query from a SELECT SQL. 
     /// </summary> 
     /// <param name="startRow">Start row</param> 
     /// <param name="numberOfRows">Number/quatity of rows to be expected</param> 
     /// <param name="sql">Original SQL (without its ordering clause)</param> 
     /// <param name="orderingClause">MANDATORY: ordering clause (including ORDER BY keywords)</param> 
     /// <returns>Paginated SQL ready to be executed.</returns> 
     /// <remarks>SELECT keyword of original SQL must be placed exactly at the beginning of the SQL.</remarks> 
     public static string GetPaginatedSQL(int startRow, int numberOfRows, string sql, string orderingClause) 
     { 
      // Ordering clause is mandatory! 
      if (String.IsNullOrEmpty(orderingClause)) 
       throw new ArgumentNullException("orderingClause"); 

      // numberOfRows here is checked of disable building paginated/limited query 
      // in case is not greater than 0. In this case we simply return the 
      // query with its ordering clause appended to it. 
      // If ordering is not spe 
      if (numberOfRows <= 0) 
      { 
       return String.Format("{0} {1}", sql, orderingClause); 
      } 
      // Extract the SELECT from the beginning. 
      String partialSQL = sql.Remove(0, "SELECT ".Length); 

      // Build the limited query... 
      return String.Format(
       "SELECT * FROM (SELECT ROW_NUMBER() OVER ({0}) AS rn, {1}) AS SUB WHERE rn > {2} AND rn <= {3}", 
       orderingClause, 
       partialSQL, 
       startRow.ToString(), 
       (startRow + numberOfRows).ToString() 
      ); 
     } 
    } 
} 

Die Funktion könnte oben verbessert, ist aber eine erste Implementierung.

Dann in Ihrem DAOs, sollten Sie sein, nur so etwas wie dies zu machen:

using (var conn = new SqlConnection(CONNECTION_STRING)) 
{ 
    using (var cmd = conn.CreateCommand()) 
    { 
     String SQL = "SELECT * FROM MILLIONS_RECORDS_TABLE"; 
     String SQLOrderBy = "ORDER BY DATE ASC "; //GetOrderByClause(Object someInputParams); 
     String limitedSQL = GetPaginatedSQL(0, 50, SQL, SQLOrderBy); 

     DataSet ds = new DataSet(); 
     SqlDataAdapter adapter = new SqlDataAdapter(); 

     cmd.CommandText = limitedSQL; 

     // Add named parameters here to the command if needed... 

     adapter.SelectCommand = cmd; 
     adapter.Fill(ds); 

     // Process the dataset... 
    } 
    conn.Close(); 
} 

Hoffe, es hilft.

Verwandte Themen