SQLSaturday #762 – Paris 2018 – Préconférences et agenda

Le SQLSaturday 762 Paris 2018 se profile à l’horizon. L’agenda de la journée est à présent disponible.

DgHsZdTXcAAZ6Fq

Mais n’oubliez pas de vous inscrire aux Préconf qui auront lieu de le vendredi 6 juillet. Une journée de formation à prix ultra-compétitif pour vous former aux algorithmes prédictifs avec Paul Peton (@paulpeton), ou bien aborder les nouvelles plateformes de données Big Data avec Fabien Adato (@FabienAD). Pour les DBA, je ne saurais que trop vous conseiller de passer une journée avec David Barbarin (@mikedavem) pour appréhender entre autres l’installation de SQL Server sur Linux et Docker.

 

Pour vous inscrire, cela se passe ici : http://www.sqlsaturday.com/762/EventHome.aspx 

Happy #SQLSatParis 2018

Publicités
Publié dans Evènements | Tagué | Laisser un commentaire

SQLSaturday #758–Montréal 2018-Les bases du dépannage SQL server–Les slides

Voici un petit lien pour télécharger les slides de ma session sur les bases du dépannage dans SQL Server.

Encore une fois, merveilleusement accueillis par nos amis Québécois, le SQLSaturday a tenu ses promesses avec une belle affluence et des sessions qui méritaient le détour.

See you next year

2018-06-02 18.13.19

Publié dans Evènements | Tagué | Laisser un commentaire

SQLSaturday #758–Montréal 2018-Les bases du dépannage SQL server

Pour la seconde année consécutive j’ai la chance d’avoir été sélectionné pour animer une session lors du SQLSaturday Montréal.

image

Pour ceux qui souhaiteraient passer un Week-End dans une ville formidable et passer du bon temps avec nos cousins Francophones, voilà donc une belle occasion de mêler  travail et détente.

Pour ma part, je présenterai une session sur les bases du dépannage SQL Server. Quelques anciennes figures connues du monde SQL Server en France seront présentes car ont décidée de franchir l’atlantique. Si vous voulez revoir Franc Mercier et Nicolas Soukoff c’est encore une bonne occasion de venir à Montréal

Le planning des sessions est disponible ici.

Pensez à vous enregistrer !

See you there …

Publié dans Evènements | Tagué | Laisser un commentaire

SQLSaturday #735–Helsinki 2018–Les slides et démos

The SQLSaturday #735 took place in Helsinki last week-end. Great city and amazing people there.

2018-05-12 19.09.42
2018-05-13 11.25.402018-05-13 13.13.242018-05-11 16.46.512018-05-13 12.04.522018-05-13 11.27.07

For all of you who attended my session about SQL Server inside a Docker container, the PowerPoint Slides and the demo files can be downloaded on my OneDrive.

Enjoy

Publié dans Docker, Evènements, SQL Server | Tagué | Laisser un commentaire

SQL Server 2016 SP2

Autant la sortie du Service Pack 1 de SQL Server 2016 avait constitué une petite “révolution”, grâce notamment à l’uniformisation de la surface de programmation dans SQL Server. Cela permet en effet de bénéficier dans toutes les éditions de fonctionnalités comme les tables partitionnées, le columnstore index, le moteur InMemory Hekaton, la compression des données, le Database Snapshot (qui permet au passage en édition standard d’avoir un accès en lecture seule à une base en Database Mirroring / Groupe de disponibilité basique) , le Dynamic Data Masking et el Row Level Security, mais encore Polybase et bien d’autres …

Finalement, la différence entre les éditions, à peu de choses près, réside dans l’accès aux ressources mémoire et CPU et dans les fonctionnalités liées à la haute disponibilité.

Le Service Pack 2 pour SQL Server 2016 est paru il y a quelques jours sans pour autant faire grand bruit. Certes pas de grande révolution cette fois ci mais quelques points qui méritent vraiment notre attention.

