SQL Server & ASP .NET Blog

Interessantes und Wissenswertes

SSIS - UNION ALL – Update Metadata

Die Union Transformation in einem SSIS-Datenfluss (SQL Server 2005 und SQL Server 2008) erkennt automatisch, wenn sich die Metadaten des Datenflusses ändern. Nur leider gibt es keinen Weg diese wie bei allen anderen Elementen automatisch mit der Datenquelle zu synchronisieren. Siehe [hier]

Hier stelle ich einen kleinen Workaround vor:

Folgendes Beispiel: Aus zwei Datenquellen sollen Daten zusammengeführt werden. Im Beispiel sind das einfach zwei Select-Statements auf eine Datenbank. Dies funktioniert so lang, wie sich die Tabellenstruktur nicht ändert. Wird beispielsweise eine VARCHAR-Spalte in der Länge geändert gibt das folgendes Bild:

union-all-1

Bei Öffnen der OLE-DB Source wird erkannt, dass sich die Tabelle geändert hat und mit Bestätigung werden die Metadaten auch aktualisiert, doch dann passiert folgendes:

union-all-2

…und bei Ausführung des Paketes gibt es die Fehlermeldung (Anmerkung: Ich habe die Spalte Strasse von VARCHAR(500) auf VARCHAR(600) geändert):

union-all-3

Ein Workaround wäre jetzt das UNION ALL zu löschen und neu zu erstellen, nur “meckern” dann alle nachfolgenden Datenflusselemente. =Schlecht!

Viel besser in den Griff bekommt man das Problem, indem man die Metadaten einfach von Hand ändert. Also Rechtsklick auf das Paket und “View Code” - das zugrundeliegende XML-File öffnet sich. Danach per Textsuche nach dem Union all Element (in meinem Fall heißt das einfach Union All) suchen. Dort im XML File folgen sämtliche Input-Spalten aus allen Elementen davor (im Tag <inputs>). Danach kommt der interessante Teil, die <outputs>.

<outputColumn id="175" name="Strasse" description="" lineageId="175" precision="0" scale="0" length="500" dataType="str" codePage="1252" sortKeyPosition="0" comparisonFlags="0" specialFlags="0" errorOrTruncationOperation="" errorRowDisposition="NotUsed" truncationRowDisposition="NotUsed" externalMetadataColumnId="0" mappedColumnId="0"/>

Hier am Beispiel meine “Strasse” Spalte. Dort steht bei length noch der falsche alte Wert. Diesen kann man einfach durch den richtigen (hier 600) austauschen, speichern, das Paket wieder öffnen und voilá, das Paket funktioniert wieder. Auch alle nachfolgenden Elemente bekommen diese Änderung mit.

Distanz zwischen zwei GPS-Koordinaten per SQL berechnen

Um die Distanz zwischen zwei GPS Koordinaten in km zu berechnen kann folgende Funktion verwendet werden.

Hier im Beispiel wird eine Tabelle “Points” mit den Koordinaten in den Feldern “Latitude” und “Longitude” benötigt.

CREATE FUNCTION [dbo].[fCalcDistanceBetweenGPSCoordinates](@Point1ID INT, @Point2ID INT)
RETURNS FLOAT
AS
BEGIN
DECLARE @lat1 FLOAT
DECLARE @lon1 FLOAT
SELECT @lat1 = Latitude, @lon1 = Longitude
FROM dbo.tPoints
WHERE Point1 = @Point1ID
DECLARE @lat2 FLOAT
DECLARE @lon2 FLOAT
SELECT @lat2 = Latitude, @lon2 = Longitude
FROM dbo.tPoints
WHERE PointID = @Point2ID
DECLARE @dLat FLOAT
SET @dLat = (@lat1-@lat2) * PI() /180
DECLARE @dLon FLOAT
SET @dLon = (@lon1-@lon2) * PI() / 180
DECLARE @a FLOAT
SET @a = SIN(@dLat/2) * SIN(@dlat/2) + 
COS(@lat1 *PI()/180) * COS(@lat2*PI()/180) *
SIN(@dlon/2) * SIN(@dlon/2);
DECLARE @c FLOAT
SET @c = 2 * ATN2(SQRT(@a), SQRT(1-@a))
DECLARE @d FLOAT
SET @d = 6371 * @c 
RETURN @d
END

Die Berechnung habe ich mir natürlich nicht selber ausgedacht sondern von [hier]

Wer sich mit der Theorie auseinandersetzen möchte, findet [hier] einen passenden Artikel.

SQL Server Database Copy Tool auf Japanisch

Hmm…interessanterweise ist diese Seite hier http://www.atmarkit.co.jp/fdotnet/dotnettips/859dbcopytool/dbcopytool.html die Seite, die die meisten Visits auf der Seite vom SQL Server Database Copy Tool generiert. Würde gern mal wissen, was da genau steht, kann aber leider kein Japanisch :-( und einen Übersetzer finde ich auch nicht.

Google kann das Ganze natürlich übersetzen :-) siehe hier http://translate.google.com/translate?prev=hp&hl=de&js=y&u=http%3A%2F%2Fwww.atmarkit.co.jp%2Ffdotnet%2Fdotnettips%2F859dbcopytool%2Fdbcopytool.html&sl=ja&tl=de&history_state0=

www.Outlet-Map.de

Mit Google Maps kann man tolle Sachen machen. In der letzten Zeit habe ich ein wenig damit experimentiert und als Testanwendung ist die Webseite:

