Affichage des articles dont le libellé est SqlServer. Afficher tous les articles
Affichage des articles dont le libellé est SqlServer. Afficher tous les articles

lundi 11 avril 2011

Sql Serveur - Manipulation avancée de chaine de caratère (string) en TSQL

Il y a quelques années j'avais découvert deux articles abordant le traitement des chaînes de caractères en TSQL. Sql Serveur étant relativement pauvre en ce domaine j'avais immédiatement classé l'information sous le libellé "merveille à ne surtout pas perdre"... pourtant, au fil du temps, je réussis finalement à perdre ces références (Arghhh!!!).
Le hasard aura voulut que je retombe dessus... et cette fois, je vais faire ce qu'il faut pour ne pas les perdre à nouveau... je vais publier ces références.
Au passage, je vais également traduire l'introduction de Robyn Page and Phil Factor, les auteurs de ces deux articles.

Je vous invite d'ailleurs à prendre connaissance des autres articles édités par Robyn Page and Phil Factor [on y trouve en autre un article expliquant comment manipuler une feuille Excel :) ]

Introduction
Traduction "at the best" de l'introduction de l'article "SQL String User Function Workbench".

Nous allons vous présenter quelques-unes des très simples fonctions de manipulation de string.
Plutôt que d'inventer ceux que nous voudrions, nous allons aborder le problème sous un angme différent et implémenter, au mieux, les fonctions Python de manipulation de chaîne de caractères.
Plein d'exemples maus aussi quelques astuces de programmation !
Quelque-fois, lors de développement d'application Sql Serveur, vous commencez à avoir besoin de manipuler des chaînes de caractères. Pour un débutant, ces fonctions SQL de manipulation peuvent sembler particulièrement ingrates – rien à voir avec ce qui est disponible en Python dans le même domaine.
Cette différence c'est plutôt une question de style. Les fonctions de bases disponible en TSQL sont d'une grande puissance, et il n'est pas toujours évident de comprendre ce qu'elles font lorsque l'on insecte le code. Personne n'essayerait de prétendre que la fameuse function STUFF est intuitive.
Lorsque l'on met en place un projet DB, il est toujours préférable de commencer avec une boîte à outils (toolkit) reprennant des fonctions élémentaire de traitement de chaîne de caractère, cela rendra votre TSQL code lisible. C'est seulement lorsque l'on rencontrera des problèmes de performance particulier qu'il sera nécessaire d'utiliser les fonction de bases plutôt que celles de la boite à outils.
Dans l'effervescence d'une équipe de développement, les choses semblent mieux se présenter si des fonctions élémentaires de traitement de chaîne de caractères sont disponibles et prêtent à l'emploi.
Sans raison particulières, nous aimons utiliser les fonctions de manipulation de chaîne de caractères de PHP et Python adaptés à l'usage de SQL Serveur.
 

Références
Voici donc une liste d'articles totalement incontournables dans ce domaine.
On y trouvera du code vraiment très précieux ainsi que les explications les concernant.
Code source
Liste des fonctions
Dans le premier article, l'on retrouvera la définition des fonctions suivantes:
   1. Capitalize
   2. Center
   3. Count Substring in String
   4. EndsWith
   5. ExpandTabs
   6. IsAlnum
   7. IsAlpha
   8. IsDigit
   9. IsLower
  10. IsTitle
  11. IsSpace
  12. LJust
  13. LStrip
  14. RFind
  15. RJust
  16. RStrip
  17. Strip
  18. SwapCase
  19. Title
  20. Zfill


Tandis que le deuxième article abordera des fonctions nettement plus avancées telles que:
   1. Split function
   2. SplitLines
   3. Within (not from python)
   4. EndsWith
   5. StartsWith
   6. Contains (not from python)
   7. Join
   8. Parts (not from python)
   9. Partition
  10. RPartition

Le troisième article abordant le traitement des arrays (php) aborde déclare les fonctions suivantes:
  • The array() function
  • The PHP-style str_Replace() function
  • The str_Find function
  • The str_GetDelimited function
Finalement, l'article consacré à l'utilisation des régulars expression en TSQL aborde les points suivants:
   1. Introduction
   2. The OLE Functions
         1. The OLE Regex Match function
         2. The OLE Regex Replace function
         3. The OLE Regex Find (Execute) function
   3. Combining two Regexs
   4. OLE Regex Performance

mardi 1 mars 2011

Sql AntiPatterns: comment éviter les pièges de la programmation de base de données

Voici une suggestion de lecture pour tous les développeurs devant travailler avec un moteur SQL.

"SQL Antipatterns: Avoiding the Pitfalls of Database Programming" est un livre plutôt amusant dans le sens ou il n'essaye pas de vous dire ce qu'il faut faire de bien mais présente les conséquences des erreurs que d'autres ont déjà eu l'occasion de faire avant vous.
Traduction de la description de l'auteur:
La plupart des développeurs ne sont pas des experts en SQL, et la plupart des requêtes SQL utilisées ne sont pas efficaces, difficile à maintenir, et quelque-fois totalement erronées. Ce livre vous présente toutes les erreurs courantes, et vous guide ensuite vers les meilleurs solutions. De plus, ce livre vous présente  ce qui est caché derrière ces solutions, ainsi, vous apprenez beaucoup au sujet des bases de données relationnelle tout au cours de ce livre.

mardi 25 janvier 2011

ADO.NET - Aide mémoire - 4/4 : Tutoriel

Le présent article fait parti d'une suite logique de 4 articles: "ADO.NET - Aide mémoire - 1/4 : Execution, Parametres, StoredProc, DataReader",  "ADO.NET - Aide mémoire - 2/4 : DataSet", "ADO.NET - Aide mémoire - 2/4 : DataView" et "ADO.NET - Aide mémoire - 4/4 : Tutoriel"

eTutorial.org publie un Tuto Ado.Net plus que largement complet, le genre de référence que l'on place dans les perles du Net. Ce tutoriel prend sa source dans les excellents ouvrages de O'Reilly. Il faudra cependant composer avec l'affichage des publicités.

Admirez donc la magnifique table des matières que j'ai repris ci-dessous.
Si vous vous posez une question, tentez d'abord de trouver une réponse dans le tutoriel de eTutorial.

Preface
Audience

Contents of This Book
What's on the CD-ROM
Conventions Used in This Book
Comments and Questions
Acknowledgments

Part I: ADO.NET Tutorial

Chapter 1. Introduction


1.1 ADO.NET Data Providers
1.2 Connected and Disconnected Data

Chapter 2. .NET Data Providers

2.1 Data Providers
2.2 Selecting a Data Provider
2.3 Creating a Custom Data Provider

Chapter 3. Connections

3.1 Connection Object Overview
3.2 The Connection String
3.3 Opening and Closing Connections
3.4 Connection Pooling

Chapter 4. Commands


4.1 Command Object Overview
4.2 Creating and Executing a Command
4.3 Parameter Object Overview
4.4 Parameterized Commands
4.5 Commands with Stored Procedures
4.6 Commands and Data Definition Language (DDL)

Chapter 5. DataReaders

5.1 DataReader Object Overview
5.2 Performing a Query with a DataReader
5.3 Stored Procedures with the DataReader
5.4 DataReaders and Schema Information

Chapter 6. DataSets

6.1 Creating an Untyped DataSet
6.2 Working with Tables in the DataSet
6.3 Adding and Removing Relations
6.4 Adding Custom Information
6.5 Cloning the Schema
6.6 Copying the DataSet
6.7 Merging Two DataSets
6.8 Removing All Data
6.9 Resetting the DataSet
6.10 Committing and Discarding Changes

