code it

Martins Tech Blog

22. Treffen der PASS Regionalgruppe Sachsen

Am 14.08.2009 findet das nächste Treffen der Regionalgruppe Sachsen der Professional Association for SQL Server in Dresden statt. Beginn ist 17:00 wie gewohnt im IT Trainingshaus am Waldschlösschen.
Dieses Mal werde ich etwas zum Thema Einsatz und Nutzen des in SQL Server 2008 neu eingeführten HierarchyId-Datentyps erzählen. Im Anschluss daran wird es wieder eine offene Diskussion geben und ich bin gespannt, welche Ideen, Meinungen und Vorschläge dort zur Sprache kommen.

Alle Tabelleninhalte einer Datenbank löschen

Während der Entwicklung von Anwendungen kommt es gelegentlich vor, dass man die eingegebenen Test- oder Demodaten wieder löschen möchte. Hier stellt sich häufig das Problem, dass referentielle Integrität ein stupides "DELETE FROM ..." für jede Tabelle verhindert. Das Skript sollte also die referentielle Integrität beibehalten, alle Daten aus den Tabellen löschen und Identity-Spalten wieder zurücksetzen.

Während der Entwicklung von Anwendungen kommt es gelegentlich vor, dass man die eingegebenen Test- oder Demodaten wieder löschen möchte. Hier stellt sich häufig das Problem, dass referentielle Integrität ein stupides "DELETE FROM ..." für jede Tabelle verhindert. Das Skript sollte also die referentielle Integrität beibehalten, alle Daten aus den Tabellen löschen und Identity-Spalten wieder zurücksetzen.

Schlüssel für die Lösung ist die Systemprozedur sp_MSForEachTable, mit der man ein SQL-Statement für jede Tabelle einer Datenbank ausführen kann: 

  1. Zunächst wird für alle Tabellen die Prüfung der referentiellen Integrität deaktiviert. 
  2. Danach wird ein DELETE-Statement ausgeführt. Dabei ist es wichtig die Option "Quoted Identifiers" zu deaktivieren, sonst würde es bei der Ausführung zu folgender Meldung kommen:
    DELETE failed because the following SET options have incorrect settings: 'QUOTED_IDENTIFIER'. Verify that SET options are correct for use with indexed views and/or indexes on computed columns and/or filtered indexes and/or query notifications and/or XML data type methods and/or spatial index operations.
  3. Das dritte Statement aktiviert die Prüfung der referentiellen Integrität wieder.
  4. Im Anschluss daran werden die Identity-Spalten noch zurückgesetzt.
EXEC sp_MSForEachTable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'  
GO  

EXEC sp_MSForEachTable '
 SET QUOTED_IDENTIFIER ON  
 DELETE FROM ?  
'  
GO  
  
EXEC sp_MSForEachTable 'ALTER TABLE ? CHECK CONSTRAINT ALL'  
GO  
  
EXEC sp_MSForEachTable '   
IF OBJECTPROPERTY(object_id(''?''), ''TableHasIdentity'') = 1   
DBCC CHECKIDENT (''?'', RESEED, 0)   
'   
GO
 

Mehrere Datensätze mit nur einem INSERT-Statement einfügen

Hier noch ein kleines Feature von SQL Server 2008, das ich unter dem Punkt syntaktischer Zucker ablegen möchte: Mit nur einem INSERT-Statement können mehrere Datensätze eingefügt werden. Dazu werden einfach mehrere Sätze im VALUES-Teil angegeben.

-- create demo table
CREATE TABLE Mitarbeiter(
  Id INT IDENTITY(1,1),
  Vorname NVARCHAR(35),
  Nachname NVARCHAR(35),
  Abteilung  NVARCHAR(50)
)
-- insert statement with multiple value clauses
INSERT INTO Mitarbeiter(Vorname, Nachname, Abteilung) VALUES 
('Meier','Klaus','HR'),
('Schmidt','Dirk','R&D'),
('Schneider','Tobias','R&D')

