code it

Martins Tech Blog

DISTINCT-COUNT in LINQ

Heute wollte ich aus einer Liste ermitteln, welche eindeutigen Einträge es gibt und wie häufig diese auftreten.

Im folgenden das Beispiel - es existiert eine Liste mit Einwohnern.

List<Einwohner> einwohnerListe = new List<Einwohner>();
einwohnerListe.Add(new Einwohner("Meier", "Heinz"));
einwohnerListe.Add(new Einwohner("Müller", "Luise"));
einwohnerListe.Add(new Einwohner("Schulze", "Christa"));
einwohnerListe.Add(new Einwohner("Meier", "Christian"));
einwohnerListe.Add(new Einwohner("Maier", "Martin"));
einwohnerListe.Add(new Einwohner("Gärtner", "Fritz"));

Nun möchte ich abfragen, wie viele Personen auf den jeweiligen Nachnamen entfallen. Im SQL würde man das ganze mittels

SELECT [Name], COUNT(*) FROM [Einwohner] GROUP BY [Name]

Für Objektlisten kann man in diesem Fall LINQ verwenden:

var groupResult = from einwohner in einwohnerListe
        group einwohner by einwohner.Name
            into ewGroup
            select new { Name = ewGroup.Key, Count = ewGroup.Count() };

Durch die Ergebnisliste kann man nun einfach mittels foreach interieren:

foreach (var groupItem in groupResult)
{
    MessageBox.Show(string.Format("{0} {1}", groupItem.Name, groupItem.Count));
}

Entitäten in XML-Daten korrekt abfragen

Entschließt man sich, komplexe Datentypen in XML-Form im SQL-Server abzulegen, stolpert man früher oder später über die Tatsache, dass im Datentyp enthaltene Sonderzeichen wie < oder > in Entitäten umgewandelt werden. Nun sollen diese Entitäten bei der Abfrage natürlich wieder in deren korrekte Repräsentation umgewandelt werden. Hier hängt viel davon ab, welche Konvertierung man dazu verwendet.

Im folgenden Beispiel ist im Feld "XmlField" der Tabelle "table" ein XML-Wert gespeichert, der im Tag parent/child den Text "Dieses Beispiel enthält <." enthält.

Die Abfrage

SELECT CAST(XmlField.query('/parent/child/text()') as nvarchar(max)) FROM [table]

liefert das Ergebnis

Dieses Beispiel enthält &lt;.

Als Lösung verwendet man einfache eine andere Abfrage. So liefert die Abfrage 

SELECT XmlField.value('('/parent/child/text())[1]', 'nvarchar(max)') FROM [table]

das korrekte Ergebnis

Dieses Beispiel enthält <.

Technical Summit 2008

Am 20. und 21.11. fand die Technical Summit in Berlin statt. Auch ich war diesmal als Zuhörer mit dabei. Mein Fazit ist eher gemischt.

Besonders interessant fand ich die Vortragsreihen zu den Themen Parallel Extensions und Windows Presentation Foundation, ich hab ein paar Tricks im Zusammenhang mit SQL Server und SharePoint mitgenommen. Von Microsoft etwas gepusht wird derzeit Azure und Windows 7. Das ist im Moment noch nicht so relevant für die tägliche Arbeit, jedoch schonmal interessant zu wissen.

Weniger interessant für mich - weil schon bekannt - waren Themen wie Workflow-Entwicklung im SharePoint oder die Vorschau auf Rosario. 

Split-Funktion für SQL-Server

Im SQL-Server sucht man vergeblich nach einer eingebauten Funktion, die es ermöglicht, Textdaten zu splitten - analog z.B. der string.Split-Funktion in C#. Das mag unter anderem daran liegen, dass es keinen Array-Datentyp gibt. Als Problemlösung dient eine UDF, die eine Tabelle mit den Split-Werten zurückgibt. Für die Implementierung gibt es sehr viele Lösungen im Web. Das folgende Beispiel soll eine mögliche Implementierung aufzeigen, die ich als simpel und funktional genug einschätze, um das Problem zu lösen.

CREATE FUNCTION dbo.fn_Split (@String nvarchar(max), @Delimiter nchar(1))
RETURNS @Results Table (Items nvarchar(max))
AS
BEGIN
DECLARE @Index int
DECLARE @Slice nvarchar(max)

SET @Index = 1
IF @String IS NULL RETURN

    WHILE @Index != 0
    BEGIN
        SELECT @Index = CHARINDEX(@Delimiter, @String)
        IF @Index != 0
            SELECT @Slice = LEFT(@String, @Index - 1)
        ELSE
            SELECT @Slice = @String

        INSERT INTO @Results(Items) VALUES (LTRIM(RTRIM(@Slice)))
        SELECT @String = RIGHT(@String, LEN(@String) - @Index)
        
        IF Len(@String) = 0 BREAK
    END
RETURN
END
GO

Der Aufruf ist dann sehr simpel:

 

Datum aus DateTime extrahieren im SQL-Server

Ich möchte hier ein paar Lösungsansätze aufzeigen. Diese sind nach meiner Präferenz sortiert:

  1. SELECT DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()))
  2. SELECT CAST(FLOOR(CAST(GETDATE() AS FLOAT)) AS DATETIME)
  3. SELECT CONVERT(DATETIME, CONVERT(CHAR(8), GETDATE(), 112), 112)

Es gibt sicher noch einige weitere. Nachteil der meisten Methoden ist entweder eine unsichere Konvertierung um Zeichenfolgen (hier ist die Sprache des SQL-Servers zu berücksichtigen) oder eine lange Ausführungszeit aufgrund eines komplizierten Ausführungsplanes.

Im SQL-Server 2008 wurde der Datentyp DATE eingeführt, der diese umständlichen Berechnungen unnötig macht. Das Statement lautet dann:

SELECT CONVERT(DATE, GETDATE())