Chapter 7. DataTables

7.1 Creating a DataTable
7.2 Working with Columns
7.3 Constraints
7.4 Primary Key
7.5 Rows
7.6 Loading Data
7.7 Committing and Discarding Changes
7.8 Cloning the Schema of the Table
7.9 Copying the Table
7.10 Selecting a Subset of Rows
7.11 Performing Aggregate Calculations
7.12 Removing All Data
7.13 Resetting the Table
7.14 Identifying Errors in the Table
7.15 DataTable Events

Chapter 8. DataColumns

8.1 Creating DataColumns
8.2 Creating AutoIncrement Columns
8.3 Creating Expression Columns
8.4 Handling Null Values
8.5 Mapping .NET Data Provider Types to .NET Framework Types

Chapter 9. DataRows

9.1 Creating a DataRow
9.2 Updating Rows
9.3 Deleting Rows
9.4 Using Row State Information
9.5 Using Row Version Information
9.6 Accepting or Rejecting Changes to Rows
9.7 Navigating Parent and Child Rows
9.8 Using Row Error Information

Chapter 10. Constraints

10.1 Constraint Object Overview
10.2 The UniqueConstraint
10.3 The ForeignKeyConstraint

Chapter 11. DataRelations
11.1 DataRelation Object Overview
11.2 Navigating Relational Data

Chapter 12. DataViews and Data Binding


12.1 The DataView and DataViewManager
12.2 Sorting and Filtering
12.3 Accessing Data Through a DataView
12.4 Windows Data Binding
12.5 ASP.NET Data Binding

Chapter 13. Strongly Typed DataSets

13.1 Creating a Strongly Typed DataSet
13.2 Adding a Row
13.3 Editing a Row
13.4 Finding a Row
13.5 Null Data
13.6 Navigating Hierarchical Data
13.7 Annotations

Chapter 14. DataAdapters


14.1 Creating DataAdapter Object
14.2 Retrieving Data from the Data Source
14.3 Retrieving Schema Information from the Data Source
14.4 Updating the Data Source
14.5 Mapping Tables and Columns
14.6 AcceptChangesDuringFill
14.7 ContinueUpdateOnError
14.8 DataAdapter Events

Chapter 15. Updating the Data Source

15.1 SqlCommandBuilder Class Overview
15.2 Updating a Data Source Using Command Builder
15.3 Updating a Data Source Using Custom Logic
15.4 Refreshing Data After Updating
15.5 Retrieving Updated Values from the Data Source
15.6 Updating Data in Related Tables
15.7 Handling Concurrency Issues
15.8 Optimization

Chapter 16. Transactions


16.1 Manual Transactions
16.2 Isolation Levels
16.3 Savepoints
16.4 Nested Transactions
16.5 Transactions Using a DataAdapter
16.6 Automatic Transactions

Chapter 17. XML and the DataSet

17.1 DataSet XML Methods
17.2 Shaping DataSet XML
17.3 Other .NET XML Classes
17.4 XmlDataDocument Object Overview
17.5 Using the Data Objects to Edit XML
17.6 SQL Server 2000 XML

Part II: ADO.NET Core Classes

Chapter 18. The Connection Class

18.1 Comments/Troubleshooting
18.2 Properties Reference
18.3 Methods Reference
18.4 Events Reference

Chapter 19. The Command Class


19.1 Comments/Troubleshooting
19.2 Properties Reference
19.3 Collections Reference
19.4 Methods Reference

Chapter 20. The Parameter Class

20.1 Comments/Troubleshooting
20.2 Properties Reference

Chapter 21. The DataReader Class

21.1 Comments/Troubleshooting
21.2 Properties Reference
21.3 Methods Reference

Chapter 22. The DataSet Class

22.1 Comments/Troubleshooting
22.2 Properties Reference
22.3 Collections Reference
22.4 Methods Reference
22.5 Events Reference
 

Chapter 23. The DataTable Class

23.1 Comments/Troubleshooting
23.2 Properties Reference
23.3 Collections Reference
23.4 Methods Reference
23.5 Events Reference

Chapter 24. The DataColumn Class

24.1 Comments/Troubleshooting
24.2 Properties Reference
24.3 Collections Reference

Chapter 25. The DataRow Class

25.1 Comments/Troubleshooting
25.2 Properties Reference
25.3 Collections Reference
25.4 Methods Reference

Chapter 26. The Constraint Class


26.1 Comments/Troubleshooting
26.2 Properties Reference
26.3 Collections Reference

Chapter 27. The DataRelation Class

27.1 Comments/Troubleshooting
27.2 Properties Reference
27.3 Collections Reference

Chapter 28. The DataView Class

28.1 Comments/Troubleshooting
28.2 Properties Reference
28.3 Methods Reference
28.4 Events Reference

Chapter 29. The DataAdapter Class


29.1 Comments/Troubleshooting
29.2 Properties Reference
29.3 Collections Reference
29.4 Methods Reference
29.5 Events Reference

Chapter 30. The CommandBuilder Class

30.1 Comments/Troubleshooting
30.2 Properties Reference
30.3 Methods Reference

Chapter 31. The Transaction Class

31.1 Comments/Troubleshooting
31.2 Properties Reference
31.3 Methods Reference

Part III: API Quick Reference

Chapter 32. How to Use This Quick Reference

32.1 Finding a Quick-Reference Entry
32.2 Reading a Quick-Reference Entry

Chapter 33. Converting from C# to VB Syntax

33.1 General Considerations
33.2 Classes
33.3 Structures
33.4 Interfaces
33.5 Class, Structure, and Interface Members
33.6 Delegates
33.7 Enumerations

Chapter 34. The System.Data Namespace


Chapter 35. The System.Data.Common Namespace

Chapter 36. The System.Data.SqlClient Namespace

Chapter 37. The System.Data.OleDb Namespace

Chapter 38. The System.Data.SqlTypes Namespace

Part IV: Appendixes

Appendix A. ADO.NET Providers

A.1 The SQL Server Provider
A.2 The OLE DB Provider
A.3 The ODBC .NET Provider
A.4 The Oracle .NET Provider
A.5 The ODP.NET Provider

Appendix B. ADO.NET XML Extensions

B.1 codegen Namespace
B.2 msdata Namespace
B.3 diffgr Namespace

Appendix C. Microsoft Data Engine (MSDE)

C.1 Installing MSDE
C.2 MSDE Essentials
C.3 Adding the Northwind Data
C.4 Migrating MSDE to SQL Server

Type, Method, Property, and Field Index

ADO.NET - Aide mémoire - 3/4 : DataView

Le présent article fait parti d'une suite logique de 4 articles: "ADO.NET - Aide mémoire - 1/4 : Execution, Parametres, StoredProc, DataReader",  "ADO.NET - Aide mémoire - 2/4 : DataSet", "ADO.NET - Aide mémoire - 2/4 : DataView" et "ADO.NET - Aide mémoire - 4/4 : Tutoriel"

DataView
Un DataView permet de créer différentes vues des données stockées dans une DataTable, une aptitude qui est souvent utilisée dans les opérations de data-binding. En utilisant un DataView, il est possible d'exposer les données d'une table avec un ordre de tri différent, il est également possible de filtrer les données par état (avant/après modification) ou encore filtrer sur base d'une expression (équivalent d'une clause "where" en sql).