Auf Systemereignisse reagieren

Was in der Erstellung von Webanwendungen eher uninteressant ist, spielt in der Entwicklung von Windows-Anwendungen schon gelegentlich eine Rolle: Wie bekomme ich mit, ob mein Rechner gesperrt oder heruntergefahren wird?
In einem sehr interessanten Post erklärt Bernd Hengelein, wie man mit Hilfe der SystemEvents-Klasse genau auf diese Ereignisse reagieren kann.

Exam 70-541: Microsoft Windows SharePoint Services 3.0 - Application Development

Nun ist das Jahr schon wieder fast zur Hälfte um. Das ist doch eine optimale Gelegenheit, die erste Zertifizierungsprüfung für dieses Jahr in Angriff zu nehmen. Gesagt - getan. Und das Ergebnis...? "Passed".
Die Prüfung selbst war wie gewohnt im Multiple-Choice-Verfahren aufgebaut und bestand aus 59 Fragen, von denen laut Einleitungstext einige nicht bewertet wurden. Die Fragen kamen aus folgenden Themengebieten:
  • Deploying Windows SharePoint Services and Custom Components
  • Creating Site and Feature Provisioning Components
  • Creating Metadata and Workflow Provisioning Components
  • Developing Windows SharePoint Services Components using the .NET Framework
  • Manipulating Site Content by using the API 
  • Manipulate Site Configuration by using the API
Neu für mich war diesmal, dass es noch zusätzliche Befragungen zu Beginn und zum Ende gab. Vor dem eigentlichen Exam gab es einen Block mit Fragen wo ich eine Selbsteinschätzung abgeben sollte wie gut ich mich in den jeweiligen Themengebieten auskenne. Dabei gab es eine Skala von Newbee bis Expert. Nach dem Exam gab es noch einen Block in dem ich zum Ablauf des Tests und dem Testcenter befragt wurde. Hier gab es solche Fragen wie: "Mussten Sie Ihren Personalausweis vorlegen und unterschreiben bevor Sie mit dem Test beginnen konnten?"

Kick it.....

Auf den Seiten dotnetkicks.com und der deutschen Variante dotnet-kicks.de haben Nutzer die Möglichkeit, Blog-Posts vorzuschlagen, die auch für andere Mitglieder der Community interessant sein könnten. Jan Welker als einer der Projektteilnehmer beschreibt in einer sehr einfachen Anleitung in seinem Blog, wie man einen Link auf dotnet-kicks.de in Blogger-Blogs einfügen kann. Ich hab es gleich mal ausprobiert und bin gespannt, ob und wie oft meine Posts zukünftig "gekickt" werden.

Neuesten Datensatz ermitteln mit LinqToObjects

Um aus einer Auflistung von Objekten mit einem Timestamp das jeweils aktuellsten zu bekommen müssen mehrere Linq-Operatoren verknüpft werden.

Ausgangslage soll folgende Liste von Objekten des Typs WorkItem sein:

List<WorkItem> workitems = new List<WorkItem>();
workitems.Add(new WorkItem() { UserId = 101, Comment = "comment for 2009-06-17", TimeStamp = new DateTime(2009, 06, 17) });
workitems.Add(new WorkItem() { UserId = 101, Comment = "comment for 2009-06-18", TimeStamp = new DateTime(2009, 06, 18) });
workitems.Add(new WorkItem() { UserId = 101, Comment = "comment for 2009-06-19", TimeStamp = new DateTime(2009, 06, 19) });
workitems.Add(new WorkItem() { UserId = 101, Comment = "comment for 2009-06-20", TimeStamp = new DateTime(2009, 06, 20) });
workitems.Add(new WorkItem() { UserId = 102, Comment = "comment for 2009-06-18", TimeStamp = new DateTime(2009, 06, 18) });
workitems.Add(new WorkItem() { UserId = 102, Comment = "comment for 2009-06-19", TimeStamp = new DateTime(2009, 06, 19) });
workitems.Add(new WorkItem() { UserId = 102, Comment = "comment for 2009-06-21", TimeStamp = new DateTime(2009, 06, 21) });