http://www.outlet-map.de

herausgekommen.

Auf dieser Seite soll ein Verzeichnis von Outlets und Fabrikverkäufen in Deutschland entstehen. Zentrales Element der Seite ist eben genau die Google Map. Momentan fehlt es natürlich noch an Adressen von Outlets… :-)

Hier ein Screenshot der Seite.

outlet-map

Also wer ein Outlet oder einen Fabrikverkauf kennt, dann immer her damit und auf der Seite eintragen.

Es geht bald weiter

Keine Sorge. Hier geht’s bald weiter. Bin momentan dabei das Control aus diesem Post in einer kleinen Seite zu verbasteln, daher keine Zeit hier etwas neues zu schreiben.

ASP .NET AJAX ohne UpdatePanel

Scott Gu beschreibt (oder beschrieb – immerhin ist der Post von 2006) eine sehr interessante Technik, um partielle Seiten-Updates ohne ein Update-Panel durchzuführen.

Im Grunde geht es darum, dass bei einem Klick auf ein Control (oder auf einen Link) der Server im Hintergrund den Inhalt eines UserControls rendert und dann per JavaScript zur Seite zurück liefert.

Wer mehr erfahren möchte => [Klickt hier]

Reimers Google Maps .NET Control

Ich spiele gerade ein wenig mit Google Maps herum – genauer gesagt mit dem Google Maps .NET Control von Reimers. Mit diesem Control ist es sehr einfach eine Google Map auf die eigene Seite zu bringen.

Kleines Beispiel:

<Reimers:GoogleMap ID="myGMap" width="100%" Height="100%" runat="server">
</Reimers:GoogleMap>

…dieses Markup genügt um eine Map auf der Seite anzuzeigen. Einen Center-Punkt und Zoom im Markup festzulegen ist auch kein Problem:

<Reimers:GoogleMap ID="myGMap" width="100%" Height="100%" runat="server" Zoom="7">
<Center Latitude="50" Longitude="10" />
</Reimers:GoogleMap>

Hinzufügen von Markern im Code-Behind funktioniert folgendermaßen. Der Code fügt einen Marker an Position 50, 10 ein. Außerdem wird ein InfoWindow definiert, welches sich beim Klick auf den Marker öffnet und das eingefügte HTML anzeigt.

GoogleMarker gm1 = new GoogleMarker("mrkClick", 50, 10);
gm1.ClientSideHandlers.OnClick = gm1.OpenInfoWindowHTML(myGMap, "<b>Hallo vom SQL-ASP-Blog!!!</b>");
myGMap.Overlays.Add(gm1);

…das sieht dann so aus:

marker

Das Control beherrscht auch eigene Callbacks, so dass man auf bestimmte Events reagieren kann. So können zum Beispiel Marker nur hinzugefügt werden, wenn eine bestimmte Zoomstufe erreicht ist etc. – alles ohne Neu laden der Seite.

Wie gesagt – ich spiele erst einmal ein wenig damit herum. Aufgefallen ist mir bisher, dass es anscheinend keine einfache Möglichkeit gibt das Control und gleichzeitig UpdatePanels auf einer Seite zu verwenden. Finde ich etwas schade…

Trotzdem ist das Control auf jeden Fall einen Blick wert, wenn man mal einfach eine Google Map auf eine Seite bringen möchte.

Zu finden gibt es das Google Maps .NET Control hier: http://www.reimers.dk/. Für den Download ist eine Anmeldung notwendig.

SSMS Addin – QuickFind

Tabellen, Prozeduren, Funktionen in großen Datenbanken suchen kann sehr mühsam sein. Wie hieß die Prozedur nochmal genau? In welcher Datenbank steckt die Tabelle xyz?

Ein kleines SSMS-Adddin kann hier helfen. QuickFind! Dieses kleine Addin durchsucht “on-the-fly” alle Objekte aller Datenbanken und durch einen Doppelklick kann man direkt zum gesuchten springen. Sehr nützlich.

quickfind

Zum Download gibt es das Addin (unter Anderen) als OpenSource auf Codeplex unter: http://ssmsaddins.codeplex.com/

In welcher Datenbank bin ich – DB_NAME()

Teilweise kommt es vor, dass man gleiche Views für verschiedene Datenbanken benötigt, die nur ein wenig voneinander abweichen. Beispielsweise soll in Datenbank 1 eine Nummer immer 4-stellig, in Datenbank 2 immer mit führender 0, also 5-stellig angezeigt werden.

Möchte man solche Views gemeinsam warten, also nur einmal ändern und dann verteilen kann DB_NAME() helfen. DB_NAME() gibt immer den Namen der aktuellen Datenbank zurück.

CREATE TABLE myTable
(
number int
)
INSERT INTO myTable VALUES (2542)
INSERT INTO myTable VALUES (8845)
INSERT INTO myTable VALUES (9895)
INSERT INTO myTable VALUES (3213)

Um nun eine View zu erstellen, die für beide Datenbanken passt geht folgendes:

CREATE VIEW [dbo].[myView] AS
SELECT 
CASE 
WHEN DB_NAME() = 'Datenbank1' 
-- vierstellig
THEN RIGHT('0000' + convert(varchar(4),number),4) 
WHEN DB_NAME() = 'Datenbank2'
-- fünfstellig 
THEN RIGHT('00000' + convert(varchar(4),number),5)
ELSE CAST(number as VARCHAR)
END AS number
FROM myTable