Hogyan írjunk ki állapotinformációt hosszan tartó adatbázis műveletnél.

Az üzleti felhasználók nem igazán szeretnek várakozni egy-egy lekérdezésünk eredményére, türelmetlenek. Egy aprócska trükkel, még az 1-2 percig tartó lekérdezésünket is “szeretni” fogják SmileEgy egyszerű példával szeretném ezt megmutatni: képzeljük el azt az esetet, amikor egy táblába 8M sort kell betölteni és szeretnénk látni, hogy éppen hol tart a betöltés.

Ehhez egy konzol alkalmazást készítettem:

/*===============================================================================
  File: Program.cs     
  Dátum: 2011.12.31
  Leírás: demo kód, sql progress info
  SQL Server verziók: 2008 és újabb
  Szerző: Berke János -  IamBerke.com
---------------------------------------------------------------------------------
  (cc) 2011, IamBerke.com

  Szabadon másolható, módosítható, bemutatható a kód, amenniyben nem kereskedelmi
  célokat szolgál. A kód megjelenhet nyomtatott vagy elektronikus formában, 
  amennyiben a forrás megjelenítésre kerül, de a megjelnéshez a szerző 
  előzetes jóváhagyása is szükséges.

  A KÓD ÉS AZ INFORMÁCIÓK MINDENFÉLE GARANCIA NÉLKÜL "AS-IS" ÁLLNAK RENDELKEZÉSRE,
  A SZERZŐ SEMMIFÉLE - SEM KÖZVETLEN, SEM KÖZVETETT - FELELŐSSÉGET NEM VÁLLAL. 
================================================================================*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;

namespace SqlProgressSample
{
    class Program
    {
        static void Main(string[] args)
        {
            string connectionString = @"Data Source=.\SQL2K8R2; Initial Catalog=tempdb; Integrated Security=SSPI";

            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                connection.Open();
                connection.InfoMessage += new SqlInfoMessageEventHandler(SqlProgressEvent);
                connection.FireInfoMessageEventOnUserErrors = true;

                using (SqlCommand command = new SqlCommand())
                {
                    command.Connection = connection;
                    command.CommandText = @"SET NOCOUNT ON;

                                            CREATE TABLE #T
                                            (
	                                            id int
                                            );

                                            DECLARE @i int = 0;
                                            PRINT 'Starting insert';
                                            WHILE @i < 7999999
	                                            BEGIN
		                                            INSERT INTO #T ([id]) VALUES (@i);
		                                            SET @i += 1;
		
		                                            IF (@i % 10000 = 0)
			                                            RAISERROR (N'%d rows inserted.', 10, 1, @i) WITH NOWAIT;
	                                            END
                                            SET NOCOUNT OFF;";
                    command.CommandType = System.Data.CommandType.Text;
                    command.ExecuteNonQuery();
                }

            }
        }

        private static void SqlProgressEvent(object sender, SqlInfoMessageEventArgs e)
        {
            if (e.Errors.Count > 0)
            {
                Console.WriteLine(e.Errors[0].Message);
            }
        }

        
    }
}

 

2 fontos dolgot kell kiemelni a fenti kódból:

  • SqlInfoMessageEventHandler delegate: erre van szükségünk, hogy az info üzeneteket elérjük. Ezek a PRINT vagy a RAISERROR megfelelő hívásaival hozható létre.
  • PRINT vagy RAISERROR az SQL kódban.

Az alábbi SQL kód volt a konzol alkalmazásban is használva, ami minden 10000 beszúrás után kiírta, hogy éppen mennyinél jár:

USE tempdb;
GO

SET NOCOUNT ON;

CREATE TABLE #T
(
	id int
);

DECLARE @i int = 0;

WHILE @i < 7999999
	BEGIN
		INSERT INTO #T ([id]) VALUES (@i);
		SET @i += 1;
		
		IF (@i % 10000 = 0)
			RAISERROR (N'%d rows inserted.', 10, 1, @i) WITH NOWAIT;
	END
SET NOCOUNT OFF;

 

A RAISERROR 10-es severity-vel került meghívásra, illetve a WITH NOWAIT opcióval. Ez utóbbi arra szolgál, hogy a kliens fel az info üzeneteket azonnal elküldi, nem várja meg a lekérdezés lefutását.

Ahhoz, hogy ez minden esteben működjön a C# kódban az SqlConnection FireInfoMessageEventOnUserErrors tulajdonságát TRUE értékre kell állítani.

Add comment