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:

 1/*===============================================================================
 2  File: Program.cs    
 3  Dátum: 2011.12.31
 4  Leírás: demo kód, sql progress info
 5  SQL Server verziók: 2008 és újabb
 6  Szerző: Berke János -  IamBerke.com
 7---------------------------------------------------------------------------------
 8  (cc) 2011, IamBerke.com
 9 
10  Szabadon másolható, módosítható, bemutatható a kód, amenniyben nem kereskedelmi
11  célokat szolgál. A kód megjelenhet nyomtatott vagy elektronikus formában,
12  amennyiben a forrás megjelenítésre kerül, de a megjelnéshez a szerző
13  előzetes jóváhagyása is szükséges.
14 
15  A KÓD ÉS AZ INFORMÁCIÓK MINDENFÉLE GARANCIA NÉLKÜL "AS-IS" ÁLLNAK RENDELKEZÉSRE,
16  A SZERZŐ SEMMIFÉLE - SEM KÖZVETLEN, SEM KÖZVETETT - FELELŐSSÉGET NEM VÁLLAL.
17================================================================================*/
18 
19using System;
20using System.Collections.Generic;
21using System.Linq;
22using System.Text;
23using System.Data.SqlClient;
24 
25namespace SqlProgressSample
26{
27    class Program
28    {
29        static void Main(string[] args)
30        {
31            string connectionString = @"Data Source=.\SQL2K8R2; Initial Catalog=tempdb; Integrated Security=SSPI";
32 
33            using (SqlConnection connection = new SqlConnection(connectionString))
34            {
35                connection.Open();
36                connection.InfoMessage += new SqlInfoMessageEventHandler(SqlProgressEvent);
37                connection.FireInfoMessageEventOnUserErrors = true;
38 
39                using (SqlCommand command = new SqlCommand())
40                {
41                    command.Connection = connection;
42                    command.CommandText = @"SET NOCOUNT ON;
43 
44                                            CREATE TABLE #T
45                                            (
46                                                id int
47                                            );
48 
49                                            DECLARE @i int = 0;
50                                            PRINT 'Starting insert';
51                                            WHILE @i < 7999999
52                                                BEGIN
53                                                    INSERT INTO #T ([id]) VALUES (@i);
54                                                    SET @i += 1;
55         
56                                                    IF (@i % 10000 = 0)
57                                                        RAISERROR (N'%d rows inserted.', 10, 1, @i) WITH NOWAIT;
58                                                END
59                                            SET NOCOUNT OFF;";
60                    command.CommandType = System.Data.CommandType.Text;
61                    command.ExecuteNonQuery();
62                }
63 
64            }
65        }
66 
67        private static void SqlProgressEvent(object sender, SqlInfoMessageEventArgs e)
68        {
69            if (e.Errors.Count > 0)
70            {
71                Console.WriteLine(e.Errors[0].Message);
72            }
73        }
74 
75         
76    }
77}

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:

 1USE tempdb;
 2GO
 3
 4SET NOCOUNT ON;
 5
 6CREATE TABLE #T
 7(
 8   id int
 9);
10
11DECLARE @i int = 0;
12
13WHILE @i < 7999999
14   BEGIN
15       INSERT INTO #T ([id]) VALUES (@i);
16       SET @i += 1;
17        
18       IF (@i % 10000 = 0)
19           RAISERROR (N'%d rows inserted.', 10, 1, @i) WITH NOWAIT;
20   END
21SET 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.