Si vous êtes fan de Troubleshooting comme moi, la DMV sys.dm_exec_query_stats se voir ajouter des informations relatives à la lecture de données dans mes segments d’un index columnstore. De même, des colonnes supplémentaires relatives à l’écriture de données en TempDB sont aussi ajoutées à cette DMV (tout comme à sys.dm_exec_procedure_stats, sys.dm_exec_trigger_stats) et aux xEvent sql_statement_completed, sp_statement_completed, and sql_batch_completed. A noter aussi la création de la DMV sys.dm_db_log_info  (déjà présente dans SQL Server 2017) en remplacement du bon vieux DBCC LOGINFO. Egalement présente dans SQL Server 2017, la DMV sys.dm_tran_version_store_space_usage est disponible dans le SP2 de SQL Server 2016.

Un certain nombre d’améliorations de performances sont à noter, sur les tables partitionnées, le cleanup de la base distribution et du change tracking. Il en va de même pour le backup sur les instances disposant de beaucoup de mémoire, le support de MAXDOP pour la création/modification des statistiques d’index.

Autre point notable, le fait de pouvoir spécifier un FileGroup autre que Default sur une opération de type SELECT INTO.

Vous l’aurez compris, la liste est longue.

Mais ce que je retiens en premier lieu et le support complet de MSTDC pour les bases faisant partie d’un groupe de disponibilité. Le support n’était que partiel avec SQL Server 2016 (et SP1), voilà donc tous les scénario accessibles, comme c’est le cas avec SQL Server 2017.

Je ne saurais que trop vous conseiller de télécharger et installer ce Service Pack sorti le 24 avril.

Notez au passage que, concernant SQL Server 2017 et les futures version, Microsoft a modifié la politique de mise à jour. Il n’y aura pas de service pack. Seulement des CU (Cumulative Update) dont la fréquence de publication est mensuelle pour la première année de vie du produit et tous les trimestres ensuite.

Happy SQL Server 2016 SP2

Publié dans SQL Server | Tagué , | Laisser un commentaire

Amazon EC2 : SQL Server sur AMI Ubuntu et Amazon Linux 2

La sortie de SQL Server 2017 a clairement marqué un tournant dans la vie de cette base de données. En effet, SQL Server est officiellement supporté sur Linux !

image

Rien de nouveau me direz vous. En effet, cela date un peu, et il y a fort à parier que la majeure partie d’entre vous ne découvre rien.

Pour ceux qui suivent l’actualité, il ne vous aura pas échappé que de temps à autres des offres conjointes Microsoft / [Red Hat | Suse] paraissent pour vous accompagner au changement à moindre frais, typiquement si vous souhaitez migrer depuis Oracle.

image

Reste ensuite à faire un choix d’architecture / infrastructure. Garder SQL Server OnPrem ou bien opter pour une architecture hybride ou encore franchir le pas et migrer vers un provider Cloud.

Bien sur Microsoft Azure est en première ligne, mais Amazon AWS est extrêmement actif sur le sujet.

Preuve en est, Amazon a annoncé il y a quelques temps le support de SQL Server sur leur “propre” distribution Linux : Amazon Linux 2.

image

Microsoft quand à lui supporte SQL Server sous les environnement RHEL, SLES, Ubuntu et Docker:

image

Mais cela ne veut pas dire que SQL Server ne fonctionne pas sur CentOS ou Debian. Faites le test Sourire

Enjoy

Publié dans Amazon EC2, Linux, SQL Server | Tagué | Laisser un commentaire

Script PowerShell-Inventaire des instances SQL Server

Il existe une besoin récurrent d’inventorier les instances SQL sur le réseau. Ne serait-ce que pour valider la conformité au niveau des licences acquises, en fonction des éditions, du nombre de cœurs, etc …

Mais on peut aussi avoir un simple besoin de lister les bases de différentes instances, de valider les modèles de récupération ou d’autres paramètres fondamentaux comme Page_Verify, Auto_Close et Auto_Shrink, mais encore la taille ou même la nombre de connexions et la date de dernière activité recensée …

Pour poursuivre la série de petits scripts PowerShell, je vous propose ce petit bout de code permettant d’inventorier les instances et les bases … Notez que l’on peut faire des exclusions mais également ajouter des instances non “découvrables” (pour cause de Firewall, WAN, etc..) au travers d’un fichier texte.