Un DataView fournit une vue dynamique des données stockées dans la DataTable sous-jacente: le contenu, l'ordre de tri, et aspects relationnels reflètent les modifications tels qu'elles se présentent. Cette caractéristique diffère de méthode "Select" de la DataTable qui produit un tableau de DataRow depuis la DataTable sur base d'un filtre et/ou tri particulier: ce contenu reproduit les modifications dans la table sous-jacente, mais les aspects relationnels et l'ordonnancement restent statiques. Les capacités dynamiques du DataView en font un candidat idéal pour l'utilisation de data-binding.

Un DataView procure une vue dynamique d'un ensemble de données unique, un peu comme les vues d'une base de données, auquel il est possible d'appliquer différents ordres de tri et critères de sélection. Au contraire d'un vue en DB, un DataView ne peut pas être manipulé comme une table et ne peut pas fournir une jointure entre plusieurs tables. Vous ne pouvez pas non plus exclure des colonnes qui existent dans la table source, ni ajouter des colonnes tels que des colonnes calculées qui ne se trouveraient pas dans la table source.

Vous pouvez utiliser une DataViewManager pour gérer la configuration des vues pour toutes les tables d'un DataSet. Le DataViewManager procure la possibilité de gérer la configuration du DefaultView de chaque table de façon pratique. Lorsqu'un contrôle est lié (bind) à plus d'une table d'un DataSet, faire le binding vers un DataViewManager est alors un choix idéal.

Voici quelques références complémentaires:
  • Page reprenant les Articles de Microsoft concernant les DataViews, articles énumérés et décrits ci-dessous.
  • Creating a DataView (ADO.NET)
    Décrit comment créer un DataView pour un DataTable
  • Sorting and Filtering Data (ADO.NET)
    Montre comment initialiser les propriétés d'un DataView pour retourner un sous-ensemble des enregistrements de données qui satisfont les critères de filtrage, ou qui retourne les données dans un ordre particulier.
  • DataRows and DataRowViews (ADO.NET)
    Explique comment accéder aux données exposées par le DataView.
  • Finding Rows (ADO.NET)
    Explique comment retrouver un enregistrement particulier dans un DataView
  • ChildViews and Relations (ADO.NET)
    Décrit comment créer une vue à partir d'une relation master-detail (parent-child) en utilisant un DataView.
  • Modifying DataViews (ADO.NET)
    Explique comment modifier les données de la DataTable sous-jacente à partir d'un DataView, incluant également l'activation/désactivation des mise-à-jours (updates).
  • Handling DataView Events (ADO.NET)
    Explique comment utiliser l'évènement ListChanged pour recevoir une notification lorsque le contenu ou l'ordre de tri d'un DataView est en cours de modification.
  • Managing DataViews (ADO.NET)
    Explique comment utiliser un DataViewManager pour gérer les configurations des DataView pour chaque table du DataSet.
Exemple de relation Master-Detail et ChildView
Directement issu d'un des articles MSDN, voici un petit morceau de code qui montre comment définir une relation Master-Detail dans un DataSet mais aussi comment l'exploiter pour créer un ChildView (CreateChildView)
DataTable catTable = catDS.Tables["Categories"];
DataTable prodTable = catDS.Tables["Products"];

// Create a relation between the Categories and Products tables.
DataRelation relation = catDS.Relations.Add("CatProdRel", 
    catTable.Columns["CategoryID"],
    prodTable.Columns["CategoryID"]);

// Create DataViews for the Categories and Products tables.
DataView catView = new DataView(catTable, "", "CategoryName", 
  DataViewRowState.CurrentRows);
DataView prodView;

// Iterate through the Categories table.
foreach (DataRowView catDRV in catView)
{
  Console.WriteLine(catDRV["CategoryName"]);

  // Create a DataView of the child product records.
  prodView = catDRV.CreateChildView(relation);
  prodView.Sort = "ProductName";

  foreach (DataRowView prodDRV in prodView)
    Console.WriteLine("\t" + prodDRV["ProductName"]);
}

Autre Exemple issus de MSDN - DataViewManager, chargement Dataset.

Cet autre exemple de MSDN (légèrement adapté) est censé démontrer l'usage du DataViewManager.
Personnellement, je crois qu'il y a moyen de mieux faire.
Par contre, ce qui est beaucoup intéressant, c'est le chargement de plusieurs tables et de leurs relations dans un seul dataset. Tables de la DB AdventureWorks.

Fichier source: AdoNet_DataViewManager.cs.

using System;using System.Collections.Generic;using System.Data;using System.Data.SqlClient;using System.Windows.Forms;
public class MyClass
{
    public static void RunSnippet()
    {
        // Adventure Works creation script and database CAN BE DOWNLOADED from CodePlex
        //    http://sqlserversamples.codeplex.com/
        //
        SqlConnection connection = new SqlConnection( "Data Source=localhost;Initial Catalog=AdventureWorksLT2008;Integrated Security=SSPI;" );
        // Assumes connection is a valid SqlConnection to Northwind.
        // Create a Connection, DataAdapters, and a DataSet.
        SqlDataAdapter custDA = new SqlDataAdapter(
        "SELECT CustomerID, CompanyName FROM SalesLT.Customer", connection);
        SqlDataAdapter orderDA = new SqlDataAdapter(
        "SELECT SalesOrderID, CustomerID FROM SalesLT.SalesOrderHeader", connection);
        SqlDataAdapter ordDetDA = new SqlDataAdapter(
        "SELECT SalesOrderID, ProductID, OrderQty FROM SalesLT.SalesOrderDetail", connection);
        
        DataSet custDS = new DataSet();
        
        // Open the Connection.
        connection.Open();

        // Fill the DataSet with schema information and data.
        custDA.MissingSchemaAction = MissingSchemaAction.AddWithKey;
        orderDA.MissingSchemaAction = MissingSchemaAction.AddWithKey;
        ordDetDA.MissingSchemaAction = MissingSchemaAction.AddWithKey;
    
        custDA.Fill(custDS, "Customers");
        orderDA.Fill(custDS, "Orders");
        ordDetDA.Fill(custDS, "OrderDetails");
    
        // Close the Connection.
        connection.Close();
    
        // Create relationships.
        custDS.Relations.Add("CustomerOrders",
            custDS.Tables["Customers"].Columns["CustomerID"],
            custDS.Tables["Orders"].Columns["CustomerID"]);
    
        custDS.Relations.Add("OrderDetails",
            custDS.Tables["Orders"].Columns["SalesOrderID"],
            custDS.Tables["OrderDetails"].Columns["SalesOrderID"]);
    
        // Create default DataView settings.
        DataViewManager viewManager = new DataViewManager(custDS);
        
        foreach (DataViewSetting viewSetting in viewManager.DataViewSettings)
        viewSetting.ApplyDefaultSort = true;
        
        viewManager.DataViewSettings["Customers"].Sort = "CompanyName";
        
        // Bind to a DataGrid.
        //   Note: N'affichera absolument rien sous snippet compiler
        System.Windows.Forms.DataGrid grid = new System.Windows.Forms.DataGrid();
        grid.SetDataBinding(viewManager, "Customers");

    }
    
    #region Helper methods
    ...
    #endregion
}

mercredi 19 janvier 2011

ADO.NET - Aide mémoire - 2/4 : DataSet

Le présent article fait parti d'une suite logique de 4 articles: "ADO.NET - Aide mémoire - 1/4 : Execution, Parametres, StoredProc, DataReader",  "ADO.NET - Aide mémoire - 2/4 : DataSet", "ADO.NET - Aide mémoire - 2/4 : DataView" et "ADO.NET - Aide mémoire - 4/4 : Tutoriel"

