none
Tempi di risposta RRS feed

  • Domanda

  • Spero di non essere ot.

    Stavo riflettendo sulla convenienza o meno di lavorare in ambiente connesso rispetto al disconnesso.

    Ho pensato che fosse meglio fare dei test.

    Ho sempre pensato che l'ambiente connesso offrisse performance migliori.

    Ho una tabella di 100.000 record, per la quale ho creato questa stored procedure:

    ceate procedure spFiltro
        @filtro varchar(10),
        @pagina int,
        @righepagina int
    as
    select *
    from
    (
    SELECT row_number() over (ORDER BY categoria desc) as riga,
           [ID]
          ,[Categoria]
          ,[SubCategoria]
          ,[Nominativo]
          ,[Indirizzo]
          ,[Cap]
          ,[Località]
          ,[Provincia]
          ,[Telefoni]
          ,[Fax]
          ,[IndirizzoEmail]
          ,[Email]
          ,[Esportato]
      FROM [Utenze].[dbo].[tbUtenze2]
      where categoria like '%%')as l
      where l.riga>@pagina*@righepagina and l.riga<=(@pagina+1)*@righepagina

    lato codice:

                t1 = DateTime.Now;
                List<Utenza> l = new List<Utenza>();
                using (SqlCommand cmd = new SqlCommand("spFiltro", cn))
                {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Parameters.Add(new SqlParameter("@filtro",txtfiltro.Text));
                    cmd.Parameters.Add(new SqlParameter("@pagina",pagina));
                    cmd.Parameters.Add(new SqlParameter("@righepagina", righeperpagina));
                    if (cn.State == System.Data.ConnectionState.Closed)
                        cn.Open();
                    try
                    {
                        DateTime g1 = DateTime.Now;
                        SqlDataReader dr = cmd.ExecuteReader();
                        MessageBox.Show((DateTime.Now - g1).ToString());
                        Utenza u;
                        while (dr.Read())
                        {
                            u = new Utenza();
                            u.ID = (int)dr[1];
                            u.Categoria = dr.GetString(2);
                            if (dr[3] == DBNull.Value) u.SubCategoria = null; else u.SubCategoria = dr.GetString(3);
                            u.Nominativo = dr.GetString(4);
                            u.Indirizzo = dr.GetString(5);
                            u.Cap = dr.GetString(6);
                            u.Località = dr.GetString(7);
                            u.Provincia = dr.GetString(8);
                            l.Add(u);
                        }
                        dr.Close();
                        dr.Dispose();
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                }

                lv.ItemsSource = l;
                t2 = DateTime.Now;
                MessageBox.Show((t2 - t1).ToString());

    Il primo messaggio rilascia un tempo di 0.40233 secondi per caricare il datareader  tutto il resto è irrisorio.

    Se poi la querystring la creo dimanicamente devo aggiungere 0.10 s.

    L'ho confrontato con linq2sql:

                using (DBDataContext dc = new DBDataContext())
                {
                    lv.ItemsSource= dc.tbUtenze2.Take(righeperpagina).Skip(pagina * righeperpagina).OrderBy(c=>c.Categoria);
                }
    tempo per caricare la listview 0.16... s

    Dove è l'inghippo?

    Ciao

    domenica 6 gennaio 2013 16:55

Risposte

  • Comunque già dal profiler si nota immediatamente quanti cicli di cpu e durata in più ha il tuo modo di scrivere la query rispetto al suo..

    • Contrassegnato come risposta Cracken66 lunedì 7 gennaio 2013 13:20
    lunedì 7 gennaio 2013 10:23

Tutte le risposte

  • Ciao Cracken,

    in questo momento non ho Windows sotto mano e non posso verificare di preciso ne controllare il DEP (se puoi, postalo tanto per capirci qualcosa in più) ma ipotizzo che sia "colpa" (se di colpa possiam parlare) di come la macchina elabora quello che tu hai scritto.

    In questo caso, hai applicato un SqlCommand ad un DataReader, facendo un ciclo, creando oggetti e cast di campi che portano via tempo.. la query stessa che hai scritto nel SqlCommand, ha già un filtro di troppo "where categoria like '%%'" . Non ci vedo nessun inghippo insomma, solo la dimostrazione che in questo caso usare linq to sql è più performante in quanto si interfaccia meglio al database rispetto all'uso di un SqlCommand.. :)

    ------------------------------------------------------------------------- Aggiornamento

    Rileggendo meglio quello che hai scritto, mi ero perso il "0.4 per caricare il datareader tutto il resto è irrisorio", ma confermo comunque quello che ho scritto sopra.. :°)

    domenica 6 gennaio 2013 21:25
  • Ciao

    Eliminando il filtro sostanzialmente non ci sono stati cambiamenti.

    Grazie

    lunedì 7 gennaio 2013 07:49
  • Teoricamente qualcosa quel filtro avrebbe dovuto prenderlo, puoi verificare il data execution plan della query?

    Ci sono table scan dentro? Hai modo di profilare l'esecuzione di entrambe le soluzioni?

    lunedì 7 gennaio 2013 08:36
  • Questo usando linq:

    exec sp_executesql N'SELECT [t2].[ID], [t2].[Categoria], [t2].[SubCategoria], [t2].[Nominativo], [t2].[Indirizzo], [t2].[Cap], [t2].[Località], [t2].[Provincia], [t2].[Telefoni], [t2].[Fax], [t2].[IndirizzoEmail], [t2].[Email], [t2].[Esportato]
    FROM (
        SELECT [t1].[ID], [t1].[Categoria], [t1].[SubCategoria], [t1].[Nominativo], [t1].[Indirizzo], [t1].[Cap], [t1].[Località], [t1].[Provincia], [t1].[Telefoni], [t1].[Fax], [t1].[IndirizzoEmail], [t1].[Email], [t1].[Esportato], [t1].[ROW_NUMBER]
        FROM (
            SELECT ROW_NUMBER() OVER (ORDER BY [t0].[ID], [t0].[Categoria], [t0].[SubCategoria], [t0].[Nominativo], [t0].[Indirizzo], [t0].[Cap], [t0].[Località], [t0].[Provincia], [t0].[Telefoni], [t0].[Fax], [t0].[IndirizzoEmail], [t0].[Email], [t0].[Esportato]) AS [ROW_NUMBER], [t0].[ID], [t0].[Categoria], [t0].[SubCategoria], [t0].[Nominativo], [t0].[Indirizzo], [t0].[Cap], [t0].[Località], [t0].[Provincia], [t0].[Telefoni], [t0].[Fax], [t0].[IndirizzoEmail], [t0].[Email], [t0].[Esportato]
            FROM [dbo].[tbUtenze2] AS [t0]
            ) AS [t1]
        WHERE [t1].[ROW_NUMBER] BETWEEN @p0 + 1 AND @p0 + @p1
        ) AS [t2]
    ORDER BY [t2].[Categoria], [t2].[ROW_NUMBER]',N'@p0 int,@p1 int',@p0=0,@p1=30   

    e questo usando il command:

    ALTER procedure [dbo].[spFiltro]
        @filtro varchar(10),
        @pagina int,
        @righepagina int
    as
    select *
    from
    (
    SELECT row_number() over (ORDER BY categoria desc) as riga,
           [ID]
          ,[Categoria]
          ,[SubCategoria]
          ,[Nominativo]
          ,[Indirizzo]
          ,[Cap]
          ,[Località]
          ,[Provincia]
          ,[Telefoni]
          ,[Fax]
          ,[IndirizzoEmail]
          ,[Email]
          ,[Esportato]
      FROM [UtenzeCap].[dbo].[tbUtenze2])as l
      where l.riga>@pagina*@righepagina and l.riga<=(@pagina+1)*@righepagina

    La prima selezione è l'esecuzione del filtro, la seconda è linq.

    lunedì 7 gennaio 2013 09:58
  • Bhé, già così puoi vedere che le query sono scritte in maniera diversa, linq sta usando il BETWEEN al posto di > di e < di, un'altra cosa che ho scordato di farti notare è che devi provare l'esecuzione della procedura più di una volta in quanto il sistema si salva il piano di esecuzione.

    Se questo non ti basta puoi postare anche la struttura della tabella e le insert di un set di dati interni (oppure il data execution plan di entrambe le query) così da mostrare il tutto, ma le motivazioni a questo punto le hai già davanti tu una volta eseguite le query col DEP.

    (:

    lunedì 7 gennaio 2013 10:15
  • Comunque già dal profiler si nota immediatamente quanti cicli di cpu e durata in più ha il tuo modo di scrivere la query rispetto al suo..

    • Contrassegnato come risposta Cracken66 lunedì 7 gennaio 2013 13:20
    lunedì 7 gennaio 2013 10:23
  • Ho copiato la sp creata da linq nella mia sp ed il risultato è

    linq=0,097 s

    command=0,021 s

    Notevole miglioramento.

    Ho fatto delle verifiche ed ho notato che la differenza è dovuta all'uso di subquery.

    Ora però vorrei implementare un filtro dinamico aggiungendo questa riga alla query più interna:

            where t0.Nominativo like '%'+@filtro+'%'

    La sp verrà richiamata ad ogni variazione del testo.

    Il risultato è che, all'inserimento di ogni carattere, i tempi di risposta cambiano sensibilmente:

    primo carattere: 0,023 s

    secondo carattere: 0,013 s

    terzo carattere: 0,036 s

    quarto carattere: 0,181 s

    quinto carattere: 0,522 s

    sesto ccaratter: 0,577 s

    settimo carattere: 0,566 s

    Dal quinto carattere i tempi si stabilizzano.

    Perchè accade? C'è un modo per restringere i tempi?

    Grazie

    lunedì 7 gennaio 2013 11:48
  • Oddio, fare tuning tramite escamotage su una query così semplice non so quanto ti faccia guadagnare, ma possiamo provarci comunque..

    Prima di tutto, esegui la query visualizzando il DEP everifica di avere già una query ottimizzata al massimo, seconda di poi potresti eseguire delle insert massive in una tabella variabile con la stessa struttura della tua tabella risultante ed eseguire le condizioni ed i calcoli di pagina direttamente sulla tabella variabile, lavorando direttamente in memoria guadagnerai in prestazioni (ovviamente è un equilibrio che và deciso sul campo basandoti anche sulla quantità di dati che estrapolerai).

    lunedì 7 gennaio 2013 12:01
  • Ho controllato se ho il dep abilitato e lo è, ma non so come usarlo.
    lunedì 7 gennaio 2013 13:00
  • Acc.. spiegare come usare un data execution plan è lunghetta, di base ti consiglio di iniziare a studiare il grafico eseguendo qualche query con piano attivo, così inizi a capire come ragiona l'esecutore di query quando tu lanci il tuo codice.

    All'inizio ti sembrerà arabo in parte, a questa pagina comunque trovi la reference di msdn per il DEP.

    Per quanto riguarda il tuo problema se vuoi, puoi eseguire la query e postarci un'immagine.

    lunedì 7 gennaio 2013 13:15
  • Ti ringrazio, sei stato molto disponibile.

    Ciao

    lunedì 7 gennaio 2013 13:19
  • Aggiungo una cosa scusa, ci sono due opzioni.. visualizza piano di esecuzione STIMATO ed EFFETTIVO.. tu fai visualizzare l'EFFETTIVO!
    lunedì 7 gennaio 2013 13:20
  • Ti ringrazio, sei stato molto disponibile.

    Ciao

    You are wellcome! :°)

    lunedì 7 gennaio 2013 13:24
  • Si, l'ho visto e visualizzato.

    Grazie

    lunedì 7 gennaio 2013 13:43