Ziel ist nun, pro UserId das WorkItem mit dem höchsten TimeStamp zu ermitteln. Zunächst muss der höchste TimeStamp pro Benutzer ermittelt werden. Das geht noch recht einfach mit einem grouped max:

from wi in workitems
group wi by wi.UserId into g
select new { UserId = g.Key, TimeStamp = g.Max(w => w.TimeStamp) }

Das Ergebnis enthält nun sozusagen die Schlüsselspalten, um eine Filterung durchzuführen. Diese muss nun mittels Join mit der originalen Liste durchgeführt werden. Problem hierbei ist, dass im equals des join-Operators nur ein Kriterium angegeben werden kann. Der Schlüssel für die Filterung besteht aber aus 2 Kriterien: UserId und TimeStamp. Die Lösung ist, in diesem Fall den Join über einen anonymen Typ zu realisieren:

var results = from workitem in workitems
        join newestitem in
            (from wi in workitems
             group wi by wi.UserId into g
             select new { UserId = g.Key, TimeStamp = g.Max(w => w.TimeStamp) })
        on new { workitem.UserId, workitem.TimeStamp } equals new { newestitem.UserId, newestitem.TimeStamp }
        select workitem;

SqlConnection nutzt Connection Pooling nicht?

Unter Connection Pooling versteht man eine Technik physische Datenbankverbindungen wiederzuverwenden und damit zeitaufwändige Schritte der Erstellung einer Datenbankverbindung nicht mehrfach ausführen zu müssen.

Mein Profiler-Log zeigte nun vor jedem Statement ein Audit Login und nach jedem Statement ein Audit Logout:

Leider brachten auch Veränderungen an der Programmlogik nichts. Ebensowenig Änderungen an den Parametern MinPoolSize oder Pooling des ConnectionStrings.

Bisher war ich davon ausgegangen, dass diese Events nur gefeuert werden, wenn sich physisch vom Server an- und abgemeldet wird.

Aber eine etwas längere Suche zeigte, dass sich hier einiges getan hat seit ich mich das letzte Mal mit diesem Thema intensiver beschäftigt habe: Seit SQL-Server 2005 SP2 werden die Events Audit Login und Audit Logout auch bei poolgesteuerten Logins gefeuert und können damit auch logischer Art sein.

Der Unterschied liegt in der EventSubClass: Für Pool-Events ist diese 2, sonst ist sie 1. Damit auch für Login-Und Logout-Events dieses Spalte mit Eingang in den Trace findet, muss sie zunächst in den Trace-Properties aktiviert werden.

Im Anschluss daran erkennt man im Trace, ob es sich um logische oder physische Events handelt.

TFS-Auswertung mit Hilfe von Excel erstellen über CheckIn-Policy-Overrides

Mit Hilfe von CheckIn-Policies kann sichergestellt werden, dass bestimmte Regeln beim CheckIn eingehalten werden – die bekanntesten Regeln sind sicher Work-Item-Zuordnung, erfolgreicher Build oder Kommentierung. Allerdings besteht hier auch die Möglichkeit, einen Policy Override durchzuführen – heißt diese Policy zu ignorieren.

Für den Projektleiter ist es nun interessant, wer solche Overrides durchgeführt hat – und warum. Denn sicher gibt es immer mal wieder Notwendigkeiten dafür, manchmal siegt aber auch die Faulheit und das geht meist zu Lasten der Qualität.

Christian Binder hat dazu einen schönen Blog-Eintrag geschrieben, der beschreibt, wie man mit Hilfe von Excel-Pivot-Tabellen auf den Cube im Data Warehouse zugreift und so eine Auswertung über Policy-Overrides bekommen kann.