[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null

clear-host 

#$DiscoveredInstanceList = sqlcmd -L
# or
$DiscoveredInstanceTable = [System.Data.Sql.SqlDataSourceEnumerator]::Instance.GetDataSources()
$DiscoveredInstanceList = @()
ForEach ($InstanceName in $DiscoveredInstanceTable) {
    $s = $InstanceName.ServerName
    if (-not ([string]::IsNullOrEmpty($InstanceName.Instancename))) {
        $s += "\" + $InstanceName.Instancename
    }
    $DiscoveredInstanceList += $s
}
# or
# $DiscoveredInstanceList = "srv1","srv2"

try {
    $AdditionalInstancesListFile = Get-Content "C:\temp\AdditionalServers.txt" 
    $DiscoveredInstanceList += $AdditionalInstancesListFile
}
catch {}

$ExcludedHostList = "devsql2017","(local)"

[int]$global:TotalDatabaseStorage = 0
[int]$global:TotalDatabaseCount = 0

$ListInstances = @()
$ListDatabases = @()
$InstanceErrorList = @()
$DisplayInstanceErrorList = $false

$Debug = $false

$CheckDatabaseDetails = $true 
$CheckLastUserAccess = $true
$CheckSystemDatabases = $true 

$OutGridView = $true
$ExportCSV = $true
$ExportCSVFile = "c:\temp\SQLInstancesInventory.csv"

ForEach ($InstanceName in $DiscoveredInstanceList) {
    $InstanceName = $InstanceName.trim()
    if ($InstanceName -eq "") {continue}
    if ($InstanceName -eq "Servers:") {continue}

    # Check excluded instances
    if ($ExcludedHostList -contains $InstanceName) {
        if ($Debug) {
            Write-Host $InstanceName " excluded" -ForegroundColor yellow
        }
        continue
    }

    $Server = New-Object -TypeName  Microsoft.SQLServer.Management.Smo.Server($InstanceName)
    $Server.ConnectionContext.ConnectTimeout = 1

    if (!($Server.ComputerNamePhysicalNetBIOS)) {
        $InstanceErrorList +=  "Error connecting $InstanceName"
        continue
    }
    else {

        $Databases = $server.Databases | Where-Object Status -eq "normal" | sort-object ID

        Write-Host $InstanceName "-"$Server.Edition "-" $Server.VersionString "(" $Server.ProductLevel ") -" $Server.collation `

        $InstanceStorage = 0
        $DatabaseCount = 0
        foreach ($Database in $Databases) {
             try {
                 If (($CheckSystemDatabases) -or ($Database.iD -gt 4)) {
                    $InstanceStorage += $Database.size
                    $DatabaseCount += 1
                    if ($CheckDatabaseDetails) {
                        if ($debug) {
                            Write-Host "  " $Database.Name "- Owner" $Database.Owner "- RecoveryModel" $Database.RecoveryModel "- Size" $Database.Size.ToString("N") "MB"
                        }

                        if ($CheckLastUserAccess) {
                            $tSQL = "SELECT database_id , 
	                                       CASE WHEN max(last_user_seek) > max(last_user_scan) THEN max(last_user_seek)
	                                            ELSE max(last_user_scan)
	                                       END AS LastUserRead,
                                           max(last_user_update) as LastUserWrite
                                    FROM sys.dm_db_index_usage_stats
                                    WHERE database_id = " + $Database.ID + "
                                    GROUP BY database_id "

                            $LastUserRead = $Database.ExecuteWithResults($tSQL).Tables[0].LastUserRead
                            $LastUserWrite = $Database.ExecuteWithResults($tSQL).Tables[0].LastUserWrite

                            if (-not ([string]::IsNullOrEmpty($LastUserRead))) {$LastUserRead = $LastUserRead.ToString("yyyy-MM-dd HH:mm:ss")}
                            if (-not ([string]::IsNullOrEmpty($LastUserWrite))) {$LastUserWrite = $LastUserWrite.ToString("yyyy-MM-dd HH:mm:ss")}

                        }
                        else {
                            $LastUserRead = ""
                            $LastUserWrite = ""
                        }

                        $LastKnownGood = $($Database.ExecuteWithResults("DBCC DBINFO() WITH TABLERESULTS").Tables[0] | Where-Object {$_.Field -eq "dbi_dbccLastKnownGood"} | Select-Object -First 1).value

                        $ListDatabases += New-Object PSObject -Property @{InstanceName=$Server.name;`
                                                                          VersionMajor=$Server.VersionMajor;`
                                                                          DatabaseName=$Database.Name;`
                                                                          CompatibilityLevel=$Database.CompatibilityLevel.ToString().replace("Version","");`
                                                                          RecoveryModel=$Database.RecoveryModel;`
                                                                          Size=$Database.Size.ToString("N");`
                                                                          Owner=$Database.Owner;`
                                                                          Collation=$Database.collation;`
                                                                          AutoClose=$Database.AutoClose;` 
                                                                          AutoShrink=$Database.AutoShrink;`
                                                                          IsReadCommittedSnapshotOn=$Database.IsReadCommittedSnapshotOn;`
                                                                          PageVerify=$Database.PageVerify;`
                                                                          ActiveConnections=$Database.ActiveConnections;`
                                                                          CreateDate=$database.CreateDate.ToString("yyyy-MM-dd HH:mm:ss");`
                                                                          LastFullBackupDate=$database.LastBackupDate.ToString("yyyy-MM-dd HH:mm:ss");`
                                                                          LastLogBackupDate=$database.LastLogBackupDate.ToString("yyyy-MM-dd HH:mm:ss");`
                                                                          LastKnownGood=$LastKnownGood;`
                                                                          LastUserRead=$LastUserRead;`
                                                                          LastUserWrite=$LastUserWrite;`
                                                                          }
                    }
                 }
            }
            catch {
                Write-host -ForegroundColor Red $_.Exception.Message
            }
        }
        $global:TotalDatabaseStorage += $InstanceStorage
        $global:TotalDatabaseCount += $DatabaseCount

        if ($Debug) {

            Write-Host $InstanceName ": " $DatabaseCount " Databases ("$InstanceStorage.ToString("N") "MB )" 
        }

        $TFList = $Server.EnumActiveGlobalTraceFlags() | Where-Object Global -EQ 1 | Select-Object TraceFlag
        if (-not ([string]::IsNullOrEmpty($TFList))) {
            $TraceFlags = [string]::Join(",",$TFList.TraceFlag)
        }
        else {$TraceFlags = ""} 

        $ListInstances += New-Object PSObject -Property @{NetName=$Server.NetName;`
                                                         InstanceName=$Server.name;`
                                                         Edition=$Server.Edition;`
                                                         VersionMajor=$Server.VersionMajor;`
                                                         Version=$Server.VersionString;`
                                                         ProductLevel=$Server.ProductLevel;`
                                                         Collation=$Server.collation;`
                                                         Processors=$server.Processors;` 
                                                         PhysicalMemory=$Server.PhysicalMemory;`
                                                         MaxServerMemory=$Server.Configuration.MaxServerMemory.RunValue;`
                                                         DatabaseCount=$DatabaseCount;`
                                                         TotalSizeMB=$InstanceStorage.ToString("N");`
                                                         ServiceAccount=$Server.ServiceAccount;`
                                                         LoginMode=$Server.LoginMode;`
                                                         DatabaseEngineType=$Server.DatabaseEngineType;`
                                                         ActiveSessions=$server.EnumProcesses($false).Rows.Count;`
                                                         TraceFlags=$TraceFlags;`
                                                         }
    }

}

if ($OutGridView) {
    $ListInstances | Sort-Object InstanceName | Select-Object NetName, InstanceName,Edition,VersionMajor,Version,ProductLevel,`
                                                             Collation,Processors,PhysicalMemory,MaxServerMemory,DatabaseCount,`
                                                             TotalSizeMB,ServiceAccount,LoginMode,DatabaseEngineType,ActiveSessions,TraceFlags |   `
                                                Out-GridView

    if ($CheckDatabaseDetails) {
        $ListDatabases | Sort-Object InstanceName,DatabaseName | Select-Object InstanceName,VersionMajor,DatabaseName,CompatibilityLevel,`
                                                                               ActiveConnections,RecoveryModel,Collation,AutoClose,AutoShrink,`
                                                                               IsReadCommittedSnapshotOn,PageVerify,Size,Owner,CreateDate,`
                                                                               LastFullBackupDate,LastLogBackupDate,LastKnownGood,LastUserRead,LastUserWrite | `
                                                                 Out-GridView
    }
}