Cet aide mémoire se focalisera plutôt sur l'utilisation générale des DataSets.
On y découvrira:
  • L'utilisation du SqlCommand, SqlDataAdapter et DataSet afin de lire les données (via DataTable et DataRow).
  • Les méthodes de lectures via:
    • DataTable.Rows : collection of DataRow
    • DataTable.Select : Permettant le filtrage et le tri d'un sous ensemble des enregistrements. Aussi une collection de DataRow.
    • DataTable.DefaultView : DataView permettant également le filtrage et le tri d'un sous ensemble. DataView contenant une collection de DataRowView.
  • Mise à jour des données d'une DataTable dans un Dataset (et sauvegarde en DB).
eTutorial.org

    En référence, je mentionnerais également:
    DataViews
    Les DataViews offrent de nombreuses possibilités avancées qui va bien au-delà de ce qui est présenté dans l'exemple de cet article.
    L'article "ADO.NET - Aide mémoire - 3/4 : DataViews" reprend une description et quelques références Microsoft.

    Exemple 
    Voici l'exemple dont seulement les parties les plus pertinentes sont reprises dans l'article (voir fichier source pour la totalité du code).
    Cet exemple utilise Snippet Compiler comme d'habitude.
    Fichier Source: AdoNet_DataSet.cs
    using System;using System.Collections.Generic;using System.Data;using System.Data.SqlClient;
    public class MyClass
    {
        public static void RunSnippet()
        {
            DropTestDatabase(); // Drop the test database if any exists
            CreateTestDatabase();
            
            DisplaySortedDataset();
            UpdateDataSet();
        }
        
        public static void DisplaySortedDataset () {
            WL( "=== DATASET read/filter/sort operation =================" );
            SqlConnection sqlConn = GetTestConnection();
            sqlConn.Open();
            
            SqlCommand sqlCmd = new SqlCommand( "select * from usr", sqlConn );
            SqlDataAdapter sqlAd = new SqlDataAdapter( sqlCmd );
            DataSet ds = new DataSet();
            sqlAd.Fill( ds, "dataTableName_User" ); // give an explicit name for the sample
            // Display all DataTable.TableName in the DataSet
            foreach( DataTable tbl in ds.Tables )
                WL( String.Format( "  DataSet.Tables[i].TableName="+ tbl.TableName ));
    
            // === DataTable Rows =====================
            // Display the Data
            WL( "Display data (Via Rows)" );
            foreach( DataRow dr in ds.Tables["dataTableName_User"].Rows )
                WL( String.Format( "   User {0} (id:{1})", dr["usrname"].ToString(), dr["uid"].ToString() ));
            
            // === DataTable Select ===================
            // Sort it + redisplay it
            WL( "Display sorted data (via Select)" );
            DataRow[] sorted = ds.Tables["dataTablename_User"].Select("usrname like '%'", "uid desc");
            foreach( DataRow dr in sorted )
                WL( String.Format( "   User {0} (id:{1})", dr["usrname"].ToString(), dr["uid"].ToString() ));
            // SubSelect + sort
            WL( "Display filtered data (via Select)" );
            DataRow[] filtered = ds.Tables["dataTablename_User"].Select("usrname like '%a%'", "usrname asc");
            foreach( DataRow dr in filtered )
                WL( String.Format( "   User {0} (id:{1})", dr["usrname"].ToString(), dr["uid"].ToString() ));
    
            // === DataView ===========================
            // Sorted and filtered with DataView
            //   DataView may be very useful to work with visual componant
            //   More information on DataViews on Microsoft site @
            //     http://msdn.microsoft.com/en-us/library/fdcwwhez.aspx
            //
            WL( "Display filtered data (via DataView)" );
            DataView dv = ds.Tables["dataTableName_User"].DefaultView;
            dv.RowFilter = "usrname like '%a%'";
            dv.Sort = "uid desc";
            // In a DataView, the Rows are available via DataRowView        
            foreach( DataRowView drv in dv )
                WL( String.Format( "   User {0} (id:{1})", drv["usrname"].ToString(), drv["uid"].ToString() ));
            
            sqlConn.Close();
        }
        
        public static void UpdateDataSet () {
            WL( "=== UPDATE operation ============================" );
            SqlConnection sqlConn = GetTestConnection();
            sqlConn.Open();
            // Read the data
            SqlDataAdapter ad = new SqlDataAdapter( "Select * from USR order by usrName desc", sqlConn );
            // Create a CommandBuilder, this will automatically generate the appropriate commands
            // and set the various properties of the SqlAdapter.
            SqlCommandBuilder cmdBuilder = new SqlCommandBuilder( ad );
            // Create and fill dataset
            DataSet ds = new DataSet();
            ad.Fill( ds, "USR" );
    
            // Display the Data
            WL( "Display data BEFORE change" );
            foreach( DataRow dr in ds.Tables["USR"].Rows )
                WL( String.Format( "   User {0} (id:{1})", dr["usrname"].ToString(), dr["uid"].ToString() ));
    
            // Update Data
            DataRow[] filtered = ds.Tables["USR"].Select( "usrname like 'anatole'", "uid" );
            if( filtered.Length > 0 ) {
                filtered[0]["usrname"] = "John Doe";
            }
            
            // Add a new row 
            //   Removal is done with DataTable.Rows.RemoveAt(RowIndex) or DataTable.Rows.Remove( DataRowObject )
            DataRow added = ds.Tables["USR"].Rows.Add();
            added["usrname"] = "Florentin Célestin";
            added["uid"] = 120;
            
            // Save the DataSet
            ad.Update(ds, "USR");
            
            // ReRead the DataSet and Display result
            ds.Clear();
            ad.Fill( ds, "USR" );
            // Display the Data
            WL( "Display data AFTER change" );
            foreach( DataRow dr in ds.Tables["USR"].Rows )
                WL( String.Format( "   User {0} (id:{1})", dr["usrname"].ToString(), dr["uid"].ToString() ));
            
            sqlConn.Close();
        }
        
        #region Sample Database helper    // Connect and create the Test Database
        // See the source file for the Full code. 
        #endregion
        
        #region Helper methods    // ...    
        #endregion
    }
    

    dimanche 16 janvier 2011

    ADO.NET - Aide mémoire - 1/4 : Execution, Parametres, StoredProc, DataReader

    Parce que rien ne vaut quelques bons articles didactiques, voici une série de références qui peuvent se montrer bien utiles un jour ou l'autre.

    Le présent article fait parti d'une suite logique de 4 articles: "ADO.NET - Aide mémoire - 1/4 : Execution, Parametres, StoredProc, DataReader",  "ADO.NET - Aide mémoire - 2/4 : DataSet", "ADO.NET - Aide mémoire - 2/4 : DataView" et "ADO.NET - Aide mémoire - 4/4 : Tutoriel"

    Mettre rapidement le pied à l'étrier
    Voici une série d'articles récapitulatifs en provenance de DeveloperFusion.
    Ils reprennent toute la chaine d'objets Ado.Net pour SqlServeur et Access avec les options principales ainsi que des exemples en C# et VB.NET. Un excellent condensé pour aborder les DataReader, les appels aux Stored Procedure, les DataSets, Update/Delete/Insert, etc.
      Quelques articles et sites de références
      • ConnectionStrings.com site incroyable reprenant des centaines de configurations de connections strings pour les différents moteurs DB SqlServeur, Oracle, pervasive, MySql, etc. Un incontournable!
      • Retrieving data using a C# .Net DataReader
        Le principal intérêt de cet article réside dans la récupération d'un dataset depuis une stored procedure avec un DataReader et remplir une ListView avec les données (réclame de passer par une structure intermédiaire).
      • Configuration Settings File for providing application configuration data
        Toujours utile, ce petit article explique comment configurer des paramètre dans le fichier app.config (idéal pour stocker une ConnectionString). 
      • Juste une note pour mentionner qu'il est possible de créer d'une connexion Sql depuis le fichier de config (en utilisant les Entreprise Application Blocks ).
        Il faudra faire un peu de recherche pour compléter ce point.
      Data Access Application Block

      Par le Data Access Application Block (DAAB pour les intimes), Microsoft propose une couche logicielle pour l'accès aux données de façon générique et se défaire du fournisseur d'accès aux bases de données. Ainsi il est supposé un gain de temps et de code pour le programmeur puisqu'il y a moins de lignes à coder.
      Mais dans la réalité, ce n'est pas si simple. La généricité concerne les requêtes de base et les différences entre les fournisseurs de bases de données apparaissent rapidement. Du coup, le code se rallonge puisqu'il faut créer des instructions différentes selon le provider. (source: cet article sur developper.com)

      Cet article n'aborde pas la section "Data Access Application Block" de Microsoft patterns & practice entreprise library. C'est une solution vraiment puissante mais je ne dispose pas d'assez de temps pour m'y attarder.
      En attendant, vous pouvez vous renseigner ici:

      Exemples Ado.Net
      Voici quelques exemples (Snippet Compiler) de code rudimentaire mais démontrant néanmoins différents usages Ado.Net.

      Ce premier exemple s'attarde sur:
      • ExecuteNonQuery - pour des opérations tels que Insert, Delete, modification de schema, etc.
      • ExecuteScalar - pour récupérer une seule valeur de retour (première ligne, première colonne).
      • ExecuteReader - pour obtenir un reader permettant de passer l'ensemble des données en revue.
      • Query paramétrisé
      • Stored Procedure paramétrisée et avec valeur de retour.
      Source file: AdoNet_Samples.cs
        using System;
        using System.Collections.Generic;
        using System.Data;
        using System.Data.SqlClient;
        
        public class MyClass
        {
                public static void RunSnippet()
            {
                // Call to the various demo functions
                ExecuteNonQuery();
                ExecuteScalar();
                ExecuteReader();
                ParametrizedQuery();
                CallStoredProc(); 
            }
            
            /// <summary>
            /// Get a Sql Connection to Master Database.
            /// Always close the connection as soon as possible.
            /// </summary>
            /// <returns></returns>
            public static SqlConnection GetMasterConnection() {
                // using intégrated security to not revealing my SA password :-)
                string ConnString = "Data Source=localhost;Initial Catalog=master;Integrated Security=SSPI;";
                return new SqlConnection( ConnString );
            }
            
            public static SqlConnection GetConnection( string DBName ){
                string ConnString = String.Format("Data Source=localhost;Initial Catalog={0};Integrated Security=SSPI;", DBName );
                return new SqlConnection( ConnString );    
            }
        
            /// <summary>
            /// Demonstrate the EXECUTE Non Query.
            /// use SqlCommand.ExecuteNonQuery only when no result is espected (like DELETE, UPDATE statements)
            /// </summary>
            public static void ExecuteNonQuery(){
                WL( "--- EXECUTE Non Query ---" );
                SqlConnection SqlConn = GetMasterConnection();
                SqlConn.Open();
                WL( "Create database BLABLA" );
                SqlCommand SqlCmd = new SqlCommand( "Create database BlaBla", SqlConn );
                SqlCmd.ExecuteNonQuery();
                // see method r.RecordsAffected when used with DELETE or UPDATE statement
                
                WL( "Drop database BLABLA" );
                SqlCommand SqlCmd2 = new SqlCommand( "Drop database BlaBla", SqlConn );
                SqlCmd2.ExecuteNonQuery();
                
                SqlConn.Close(); // Close connection Asap
            }
            
            /// <summary>
            /// Demonstrate the SCALAR READING.
            /// use SqlCommand.ExecuteScalar (scalar reading) to retreive the result of the first row, first columns.
            /// </summary>
            public static void ExecuteScalar(){
                WL( "--- SCALAR READING ---" );
                SqlConnection SqlConn = GetMasterConnection();
                SqlConn.Open();
                // Read number of SqlUsers
                SqlCommand SqlCmd = new SqlCommand( "select count(*) from sys.sysusers", SqlConn );
                int iUserCount = (int)SqlCmd.ExecuteScalar();
                // see method r.HasRows to see if there is data
                WL( String.Format( "There are {0} sql users in the database", iUserCount ));
                
                SqlConn.Close(); // Close connection ASAP    
            }
            
            /// <summary>
            /// Demonstrate the DATASET READER
            /// use SqlCommand.ExecuteReader when needed to retreive a Dataset
            /// </summary>
            public static void ExecuteReader(){
                WL( "--- EXECUTE READER ---" );
                SqlConnection SqlConn = GetMasterConnection();
                SqlConn.Open();
                // Return list of user (and a null row).
                SqlCommand SqlCmd = new SqlCommand( "SELECT UID, NAME FROM SYS.SYSUSERS union select null, null", SqlConn );
                SqlDataReader r = SqlCmd.ExecuteReader();
                // see method r.HasRows to see if there is data
                WL( "Reading SqlUser list" );
                while( r.Read() ){
                    // Test nullability
                    if( r["uid"] == DBNull.Value )
                        continue;
                    // Cast SmallInt Sql datatype to Short C# type
                    WL( String.Format( "   {0} ({1})", (string)r["name"],  (short)r["uid"]  ) );
                }
                r.Close(); // close DataSet Asap
                SqlConn.Close(); // Close Connection Asap
            }
            
            /// <summary>
            /// Demonstrate how to add parameters to a query command.
            /// </summary>
            public static void ParametrizedQuery() {
                WL( "--- Parametrized Query ---" );
                SqlConnection SqlConn = GetMasterConnection();
                SqlConn.Open();
                SqlCommand SqlCmd = new SqlCommand( "SELECT id, name from sys.sysobjects where xtype = @TypeToFilter", SqlConn );
                SqlCmd.Parameters.Add( new SqlParameter( "TypeToFilter", SqlDbType.NVarChar, 5 ) );
                SqlCmd.Parameters["TypeToFilter"].Value="U"; // @ not required for parameter name
                SqlDataReader r = SqlCmd.ExecuteReader();
                while( r.Read() )
                    WL( String.Format("id:{0}, name:{1}", r["id"], r["name"] ) );
                r.Close();
                
                SqlCmd.Parameters["TypeToFilter"].Value="PC";
                r = SqlCmd.ExecuteReader();
                while( r.Read() )
                    WL( String.Format("id:{0}, name:{1}", r["id"], r["name"] ) );
                r.Close();
                
                SqlConn.Close();
            }
            
            /// <summary>
            /// Demonstrate how to call a stored procedure and retreive the return value parameter
            /// </summary>
            public static void CallStoredProc(){        
                WL( "--- STORED PROCEDURE call ---" );
                // drop existing database if exists
                SqlConnection SqlConnDropDb = GetMasterConnection();
                try {
                    // Drop database
                    WL( "X) dropping test database" );
                    SqlConnDropDb.Open();
                    SqlCommand SqlDrop = new SqlCommand( "Drop database StoredProcTest", SqlConnDropDb );
                    SqlDrop.ExecuteNonQuery();
                    WL( "Previous StoredProcTest database droped" );
                }
                catch {
                    WL( "No previous StoredProcTest database" );
                }
                finally {
                    SqlConnDropDb.Close();
                }
        
                
                WL( "1) Create database and Stored proc" );
                SqlConnection SqlConn = GetMasterConnection();
                SqlConn.Open();
                SqlCommand SqlCmd = new SqlCommand( "Create database StoredProcTest", SqlConn );
                SqlCmd.ExecuteNonQuery();
                SqlConn.Close();
                
                SqlConnection SqlConnProc = GetConnection( "StoredProcTest" );
                SqlConnProc.Open();
                SqlCommand SqlCmdProc = new SqlCommand( "CREATE TABLE USR ( uid int not null, usrname varchar(20) not null)", SqlConnProc );
                SqlCmdProc.ExecuteNonQuery();
                SqlCmdProc.CommandText = "INSERT INTO USR VALUES ( 1, 'Dominique' )";
                SqlCmdProc.ExecuteNonQuery();
                SqlCmdProc.CommandText = "INSERT INTO USR VALUES ( 2, 'Francoise' )";
                SqlCmdProc.ExecuteNonQuery();
                // Create the stored procedure de test HAVING a RETURN PARAM
                SqlCmdProc.CommandText = 
        @"create procedure UpdateUsr( @Uid as int, @NewName as varchar(20), @Result Varchar(50) OUTPUT )
        as
          Update USR 
          Set UsrName = @NewName 
          where Uid = @Uid
          
          if( @@ROWCOUNT > 0 )
            SET @Result = 'c est fait'
          ELSE
            SET @Result = 'connait pas!' ";
                SqlCmdProc.ExecuteNonQuery();
                SqlConnProc.Close();
                
                // Tester la stored procedure 
                WL( "2) Dataset before stored Proc Call" );
                SqlConnProc.Open();
                SqlCommand SqlRead = new SqlCommand( "select * from USR order by uid", SqlConnProc );
                SqlDataReader r = SqlRead.ExecuteReader();
                while( r.Read() ) 
                    WL( String.Format( "   UID: {0} - UserName: {1}", r["UID"], r["UsrName"] ) );
                r.Close();
                
                // Calling the stored procedure
                WL( "3) Call the stored Proc" );
                WL( "Update existing UID 2" );
                SqlCommand storedProcCall = new SqlCommand( "UpdateUsr", SqlConnProc );
                storedProcCall.CommandType = CommandType.StoredProcedure;
                storedProcCall.Parameters.AddWithValue( "@uid", 2 );
                storedProcCall.Parameters.AddWithValue( "@newname", "John Doe" );
                SqlParameter resultParam = new SqlParameter( "@result", SqlDbType.VarChar, 8000 );
                resultParam.Direction = ParameterDirection.Output;
                storedProcCall.Parameters.Add( resultParam  );
                storedProcCall.ExecuteNonQuery();
                WL( String.Format( "   The stored procedure returns: {0}", resultParam.Value ) );
                
        
                WL( "Update unexisting UID 100" );
                // @ is required into the parameter name BECAUSE it is identified with @
                // in the store procedure signature
                storedProcCall.Parameters["@uid"].Value = 100; 
                storedProcCall.Parameters["@newname"].Value = "Stupid User";
                storedProcCall.ExecuteNonQuery();
                WL( String.Format( "   The stored procedure returns: {0}", resultParam.Value ) );
                    
                
                
                WL( "4) Dataset after stored Proc Call" );
                r = SqlRead.ExecuteReader();
                while( r.Read() ) 
                    WL( String.Format( "UID: {0} - UserName: {1}", r["UID"], r["UsrName"] ) );
                r.Close();
                SqlConnProc.Close();
                SqlConnProc.Dispose(); // Force to release the connection from the Pool
                
            }
            
        
            
            #region Helper methods
            
            ...
            #endregion
        } 

        Les exemples concernant les DataSet et SqlDataAdapter sont disponibles dans l'article "ADO.NET - Aide mémoire - 2/2 : DataSet"

        mercredi 5 janvier 2011

        Bonnes pratiques que tout DBA devrait connaître

        Juste parce que cela ne fait pas de mal de rappeler certaines règles élémentaires, voici un petit traité (en français) des bonnes pratiques du DBA.

        Le DBA ne doit jamais oublier qu'il est le protecteur des informations de l'entreprise.
        C'est un serment qu'il fait lorsqu'il accepte la casquette de DBA.

        1. Installer et mettre à jour SQL Server
        2. Configuration générale du Serveur
        3. Les bases de la sécurité
        4. Propriétés de SQL Server
        5. Configuration de la mémoire
        6. Gestion des fichiers de données et de log (MDF, LDF)
        7. Gestion de Tempdb
        8. Propriétés de la DB
        9. Configuration des Jobs - recommendations générales
        10. Ne pas tronquer les fichier (Don’t Shrink Files)
        11. Les jobs pour reconstruire/reorganiser les indexes (Create Index Rebuilding/Reorganize Job)
        12. Un job pour détecter la corruption de donnée
        13. Définir des alertes pour les erreurs critiques
        14. Implémenter une stratégie Backup/Restore
        15. Créer un "Disaster Recovery Plan"
        16. Tout documenter
        17. Tout tester

        1. Installer et mettre à jour SQL Server
        D'une façon générale, lorsque l'on installe une nouvelle instance de Sql Serveur.
        • Utiliser les drivers matériel les plus récents.
        • Utiliser la version de l'OS la plus récente avec le dernier service pack.
        • Utiliser la version de SqlServeur la plus récente avec le dernier service pack et tous les Hotfix.
        • Tester le tout.
          Une fois stable, être très prudent lorsqu'il faut effectuer des changements.
        Lorsque l'on veut faire une mise à jour d'une instance existante de Sql Serveur.
        • Ne surtout pas faire de mise-à-jour à moins d'avoir une bonne raison de le faire. Si votre instance fonctionne bien, ne prenez pas le risque de la déstabiliser en modifiant les composants logiciels.
        • Par exemple, une mise à jour peut s'envisager lorsque: l'utilisation d'une nouvelle fonctionnalité est nécessaire, que vous avez des problèmes avec l'installation actuelle, que vous avez besoin de faire une mise-à-jour du matériel.
        • Il est toujours plus sûr de faire une mise-à-jour vers un nouveau serveur matériel (ayant un nouvel OS et/ou nouvelle version de sql serveur) que de faire une mise-à-jour directement sur le matériel de production.
          Cela permet de faire efficacement des tests et offre l'opportunité de revenir en arrière en cas de problèmes.
        2. Configuration générale du Serveur
        • Idéalement, les instances de Sql serveur devraient tourner sur un serveur dédicacé (physique ou virtuel) et sans qu'aucun autre logiciel ne fonctionne sur la machine.
        • Eviter l'exécution de multiples instances sur un même serveur (à moins d'avoir une bonne raison de le faire).
          Préférer l'option de la virtualisation à l'exécution de multiples instances.
        • Les services SQL Serveur non utiles doivent être désactivés ou désinstallés.
        • Ne pas exécuter localement d'antivirus ou Anti Spyware sur le serveur SQL (cas il représente un frein énorme qui dégrade fortement les accès matériel).
          Si un tel logiciel est néanmoins obligatoire, pensez à exclure les fichiers MDF, LDF, BAK et TRN.
        3. Les bases de la sécurité
        • Ne jamais donner aux utilisateurs plus de droits/permissions que ce qui est nécessaire pour effectuer leurs tâches. Cela semble simple et logique mais c'est souvent compliqué dans les faits. Cela n'en reste pas moins un point critique.
        • Ne jamais utiliser le compte SA pour quoi que ce soit!
          Lui assigner un mot de passe complexe et le garder sous la main au cas de situation difficile. Privilégier l'utilisation d'un compte de domaine qui est membre du role SysAdmin.
        • Ne pas autoriser les applications à utiliser le compte SA ou role SysAdmin pour accéder aux données.
        • Lorsque cela est possible, utiliser l'authentification Windows pour se connecter à SqlServer. Cela a surtout du sens pour les équipes de développements.
        • Ne jamais donner un accès au rôle SysAdmin aux consultants.... personne n'est à l'abri d'une erreur, surtout dans un environnement qu'il ne connait pas. Il est d'ailleurs courant que les consultant emporte des mots de passes et informations sensibles sur leur cahier de note (raison de plus d'être restrictif).
          Il convient néanmoins de faire une exception pour les contractants qui effectuent des tâches de maintenance.
        • Bloquer sa station ou clôturer sa connexion sql lorsque l'on quitte son poste.
        4. Propriétés de SQL Server
        • Ne jamais modifier les paramètres par défaut d'une instance de Sql Serveur (1*) ... à moins que vous compreniez exactement les implications de ce que vous faite!
          Même documenté, un détail de ce genre peut passer inaperçu lors d'une migration ou d'une remise en production dans des conditions d'urgences.

        1*: Voici quelques exemples de propriétés Sql Serveur: Memoire, processeurs, sécurité, connections, paramètres DB, paramètres avancés, permissions.

        5. Configuration de la mémoire
        • Privilégier des versions 64 bits pour le hardware, l'OS et Sql Serveur.
        • En mémoire 64 bits, activer l'option "Lock pages in memory" et laisser SqlServeur gérer dynamiquement sa mémoire (la version 2008 est très efficace).
        • Pour une version 32 bits de Sql Serveur sur un serveur ayant au moins 4Gb de mémoire, assurez vous que le switch /3GB est actif et que la mémoire AWE est correctement configurée. Un configuration correcte dépend de la quantité de mémoire disponible.
        6. Gestion des fichiers de données et de log
        • Eliminer la fragmentation physique (défragmenter le disque) avant de créer de nouveaux MDF et LDF.
        • Allouer un espace suffisant aux fichiers MDF et LDF nouvellement créés, cela minimisera les événements AutoGrowth.
        • Les fichiers MDF devraient être localisés sur leurs propres disques.
        • Les fichiers LDF devraient être localisés sur leurs propres disques.
        • Les fichiers de backups (BAK et TRN) doivent être localisés sur leurs propres disques physiques (et donc même pas le même SAN que les MDF et LDF)
        Instant File Initialization
        • Activer l'option "Instant File Initialization". Cela permet de créer ou d'agrandir des fichiers MDF plus rapidement. L'espace alloué n'est alors plus remplis avec des 0 avant d'être utilisé.
          NB: les fichier LDF ne sont pas concernés par cette option.
        • Améliorera la performance des opérations: CREATE DATABASE, RESTORE DATABASE, ALTER DATABASE, AutoGrowth.
        • Nécessite au moins SqlServer 2005/2008 et Windows 2003/2008.
        Note:
        Instant file initialization is turned on if the SQL Server (MSSQLSERVER) service account has been granted the SE_MANAGE_VOLUME_NAME permission by adding the account to the Perform Volume Maintenance Tasks security policy.
        Members of the local Windows Administrator group automatically have this right.


        7. Gestion de Tempdb
        • Donner une taille correcte à TempDB, cela évitera les évènement AutoGrowth. Par défaut, la taille de TempDB est de 8Mb ce qui est très peu.
        • Fixer l'AutoGrowth à une taille fixe plutôt que les 10% par défaut. Cela minimisera les événements AutoGrowth car 10% génère beaucoup d'évènements.
        • Placer la TempDB sur sont propre disques car SQL serveur y a souvent recourt pour y stoquer des résultats temporaires.
        • Sur un serveur à forte activité, considérer de séparer la TempDB dans plusieurs fichiers physiques (1*).
          Si possible utiliser des devices sql différents, cela permettra au noyau Sql d'envoyer plusieurs demande de traitement en parallèle (cela stress plus les IO mais augmente également le temps de réponse générale.
        1*: Les fichiers physiques doivent avoir la même taille.
        Le nombre de fichiers répond à la formule Nbre_CPU_Core/4  (jusque Nbre_CPU_Core/2) avec un maximum de 8 fichiers.

        8. Propriétés de la base de données
        • Ne jamais modifier les propriétés d'une base de données à moins d'avoir une excellente raison de le faire.
        • Auto Create Statistics: Actif
        • Auto Update Statistics: Actif
        • Auto Shrink: désactivé
        • Autogrowth: Laisser Actif (habituellement pour éviter les erreurs).
          Filegrowth L'accroissement de la taille des fichiers devrait être géré manuellement. Sinon, utiliser un AutoGrowth de taille fixe pour minimiser les évènements AutoGrowth.
        • Recovery Mode: Full
          Utiliser l'option "full" pour toutes les DB de production ainsi, il est possible de faire un backup du transaction log.
        • Page Verify: Checksum (2005/2008)
        • Compatibility Level: devrait être la version courante de Sql Serveur... a moins qu'il existe un problème de compatibilité.
        9. Configuration des Jobs - recommendations générales
        • Si le serveur n'a pas de Job configuré alors il y a un problème car tous les serveurs ont besoins de jobs.
        • Planifierl'exécution des Jobs en heures creuses (comme cela ils n'interfèrent pas avec la production).
        • Eviter que l'exécution des Jobs se chevauchent.
        • Utiliser les alertes pour être notifier des jobs qui ont échoués.
        • Vérifier l'exécution des jobs journellement (pas de plantage, exécution anormalement longue, ...).
        • Utiliser le "Maintenance Plan Wizard" avec prudence. Une mauvaise utilisation peut facilement créer des jobs qui dégradent les performances du serveur Sql.
        10. Ne pas tronquer les fichier
        Don’t Shrink Files
        • Si la taille des fichiers MDF et LDF sont correctement configurés, alors il n'est pas nécessaire de les tronquer.
        • Ne jamais planifier de job tronquant les fichiers (ou les DB).
        • Si vous devez malgré tout tronquer une base de donnée:
          • Faite le manuellement (ex en utilisant aussi le script sp_force_shrink_log)
          • Reconstruisez les indexes après l'opération.
          • Planifiez cette opération en heure creuse.
        Ne pas tronquer les fichiers offre quelques bénéfices:
        • Elimine le syndrome "Growth and Shrink".
        • Réduit la fragmentation physique des fichiers.
        • Libère les ressources utilisées pour ces opérations très couteuses en I/O.
        11. Les jobs pour reconstruire/reorganiser les indexes
        Create Index Rebuilding/Reorganize Job
        • Les indexes ont besoin d'être reconstruit ou réorganisés régulièrement pour minimiser la fragmentation et réduire les espaces perdu.
        • Reconstruire an index s'il est fortement fragmenté (>30%).
          Sql Serveur Enterprise Edition peut effectuer cette tâche online.
          Cette opération met automatiquement les statistiques à jours (donc plus besoin de le faire)
        • Reorganiser l'index s'il n'est pas fortement fragmenté ( 5% < frag% < 30%).
          Cette opération consomme peu de ressource et peut toujours être conduite online. Cependant il faut cependant faire une mise à jour des statistiques manuellement
        • Reconstruire ou réorganiser uniquement les indexes qui en ont besoins.
          La vue sys.dm_db_index_physical_stats permet d'identifier de tels indexes.
          L'article "Performance - Fragmentation des indexes" présente un script permettant d'ailleurs de faire ce tri.
        Ressources:
        • L'article "Performance - Fragmentation des indexes" présente un script permettant d'évaluer la fragmentation des indexes et de préparer un script a exécuter pour remédier au problème.
        • Index Defrag Script est un script très complet avec plein d'options permettant de faire un traitement très pointu de défragmentation des indexes.

        12. Le job pour détecter la corruption de donnée
        • Idéalement, l'exécution de DBCC CHECKDB devrait être exécuté aussi souvent que possible.
        • Si un problème existe, il est préférable de le détecter aussi tôt que possible pour réduire le risque de perte de donnée.
          Ne pas utiliser l'option DBCC CHECKDB Repair a moins de pleinement en comprendre les implications.
        • Créer un job qui permet exécute la commande suivante (ou similaire)
          DBCC CHECKDB ('DATABASE_NAME') WITH NO_INFOMSGS, ALL_ERRORMSGS;
        • Pour les serveurs de production vraiment chargé ou les grosses DB, il est possible d'utiliser l'option PHYSICAL_ONLY, cela réduit le temps d'exécution.
        13. Définir des alertes pour les erreurs critiques
        • Créer un "Sql Server Event Alert" pour tous les évènements ayant une sévérité supérieure ou égale à 19 (fatale).
        • Que les alertes soient envoyées à la personne adéquate!
          Vous en tant que DBA ou la personne responsable du monitoring journalier.
        • Considérer l'usage d'un outil spécifique si les Alertes sql ne rencontre pas vos besoins.
        14. Implémenter une stratégie Backup/Restore
        • Créer un Job qui effectue un backup journalier des DB de production et un backup du transaction log toutes les heures (ou similaire).
        • Si le Recovery Model est de type Bulk ou Full, vous devez impérativement faire des backups du transaction log (sinon ce dernier atteindra assez vite une taille incontrôlable).
        • Utiliser la commande "RESTORE WITH VERIFYONLY" pour vérifier l'intégrité du backup. Attention, cela ne garanti pas de bons backups.
        • Tester périodiquement les backups.
          Faire un restore périodique des backups pour s'assurer qu'ils sont bien utilisable.
        • Mettre en place une politique de rétention pour les backups.
        • Stocker les backup dans un endroit sûr et hors du site.
        • Ne pas stocker les backups sur le même disque array que les DB , ni sur le même SAN.
        • Penser à utiliser "backup compression" pour gagner du temps ou de l'espace disque... particulièrement utile dans un environnement de production fort sollicité.
        15. Créer un "Disaster Recovery Plan"
        • Il est impératif de créer une document qui résume, étape par étape, mais aussi en détail, comment récupérer votre serveur Sql ou DB en cas de problème.
          Le document doit couvrir tous les scénarios incluant petit et grand problèmes.
        • Il est important de s'entrainer à utiliser le plan afin de s'y familiariser et de pouvoir facilement le mettre en oeuvre.
        • Garder le numéro de téléphone du support produit de Microsoft sous la main.
        • Souvenez-vous que: La plupart des incidents sont mineurs, telle la corruption de DB. Les grands incidents (désastres) se produisent très rarement, voir jamais. Mais dans tous les cas, vous devez être préparé aux deux.
        16. Tout documenter
        • Nous le savons tous, la documentation c'est barbant et rare sont les personne qui apprécient faire de la documentation (sauf peut-être moi?).
          Pourtant la documentation est un élément critique pour un DBA.
          Les éléments suivants doivent être documentés:
          • L'installation et la configuration de chaque instance.
          • L'installation et la configuration de chaque application qui utilise le serveur SQL comme back-end.
          • Les tâches de dépannages. Comme cela, il n'est pas nécessaire de réinventer la roue si un problème se produit encore.
          • Chaque fois qu'un changement (même mineur) est effectuer sur n'importe quelle instance de Sql Serveur... et ce quelque soit la raison.
        • S'assurer que la documentation est facilement disponible et que toute personne susceptible de l'utiliser puisse y avoir accès.
        17. Tout tester
        • Avant de faire un quelconque changement sur un serveur de production, assurez-vous toujours que cette modification ait été préalablement testé dans un environnement de test... sans exception!

        Source:
        Le contenu de cet article est issus de la présentation Best Practices Every SQL Server DBA Must Know.pdf parue sur SqlServeur Central.
        Merci à de Brad M. McGehee pour cet excellent document.

        jeudi 7 octobre 2010

        sp_msForEachDB

        Après l'article "sp_msForEachTable, la perle cachée", voici une présentation de sp_msForEachDB.

        Cette autre stored procedure made-in Microsoft permet d'exécuter une commande pour chacune des DB présentes sur le serveur.
        Bien partique si, par exemple, l'on veut vérifier la taille du transaction log pour chacune des 20 DB.
        Bien qu'il soit possible d'extraire cette information des tables systèmes, sp_msForEachDB m'a rendu ce petit service rapidement et facilement.

        Le script ci-dessous utiliser sp_msForEachDB pour générer un autre script sql qu'il ne reste plus qu'a exécuter.
        L'execution aurait put être faite directement mais une erreur étant si vite arrivée, il faut mieux être prudent et relire le résultat..


        sp_msForEachDB 'print "print ''---- Check SpaceUsed ? ------------------------''" ; print "USE [?]" ; print "GO"; print "sp_helpfile" ; print "GO" ; print "print '' ''" ; print "print '' ''" ; '
        

        RBAR - Row By Agonizing Row

        Le dernier séminaire du CLUG sur SSIS fut vraiment intéressant. Il faut en autre question de RBAR.

        RBAR  identifie un procédé de développement traitent l'information row-by-row, que cela soit au sein de SQL serveur ou dans les applications métier.
        Il est en effet courant de voir des architectes logiciel coder le contrôle des contraintes et la logique de mise à jour dans leurs applications plutôt que de coder ces fonctionnalités dans la base de donnée elle même (c'est soit disant plus facile pour eux).
        Tout comme il est courant de voir des "select * from table" ou équivalent au sein de l'application.
        Si cela est efficace sur des petites bases de données (logiciel en début de vie), cela est ingérable sur les VLDB (very large DB).

        Imaginez donc l'ouverture et le passage en revue d'une table de plusieurs millions de records pour vérifier l'existence d'une Primary Key! Sql serveur fait cela bien mieux que nous.
        Imaginez la localisation de quelques enregistrements pour les effacer et flagger un champs dans une table parent a peine moins grande... encore une opération que Sql Serveur fera bien plus efficacement que du code logiciel.
        Passer un dataset en revue, ou faire un traitement enregistrement par enregistrement (avec, souvent des boucles imbriquées) peut anéantir les performances de votre logiciel et du moteur SQL qui croule sous les opérations. C'est ça le RBAR de la mort qui tue!

        Ne jamais oublier
        • Ne jamais oublier qu'une base de données avec des types bien choisis, des définitions de contraintes sera toujours le meilleur choix (plus rapide).
        • Ne jamais oublier que SQL serveur est le mieux placé et le plus rapide pour gérer les contraintes et les modifications en cascades (stored procedure, triggers).
        • Ne jamais oublier qu'il faut être parcimonieux en sélectionnant ses données.
        • Ne jamais oublier qu'il faut traiter les données par ensemble (set-based update) plutôt que ligne-par-ligne (row-by-row), le rapport de performance sur une VLDB peut être supérieur à 1 pour 1000.
        Lecture sur le sujet RBAR


        L'article RBAR - Row By Agonizing Row (autre lien) de Remy Gregoire fait tès bonne introduction de RBAR et de ses conséquences.
        Il discute de l'implémentation de type RBAR dans les application métier et montre, exemple à l'appuis, comment éviter les traitements de type RBAR dans les triggers.