if ($ExportCSV) {

    $ListInstances | Sort-Object InstanceName | Select-Object NetName, InstanceName,Edition,VersionMajor,Version,ProductLevel,`
                                                             Collation,Processors,PhysicalMemory,MaxServerMemory,DatabaseCount,`
                                                             TotalSizeMB,ServiceAccount,LoginMode,DatabaseEngineType,ActiveSessions,TraceFlags |   `
                                                Export-Csv $ExportCSVFile -NoTypeInformation  -Force -Delimiter ";"

    if ($CheckDatabaseDetails) {
        $ListDatabases | Sort-Object InstanceName,DatabaseName | Select-Object InstanceName,VersionMajor,DatabaseName,CompatibilityLevel,`
                                                                               ActiveConnections,RecoveryModel,Collation,AutoClose,AutoShrink,`
                                                                               IsReadCommittedSnapshotOn,PageVerify,Size,Owner,CreateDate,`
                                                                               LastFullBackupDate,LastLogBackupDate,LastKnownGood,LastUserRead,LastUserWrite | `
                                                                 Export-Csv $ExportCSVFile -NoTypeInformation -Force -Delimiter ";"
    }
}

# Display grand total 
if ($global:TotalDatabaseCount -gt 0) {
    write-host ""
    write-host "Grand Total :"
    Write-Host $global:TotalDatabaseCount " Databases ("$global:TotalDatabaseStorage.ToString("N") "MB )" 
}

if ($DisplayInstanceErrorList) {
    write-host ""
    write-host "Errors :"
    $InstanceErrorList
}

Bien entendu je vous encourage à modifier ce bout de code pour qu’il s’adapte à vos attentes … Merci cependant de conserver les crédits …

Happy PowerShell

Publié dans PowerShell, SQL Server | Tagué | 1 commentaire

Erreurs 41017, 41160 et 41152 lors de la création d’un groupe de disponibilité

Il y a quelques semaines, je travaillais sur des groupes de disponibilités SQL Server, comme souvent me direz vous.
Malgré un grand nombre d’install chez divers clients, et encore plus de démos, j’ai eu affaire à une erreur jamais vue auparavant :

SNAGHTML50aa99b

Pour résumer, impossible de créer l’AG. Le cluster était monté, il était possible de créer une groupe de ressources et de le faire basculer les 2 nouds en question. Mon profil avait suffisamment de permissions et malgré tout en retour, les erreurs SQL 41017, 41160 et 41152.

En fouillant dans la log du Cluster (Get-Clusterlog), des erreurs bien plus parlantes sont apparues.

SNAGHTML514c8fa

DLL is not present !!!!

En fait, lorsque l’on veut mettre en place de la haute disponibilité au travers d’un WSFC, celui ci doit trouver un “moyen” de communiquer avec l’applicatif en HA. Donc pour tout rôle “clusterisable” on doit trouver les types de ressource correspondants, la fameuse DLL manquante : hadrres.dll.

Effectivement, point de SQL Server dans les propriétés du cluster :

SNAGHTML54f4f37

J’avoue ne pas avoir totalement le fin mot de l’histoire, pourquoi la DLL ne s’est pas correctement enregistrée …

J’aurais pu ajouter manuellement un type de ressource  mais J’ai opté pour une méthode assez radicale. J’ai désactivé la fonctionnalité Haute Disponibilité sur les services SQL, puis supprimé la feature cluster de Windows. Un reboot plus tard, l’ajout de la fonctionnalité et la réactivation de HADRON effectuées, les types de ressources sont bien présents.

SNAGHTML54d1350

SNAGHTML5539eda

Happy troubleshooting AlwaysON AGs ….

Publié dans SQL Server | Tagué , | Laisser un commentaire

Get-ChildItem ‘SQLSERVER:\SQLRegistration’ -Recurse

Un petit billet, rapide, pour se faire un peu peur avant le Week-End …

SSMS est un outil vraiment complet pour l’administration de SQL Server. Possibilité d’utiliser des dizaines d’assistants, écrire du code T-SQL, débugger, etc … Bref tous les administrateurs ont cet outil à portée de main. Les développeurs également, même si SSDT offre certaines fonctionnalités non présentes dans SSMS comme la comparaison de Schéma ou de Data, le refactoring de code en cas de renommage d’objet par exemple …

Il offre également la possibilité de travailler sur plusieurs instances simultanément, au travers des requêtes multi serveur. On peut enregistrer les différentes instances soit

  • au travers d’un Central Management Server : une instance SQL Server, potentiellement édition Express, est choisie comme repository et seule l’authentification Windows est utilisable
  • au travers des registered servers, dans ce cas la liste est enregistrée en local. Cela offre la possibilité de choisir un mode d’authentification Windows ou bien SQL. Extrêmement pratique. D’autnat plus que l’on peut opter pour la persistance du mot de passe afin de ne pas avoir à ressaisir ce satané mot de passe pour le compte SA qui fait 28 caractères !

Lorsque l’on me pose la question : que vaut il mieux utiliser comme méthode d’authentification, Windows ou bien SQL ? La casquette sécurité me fait répondre Windows, au travers de Kerberos. Mais fonctionnellement, c’est aussi se priver de connexions depuis des environnements non liés à Active Directory, comme des serveurs Linux par exemple. La réponse n’est donc pas si catégorique que cela.

Je vous propose donc de lancer deux lignes de PowerShell sur votre machine, sur laquelle est installée SSMS.

Import-module SQLPS
Get-ChildItem 'SQLSERVER:\SQLRegistration' -Recurse `
        | select displayname, servername, secureconnectionstring| ft -AutoSize

Si vous avez opté pour la sauvegarde de vos mot de passe, mais que vous avez quelques trous de mémoire, profitez en pour les noter, ils sont affichés en clair !

image

Voilà voilà, je pose ça là et je vous laisse donc modifier tout vos mots de passe, ne pas les persister dans SSMS et switcher tant que faire se peux sur une authentification Windows.

happy hacking …

Publié dans PowerShell, SQL Server | Tagué | Laisser un commentaire

AlwaysOn availability Groups – misaligned log IOs

Un petit billet, rapide, concernant la mise en place des groupes de disponibilité sous SQL Server.

Extrêmement simple à mettre en place (une installation classique de SQL Server, l’ajout de la fonctionnalité Windows Server failover Cluster, une case à cocher pour activer AlwaysOn Availability Group) et permettant de disposer à la fois d’une solution de haute disponibilité, de disaster recovery, voire de load balancing pour de la lecture massive sur une ferme de serveur secondaires (depuis SQL Server 2016 et 2017 nativement).

Simple à mettre en place, donc, mais il ne faut pas négliger les prérequis Active Directory et disque. En effet, même si le système est conçu pour fonctionner sur un stockage asymétrique (on n’est pas liée à une baie de disque ou à des disques en attachement local, on pout tout à fait panacher) il convient de respecter quelques règles.

La première assez évidente, bien qu’il soit possible de la transgresser, consiste à utiliser les mêmes lettres de volumes ou points de montage, les mêmes répertoires pour stocker les fichiers de Data et Log.

La seconde est de disposer de disques de … même génération ! En effet, depuis quelques temps, des disques dit 4K remplacent les « vieux » disques 512 octets. Attention, je ne parle pas du formatage au sens Allocation Unit Size NTFS, mais bien du pan physique du nombre d’octets par secteurs.

Imaginez donc mette en place un AG sur 2 réplicas, chacun sur une baie de disque spécifique. Tout fonctionne parfaitement, jusqu’au jour où on ajoute un tiroir dans une des baies et que l’on déplace un volume (opération extrêmement simple dans le cadre de la virtualisation) sur une LUN hébergées sur ce nouveau tiroir.

Et puis, le journal de transaction sur le serveur principal commence à grossir, et malgré les backup log, ne se vide pas …
Et le journal de transaction commence à présenter des messages mentionnant des IO mal alignés … Et un serveur secondaire qui n’est plus synchrone …
Hum hum …

image

Sur le serveur principal on dispose de disques 512 octets

clip_image002[5]

Alors que le serveur secondaire dispose lui de disques 4K.

clip_image002

Deux possibilités pour résoudre le problème :

  • Stopper l’instance sur le serveur secondaire, présenter un nouveau volume sur une LUN 512 octets, utiliser une commande du style ROBOCOPY /SEC pour recopier le contenu du volume de log et ensuite intervertir les lettre de volumes et relancer le service SQL.
  • La seconde possibilité consiste à utiliser le Trace Flag 1800 qui permet d’éliminer le problème. Une fois le service SQL redémarré, les erreurs disparaissent.

image

Well done.
Happy troubleshooting

Publié dans SQL Server | Tagué , | 3 commentaires