Wie aktuell ist noch HTML 5?

HTML 5 ist nicht gleich HTML 5

HTML 5 entstand nicht durch das W3C-Konsortium, sondern war ursprünglich eine Spezifikation von WHATWG, einer Arbeitsgruppe von großen Browserherstellern. Nach einiger Zeit wurde das W3C darauf aufmerksam, wollte sich allerdings nicht der WHATWG anschließen und entwickelte an einer Kopie der Spezifikation weiter. Seitdem gibt es 2 unterschiedliche Versionen oder Auffassungen, die ein gemeinheitliches Verständnis von HTML 5 verhindern. Nach dem Verständnis von WHATWG, soll vom jetzigen Standpunkt eine rollierende, iterative Entwicklung stattfinden, so dass es kein HTML 6 geben wird… das W3C hingegen beruft sich auf Spezifikationsstände, die in einer Versionsnummer kenntlich gemacht werden sollen (wie bisher).

Wenn wir als Web-Entwickler von HTML 5 sprechen, müssen wir immer klar machen, auf welche Spezifikation wir uns beziehen und vor allem: ZU WELCHEM ZEITPUNKT. Momentan gibt es kein einheitliches Verständnis von HTML 5 und die meisten Internetnutzer fragen sich, über welche sinnlosen Dinge wir uns den Kopf zerbrechen, denn ihr Webbrowser „kann doch schon HTML 5″….

Browserunterstützung – Stand: 6. Januar 2011

Auf der Seite http://html5test.com/ lässt sich die Browserkompatibilität mit HTML 5-Seiten testen. Welcher Spezifikation das genügt, müsste man im Detail untersuchen.

  • Internet Explorer 7 ist das Schlusslicht: 21 von 300 Punkten
  • Internet Explorer 9 PPT hat bereits 79 von 300 Punkten
  • der aktuelle Firefox 3.6.13 erhält 139 von 300 Punkten
  • die Beta Version vom 4er Firefox erhält bereits 235 von 300
  • Opera 10.62 bekommt 203 von 300 Punkten
  • Apple Safari 5.0.1 erhält 178 von 300 Punkten
  • Gewinner ist Google Chrome / Chromium 7 mit 262 von 300 möglichen Punkten.

Bei dem beachtlichen Marktanteil vom Internet Explorer (immerhin über 50 Prozent) spielt es keine Rolle welcher Browser der bessere HTML 5 Interpreter ist … Internetseiten sollen barrierefrei und browserunabhängig für jeden Nutzer lesbar bleiben…

Wer unbedingt jetzt schon HTML5 nutzen will…

… kann das HTML 5 Boilerplate einsetzen. Hierbei handelt es sich um einige Bibliotheken, die nach der Einbindung bei älteren Browsern eine Ersetzung von HTML5-Tags in Browser-verständliche Elemente vornimmt. Die funktioniert in einigen Browsern wie dem Firefox aber nur bedingt…

Mein Fazit…

… ist, wenn sich die Herrschaften mal geeinigt haben, was HTML 5 ist, wird das mit Sicherheit eine tolle Sache. Bis dahin sollte man darauf verzichten. Die gerühmten Elemente wie WebGL, die das Erstellen von 3D-Objekten im Browser ermöglichen, sind eigentlich kein Bestandteil der Spezifikation, werden aber mit HTML 5 in Verbindung gebracht.

WebDAV-Laufwerk mit Microsoft Windows Server 2003 und IIS konfigurieren

Aufgabenstellung

Es soll auf ein Dateisystem eines Internetservers zugegriffen werden, um z.B. Internetseiten zu bearbeiten oder z.B. Daten (CSV-Dateien, Excel-Sheets, Access-DBs… ) zu übertragen.

Problem

Aus einem sicherem Firmennetzwerk sollen Dateien komfortabel auf einen entfernten Windows Server kopiert werden. Der Begriff „komfortabel“ bedeutet, das man völlig orts- und relokationstranparent mit diesem Speicherort umgehen kann, es also keine Rolle spielt ob ich von Photoshop aus meine PSD-Dateien lokal habe oder mit einem entfernten Ort arbeite. Das Firmennetzwerk erlaubt über einen Proxy-Server Internetzugriff lediglich auf Port 80, so das man Internetseiten aufrufen darf. Eine FTP-Verbindung mit einem Internet-Server ist nicht möglich, da der FTP-Port 21 geblockt wird.

Auch das Arbeiten mit einer Web2FTP-Lösung oder eines Tunnels über ein FTP-Uploadformular erscheint sehr unbefriedigend, da ich in Photoshop über „Speicher Unter…“ ja kein Webformular auswählen kann, sondern vorerst die Datei zwischenspeichern und hochladen muss.

Vorraussetzungen

Dieser Artikel geht davon aus, das sie den Microsoft Windows Server 2003 oder höher (32- oder 64-Bit) bereits für ein laufendes Netzwerk konfiguriert haben und mindestens IIS 6 sowie den IIS Manager installliert haben. Der IIS sollte durch Zugriff auf die IP-Adresse erreichbar sein.

Ansatz

Das WebDAV-Protokoll erlaubt das Mounten von Dateisystemen über den in Windows integrierten WebDAV-Client. Sollte dieser Dienst deinstalliert sein, oder aus irgendwelchen Gründen nicht funktionieren, bietet das kostenlose Freewartool „Netdrive“ einfachste Einstellmöglichkeiten für Proxyserver und entfernte HTTP-Locations (wie http://meinserver/webdavfreigabe).

Lösung

Zuerst wird IIS konfiguriert

* WebDAV muss eine erlaubte Webdiensterweiterung im IIS-Manager sein

* Unter Webdiensterweiterungen muss für 64-Bit-Systeme ebenfalls das x64 Modul für Webdav eingebunden werden:

C:\Windows\Syswow64\inetsrv\httpext.dll


* HTTP (TCP-Port 80) muss in der Firewall erlaubt sein

Anschließend das Verzeichnis freigegeben

Die Einrichtung erfolgt über einen Ordner im Dateisystem der mit Hilfe der Registerkarte Webfreigabe bei den Eigenschaften des Ordner freigegeben wird.
Wählen Sie einen Alias aus und legen Sie die Webseite fest unterhalb der Webdav-Freigabe sichtbar ist.

Anonymer Zugriff ermöglichen

Soll der Ordner anonym für jeden zugänglich sein, kann man den Internet Guest Account „IUSR_TEMPLATE“ in den Ordneroptionen hinzufügen

Authentifizierter Zugriff (Benutzername/Kennwort)

In den meisten Fällen möchte man eine Standard Authentifizierung haben, wo zumindest Benutzername und Kennwort abgefragt wird, um den WebDAV-Ort vor ungewollten Zugriffen zu schützen.

Zunächst legt man hierfür einen User auf dem Windows-Server an (z.B. Schludi) (Anmerkung: Ich habe ihn in die gleichen Gruppen gesteckt wie IUSR_TEMPLATE, weiß nicht ob das notwendig ist…) . Der User kann am Server über START, Administrative Tools, Computer Management angelegt werden:

Das Passwort, was man dem User gibt, ist gleichzeitig das Passwort für den WebDAV-Zugriff.

Anschließend weißt man in den Ordereigenschaften (hier der User schludi) dem Ordner den Nutzer zu:

Hat man dies getan sollte man die Default-Website im IIS markieren und F5 klicken (aktualisieren). Es erscheint im aufgeklappten „Default-Website“-Knoten ein Zahnrad-Icon mit dem Alias-Namen des Ordners. Hier klicken wir wieder mit der rechten Maustaste und wählen den Menüpunkt Properties/Eigenschaften. Unter dem Reiter „Directory Security“ wird der Haken „Enable anonymous access“ entfernt und die Authentifizierungsmethode auf Basic Authentication gestellt. Die Einstellungen sollten mit dem Apply/Übernehmen-Button gesichert werden.
Ich habe übrigens eine Default-Domain „CT42946\“ eingetragen, damit es nicht nötig wird, den Domainpräfix vor den Usernamen zu schreiben. Ich habe in internationalen Intranets die Erfahrung gemacht, das Server in unterschiedlichen Domainen oftmals den Benutzernamen nicht akzeptieren, wenn man den Default-Domainpräfix nicht angegeben hat.

Zugriffmöglichkeiten mit Windows Methoden

Es gibt nun diverse Möglichkeiten diese WebDAV-Location als Netzwerklaufwerk mit herkömmlichen Windows-Methoden zu mounten, wovon je nach Client-Konfiguration nicht jede funktionieren muss…
Generell sollte der Dienst „WebDAV-Client“ auf dem Client-Rechner unter Services installiert und aktiviert sein, dies kann über die Systemsteuerung/Control Panel unter „Dienste/Services“ vorgenommen werden.

Laufwerksbuchstabe über Kommandozeile/Eingabeaufforderung mounten

Es wird die MS DOS Eingabeaufforderung geöffnet und der folgende Befehl eingegeben:

net use u: http://serveroderip/webdavalias

Ist eine Basic Authentication nach der Vorgehensweise oben eingestellt worden, fragt die Eingabeaufforderung nun User und Kennwort ab. Sollte ein Proxy erforderlich sein, wird der Standard-Proxy der Interneteinstellungen (Systemsteuerung/Internet Explorer) genommen.
Nun sollte Laufwerk U: gemountet sein. Ist dies nicht der Fall empfehle ich testweise einen Zugriff per Netdrive (siehe einige Zeilen weiter unten) auszuprobieren. Probieren Sie auch aus, ihren Domain-Präfix vor den Username DOMAINPRÄFIX\User zu schreiben (siehe auch weiter oben im Text: Empfehlung zur Einstellung des Default-Domain-Präfix).

Netzwerklaufwerk über Explorer zuordnen

Nachdem man den Windows-Explorer geöffnet hat, gibt es die Möglichkeit mit einem Rechtsklick auf das Netzwerk-Icon die Option „Netzwerklaufwerk verbinden“ zu wählen. Hier öffnet sich ein Dialog, der normalerweise einen typischen Windows-Filesharingpfad erwartet, aber unten einen Link „Verbindung mit einer Website herstellen, auf der Sie Dokumente und Bilder speichern können“ hat. Dieser Link ermöglicht mit Bordmitteln von Windows das mounten eines Netzwerklaufwerkes.

Zugriff mit einem Netdrive-Client

Obwohl man ihn erst runterladen muss, finde ich die Nutzung des Netdrive-Clients viel komfortabler und weniger problematisch als die Nutzung der Windows-Bordmittel. Einfach mal nach „netdrive download“ googeln, und man findet irgendeinen Ort (z.B. CHIP-Server) wo er runterladbar ist.

Nach der Installation lässt sich einfach die Verbindung eintragen, der „Advanced“-Button ermöglicht die Angabe eines Proxyservers, über den Verbindung aufgenommen werden kann.

Resumee

WebDAV ist eine gute Methode um bei stabiler Internetverbindung mit entfernten Dateisystemen zu arbeiten, auch wenn der Zugriff nach außen lediglich über Port 80 möglich ist. Hierbei muss der Web-Server nicht umkonfiguriert werden, sondern lediglich ein WebDAV-ALIAS auf der Defaultwebsite hinzugefügt werden.

Spießer-Zensuren in Wikimedias

Faszinierend wieviele freiwillige Autoren bei Wikipedia unentgeltlich ihre Arbeit verrichten. Die Idee einer freien Kollaborationsanwendung / eines „Gedankentempels“ (Zitat von Jimmy Wales, Gründer von Wikipedia) scheint allerdings mit der Diskussion nach Sinnhaftigkeit von Artikeln und deren Existenzberechtigung zu stehen und zu fallen. Nachdem die Administratoren ihre Rechte bekommen haben um Artikel zu löschen, entpuppen sich viele als nervöse, bewaffnete Moralapostel mit dem Finger am Abzug. Das scheint bei einer Enzyklopädie erstmal sinnvoll. Lieber kein Artikel zusätzlich, als ein schlechter Artikel zusätzlich…

Aber wieso wird bei Stupipedia und Uncyclopedia Satire zensiert? Ich saß mit einem Bekannten in der Sylvesternacht am Tisch und trank ein Gläschen Eierlikör. Dieser kam auf die Idee dem Getränk den Namen „Hoden Brandy“ zu geben, was wir zunächst witzig fanden. Die Idee, man könne einen satirischen Artikel darüber verfassen, scheiterte schon nach der Artikelanlage.

Bei Uncyclopedia haben wir uns einen festen User registriert und den Artikel „Hodenbrandy“ angelegt. Nachdem die Sektion „Definition“ verfasst wurde, in der wir lediglich einige Sätze mit Andeutungen auf „Eierlikör“ schrieben, war der Artikel bereits beim zweiten oder dritten Mal speichern verschwunden. Wir dachten uns: „Okay, da dachte wohl jdm. das wär ein Spam-Artikel“… Also versuchten wir ihn nochmals anzulegen, allerdings war der Artikel gesperrt. Wir legten einen zweiten Artikel „Hoden Brandy“ (auseinander mit Leerzeichen) an. Kurz danach wurde unser Benutzer gesperrt.

Anschließend versuchten wir unser Glück auf Stupipedia. Wir kopierten den Text, den wir mit dem Wiki-Markup bereits vorsorglich in Notepad zwischengelagert hatten, hinein. Kurz danach kam der erste Kommentar „grenzwertig“. Durch das Setzen des „In-Use“-Tags erklärten wir, dass wir noch am Arbeiten sind, und den Artikel überdenken. Wir erstellten eine lustige Visio-Grafik mit einem Kaizen-Diagramm über den Produktionsprozess des Brandys… Ca. 10 Minuten später wurde der Artikel gelöscht, da es sich angeblich um einen sexistischen Inhalt handele.

Der Artikel war im vgl. zu anderen Satiren weder sexistisch noch eklig oder hätte es gerechtfertigt unseren Benutzer zu sperren. Es wurde niemand persönlich angegriffen, da auch keine Person
angesprochen wurde. Wenn man sich die Sprüche von Michael Mittermeier oder Mario Barth anhört, vernimmt man gelegentlich tollerierte sexistische Inhalte. Aber die Beschreibung von Eierlikör unter dem Namen „Hoden Brandy“ ist an Harmlosigkeit kaum zu überbieten.

Auf der einen Seite bewundere ich das Engagement der Administratoren, auf der anderen Seite ärgere ich mich über die Spießigkeit und frage mich ob eine längere Tätigkeit auf diesem Gebiet zu Abgestumpftheit führt. Die Interpretation der Richtlinien wird häufig am eigenen Maßstab festgemacht. Was die Erhaltung von Artikeln und den Umgang mit der Arbeit von fremden Personen betrifft, wünschte ich mir hier etwas Professionalität. Auf den Löschen-Button ist schnell geklickt und die Arbeit von Stunden zerstört.

Auf der anderen Seite kann man wirklich über die Sinnhaftigkeit von Satire in Uncyclopedia/Stupipedia streiten 😉 …
Definiert Wikipedia die Satire als „eine Spottdichtung, die mangelhafte Tugend oder gesellschaftliche Missstände anklagt“, sollte sie hier gegen ihr eigenes Rahmenwerk eingesetzt werden.

Hier ein netter Versuch – ein satirischer Artikel über den Vorfall bei den Stupidedia Administratoren:
http://www.stupidedia.org/stupi/Stupidedia_Administrator

… mal schauen wie langer er sich hält, er soll ab 11. Januar gelöscht werden, unten sieht man wie sich die Administratoren aufregen XD …

Baum von mx:AdvancedDataGrid in die Zwischenablage kopieren

Aufgabe

Der Baum eine mx:AdvancedDataGrid mit mx:SummaryRows soll in die Zwischenablage kopiert werden.

Problem

Der DataProvider des mx:AdvancedDataGrid ist eigentlich flach. Der erzeugte Baum/die Hierarchie soll in die Zwischenablage kopiert werden, so dass er in Excel per Copy&Paste mit Einrückungen und Summen eingefügt werden kann.

Lösung

Eine rekursive Funktion wird erstellt, die über den erzeugten Baum iteriert:

// Für das Einrücken in die Zwischenablage
public var anzahlGroupingFelder:Number=0; // Gesamttiefe der Felder
public var aktuelleTiefe:Number=0;		  // Tiefe der aktuellen Rekursion
public var rekursionsString:String='';
public var farbenFuerLevel:Object = new Object();
private function getCompleteADGRekursion(treeData:ArrayCollection):void
{
	var aktuelleGruppeFelder:String='';
		
	for (var i:Number=0; i<treeData.length; i++)
	{
		if(i==0) aktuelleTiefe++;
		var tabs:String='';
		var behindtabs:String='';
		
		for (var l:Number=0; l<=anzahlGroupingFelder-aktuelleTiefe+3; l++) 
		{
			behindtabs+='<td></td>';						
		}
					
				
		var item:Object=treeData[i];
		if(item.children)
		{
			for (var j:Number=1; j<aktuelleTiefe; j++)
			{
				tabs+='<td></td>';						
			}
						
			var properties:String='<td><i><b>'+item.actuallastyear+
			'</b></i></td><td><i><b>'+item.actualyeartillaugust+
			'</b></i></td><td><i><b>'+item.actualyearlinearprojection+
			'</b></i></td><td><i><b>'+item.nextyear+
			'</b></i></td><td><i><b>'+item.nnextyear+
			'</i></td><td><i><b>'+item.nnnextyear+
			'</b></i></td><td><i><b>'+item.nnnnextyear+
			'</b></i></td>';
						
			rekursionsString+='<tr style="background-color:'+
			farbenFuerLevel&#91;aktuelleTiefe&#93;+';">'+tabs+
			'<td>'+item.GroupLabel+behindtabs+'</td>'+
			properties+'</tr>';
			getCompleteADGRekursion(item.children);
		}
		else
		{
			for (var k:Number=0; k<anzahlGroupingFelder; k++)
			{
				tabs+='<td style="background-color:#EFEFEF"></td>';						
			}
						
			var properties2:String='<td>'+item.productcharacteristic+
			'</td><td>'+item.productcharacteristic2+
			'</td><td>'+item.productcharacteristic3+
			'</td><td>'+item.material+
			'</td><td>'+item.actuallastyear+
			'</td><td>'+item.actualyeartillaugust+
			'</td><td>'+item.actualyearlinearprojection+
			'</td><td>'+item.nextyear+
			'</td><td>'+item.nnextyear+
			'</td><td>'+item.nnnextyear+
			'</td><td>'+item.nnnnextyear+'</td>';												
			
			rekursionsString+='<tr>'+tabs+properties2+'</tr>';
		}
					
		// Beim ersten gehts eins tiefer
					
		// Beim letzten eins zurück
		if(i==treeData.length-1) aktuelleTiefe--;
	}
}

Aufruf der rekursiven Funktion:

private function copyToClipboard(dg:AdvancedDataGrid):void
{
// Farben je Interationstiefe;

farbenFuerLevel[1]=’#00B684′;
farbenFuerLevel[2]=’#31C39C‘;
farbenFuerLevel[3]=’#63D3B5′;
farbenFuerLevel[4]=’#CEF3E7′;
farbenFuerLevel[5]=’#BBBBBB‘;
farbenFuerLevel[6]=’#AAAAAA‘;
farbenFuerLevel[7]=’#999999′;
farbenFuerLevel[8]=’#888888′;

var wasIstImDataProviderDebug:Object=dg.dataProvider;
try
{
anzahlGroupingFelder=Number(wasIstImDataProviderDebug.source.
grouping.fields.length);
}
catch(e:Error)
{
anzahlGroupingFelder=0;
}

if(anzahlGroupingFelder>0)
{
rekursionsString=‘‚;
rekursionsString=‘

‚;
rekursionsString=‘

‚;

for each (var order:ReportingOrderVO in reportingOrder)
{
if(order.position!=0)
{
rekursionsString+=‘

‚;
}
}

rekursionsString+=‘

‚;
rekursionsString+=‘

‚;
rekursionsString+=‘

‚;
rekursionsString+=‘

‚;
rekursionsString+=‘

‚;
rekursionsString+=‘

‚;
rekursionsString+=‘

‚,
rekursionsString+=‘

‚;
rekursionsString+=‘

‚;
rekursionsString+=‘

‚;
rekursionsString+=‘

‚;
getCompleteADGRekursion(
ArrayCollection(dg.hierarchicalCollectionView.source
.getRoot())
);
rekursionsString+=‘

‚+order.description+
Detail 1 Detail 2 Detail 3 Detail 4 Actual last year‚;
rekursionsString+=‘
Actual year‘;
rekursionsString+=‘ till august
Actual year linear projection Next year NNext year NNNext year NNNNext year

‚;
System.setClipboard(rekursionsString);
}
else
{
var totalExport:String = new String();
var colList:Array = new Array();
for(var i:int = 0; i < dg.columnCount; i++) { colList.push(dg.columns[i].dataField); totalExport += String(dg.columns[i].headerText) .replace('\r',' ') + "\t"; } totalExport += "\r"; for(var yp:int = 0; yp < reporting.length; yp++) { for(var xp:int = 0; xp < colList.length; xp++) { var insertString:String=reporting .getItemAt(yp)[colList[xp]]; if(!insertString) insertString=''; totalExport += insertString .replace("\t","") + "\t"; } totalExport += "\r"; } System.setClipboard(totalExport); } Alert.show('Data has been copied to clipboard. Now you can paste it to '+ 'Microsoft Excel or another application of your choice.'); } [/actionscript3]

Von Microsoft Excel in den Microsoft SQL Server importieren

Dies ist ein VBA Skript um Dateien von einer Excel-Tabelle in den Microsoft SQL Server zu importieren. Bevor das Skript läuft muss im VBA-Editor unter Extras -> Verweise „Microsoft ActiveX Data Objects“ als Bibliothek ausgewählt werden, damit ADODB.* zur Verfügung steht.

Private Sub CommandButton1_Click()
    ' Die Datenbankverbindung
    Dim Cn As New ADODB.Connection
    Dim Rs As New ADODB.Recordset
    Dim letzteZeile As Integer
    Dim i As Integer
    
    ' Die beiden Felder material, prdha
    Dim materialnr As String
    Dim prdha As String
    
    ' Finde die letzte Zeile
    letzteZeile = Cells.Find("*", [A1], , , xlByRows, xlPrevious).Row + 1
    
    ' Provider OLEDB Provider
    Cn.Provider = "SQLOLEDB.1"
    
    ' Connectionstring
    Cn.ConnectionString = "Password=meinpasswort;" & _
    "Persist Security Info=True;" & _
    "User ID=meinuser;" & _
    "Initial Catalog=meinedatenbank;" & _
    "Data Source=SERERNAMEODERIPADRESSE"
    
    ' Verbindung öffnen
    Cn.Open
            
    For i = 1 To letzteZeile
        materialnr = Tabelle1.Cells(i, 1)
        prdha = Tabelle1.Cells(i, 2)
        Cn.Execute ("INSERT INTO prdha(material, prdha) " & _
                         "VALUES('" + materialnr + "','" + prdha + "')")
    Next i
    
    ' Verbindung schließen
    Cn.Close
    
End Sub

Security Event Log auslesen bei Microsoft Windows Server

Das nachfolgende Konsolenprogramm gibt eine Liste des Security Event Logs beim Windows Server aus. Hier sieht man welche Person zu welchem Zeitpunkt eine Windows-Authentifizierung über eine Windows/ASP.NET-Anwendung am IIS Server getan hat.

namespace APITester
{
class Program
{
static void Main(string[] args)
{
EventLog test = new EventLog();
test.Log = „Security“;

for (int i = 0; i < test.Entries.Count; i++) { try { Console.WriteLine(test.Entries[i].TimeGenerated + " " + test.Entries[i].UserName); } catch (Exception e) { } } Console.ReadLine(); } } }[/javascript]

Zugriff von VBA auf Microsoft SQL Server

Zunächst wird (z.B. in Excel) im VBA-Editor im Menü „Verweise“ der Verweis „Microsoft ActiveX data Object“ hinzugefügt, damit man Zugriff auf die ADO-Klassen bekommt.

Private Sub CommandButton1_Click()
    Dim Cn As New ADODB.Connection
    Dim Rs As New ADODB.Recordset
    
    Cn.Provider = "SQLOLEDB.1"
    
    Cn.ConnectionString = "Password=daspasswort;" & _
    "Persist Security Info=True;" & _
    "User ID=deruser;" & _
    "Initial Catalog=datenbankname;" & _
    "Data Source=rechnername"
    
    Cn.Open
    
    Rs.CursorType = adOpenKeyset
    Rs.LockType = adLockPessimistic
    Rs.Open "SELECT TOP 10 * FROM globe2010juni_converted", Cn, adOpenStatic
   
    Do Until Rs.EOF
      Debug.Print Rs.Fields("compcode").Value
      Rs.MoveNext
    Loop
   
    Cn.Close
End Sub

MS SQL Server-Tabelle mit Pivot drehen (z.B. für Zeitreihenanalyse)

Problemfeldbeschreibung

Beim Arbeiten mit Daten im Microsoft SQL Server kommt man gelegentlich an den Punkt, an dem man sich das „Drehen“ von Tabellendaten als nützlich entpuppt. Anwendungsgebiete sind u.a. die Prognoserechnung, Zeitreihenanalyse oder einfach Matrizenmultiplikation.

Nehmen wir z.B. den folgenden Datensatz, er soll gedreht werden:

200901     1    2009    0    30
200902     2    2009    58    16
200903     3    2009    520    15
200906     6    2009    780    15
200809     9    2008    0    7
200909     9    2009    260    8
200810    10    2008    0    

Der unerfahrene Datenbankentwickler beginnt dann häufig mit der Implementierung von langsamen Cursorskripten á la

DECLARE @compcode varchar(255)
DECLARE @market varchar(255)
DECLARE @salesdiv varchar(255)
DECLARE @material varchar(255)
DECLARE @month int
DECLARE @year int
DECLARE @bruttoumsatz varchar(255)
DECLARE @amount varchar(255)
DECLARE @spalte varchar(255)

DECLARE mmg_cursor CURSOR FOR
SELECT compcode, market, salesdiv, material, month, year, bruttoumsatz, amount
FROM cube WHERE CAST(CAST(year as varchar)+(CASE WHEN month<10 THEN ('0'+CAST(month as varchar)) ELSE CAST(@month as varchar) END ) as int) >=200610

OPEN mmg_cursor

FETCH NEXT FROM mmg_cursor INTO @compcode,@market, @salesdiv, @material,
@month, @year, @bruttoumsatz, @amount

WHILE @@FETCH_STATUS = 0
BEGIN

SET @spalte=CAST(@year as varchar)+(CASE WHEN @month<10 THEN ('0'+CAST(@month as varchar)) ELSE CAST(@month as varchar) END ) EXEC('UPDATE hypercube SET ['+@spalte+']='+@amount+' WHERE [compcode]='''+@compcode+''' AND [salesdiv]='''+@salesdiv+''' AND [market]='''+@market+''' AND [material]='''+@material+''' ') FETCH NEXT FROM mmg_cursor INTO @compcode,@market, @salesdiv, @material, @month, @year, @bruttoumsatz, @amount END DEALLOCATE mmg_cursor [/javascript]

Ansatz

Es bedarf keines Benchmarks oder einer Messung um festzustellen, dass diese Methode sehr zeitaufwändig und unzeitgemäß ist. Microsoft rät von solchen Cursor-Skripten ab und rät von der Nutzung der PIVOT-Funktion, die an Performance und Handhabung kaum zu überbieten ist.

Lösung

Das folgende SQL-Skript dreht die Tabelle mit PIVOT (Die MS SQL Server-Datenbank muss hierzu mind. auf MSSQL2005-Kompatibilität gestellt werden.

declare @columns varchar(max)
declare @columnsoben varchar(max)
declare @count int
declare @max int
declare @current int
declare @sql nvarchar(max)
declare @t table(id int identity(1,1), datekey int)
insert into @t select distinct datekey from vglobe2 order by 1
set @columns = “
set @columnsoben = “
set @count = 1
set @max = (select max(id) from @t)
while (@count <= @max) begin set @current = (select datekey from @t where id = @count) set @columns = @columns + '[' + cast(@current as varchar(6)) +'], ' set @columnsOben = @columnsOben + '[' + cast(@current as varchar(6)) +'],' + '0 as [' + cast(@current as varchar(6)) +'stat],' set @count = @count+1 end set @columns = substring(@columns, 1, len(@columns)-1) -- letztes komma set @columnsOben = substring(@columnsOben, 1, len(@columnsOben)-1) -- letztes komma set @sql = 'SELECT Artikel, ' + @columnsoben + N' FROM (SELECT artikel, bruttoumsatz , datekey FROM vglobe2) p PIVOT ( sum(bruttoumsatz) FOR datekey in (' + @columns + N') ) AS pvt ORDER BY 1' print @sql exec sp_executesql @sql [/sql]

Kostenloses Tool um SAP Rollen zu vergleichen / SAP Role Compare

Problemfeldbeschreibung

In großen Unternehmen sind die einzelnen Businessabteilungen so organisiert, dass die IT-Schnittstelle über eine Person mit IT-Background realisiert wird, diese jedoch nicht zwangsläufig von der IT gestellt wird. Hierbei kann es sich um eine „ehrenamtliche“ Tätigkeit handeln, die nichts mit der Hauptaufgabe des IT-Koordinators zu tun hat.

Die Koordinatoren besitzen besondere Rechte für die Vergabe von Rollen in der Transaktion SU01, welche auf bestimmte Länder oder Anwendungen begrenzt wird und den jeweiligen Businessbereich abdeckt.

Bei einem großem unübersichtlichen Rollenaufkommen bleibt den IT-Koordinatoren nur der Vergleich von Rollen, um herauszufinden welche Berechtigungen jemand benötigt oder welche ihm weggenommen werden sollen.

Aufgabenstellungen

Die Sachbearbeiter kommen zu den IT-Koordinatoren und diese bekommen dann die folgenden Frage- und Aufgabenstellungen

  • Die Person x benötigt die gleichen Berechtigungen auf Transaktion y wie die Person z hat.
  • Die Person x benötigt zusätzlich (alte Rechte bleiben erhalten) alle Berechtigungen im Testsystem, die sie im Produktivsystem hat
  • Die Person x braucht zusätzlich (alte Rechte bleiben erhalten) die gleichen Berechtigungen wie Person z.
  • u.v.m….

Anhand der Vielfalt von Fragestellungen stellen sich jetzt dem IT-Koordinator die folgenden Fragen

  • Welche Rollen hat die Person z an der Transaktion y, die Person x nicht hat?
  • Welche Rollen hat die Person x im Testsystem, die sie nicht im Produktivsystem hat?
  • Welche Rollen hat Person x die Person z nicht hat?
  • u.v.m. …

Ansatz

Die einzige Möglichkeit die dem Koordinator bleibt ist der Vergleich von Rollen zu Transaktionen über die Transaktion SUIM (Infosystem). Damit lassen sich die o.g. Anwendungsfälle alleine nicht abdecken. Vielmehr bietet die Transaktion die Möglichkeit eine Liste von Rollen

  • … nach Transaktionszuordnung
  • … nach Benutzerzuordnung
  • u.v.m. …

abzufragen. Diese Rollen müssten „händisch“ verglichen werden. Bei kryptischen Rollennamen nach dem Prinzip „CA_XXXX_LE44_MB“ und einer Anzahl von 200 Rollen pro Person oder Transaktion, artet das für eine „ehrenamtliche“ Tätigkeit in viel Arbeit aus.

Lösung

Vorgehensweise

Um diesen Vorgang wesentlich effizienter und effektiver zu gestalten, kann man die kostenlose Web-Anwendung (Flash) „Sap Role Compare“ benutzen. Hierbei ist die Vorgehensweise die folgende.

1.) Transkation SUIM aufrufen
2.) Die gewünschte Liste (z.B. Rollen nach Transaktionszuordnung) wählen
3.) Benutzer/Transaktion auswählen und auf die Uhr mit dem grünen Häkchen links oben klicken
4.) Wichtig: In der Ergebnisliste mit der Scrollleiste rechts GANZ RUNTER scrollen (sonst kommen die Daten nur teilweise in die Zwischenablage)
5.) Auf die Kopfzeile der Spalte klicken, in der die Rollennamen stehen, so dass ALLE Rollennamen selektiert sind.
6.) Rechte Maustaste->kopieren und im ersten Feld links der Webanwendung (Rollen (x) ) mit rechte Maustaste->einfügen (jetzt hätten wir alle Rollen der Transaktion, falls nach Transaktionname ausgewählt wurde).
7.) Wieder in SUIM den Benutzer/ die Transaktion auswählen, mit der verglichen werden soll (wir hätten jetzt die Rollen der Transaktion im ersten Feld (Rollen (x)) und kopieren ins zweite Feld (Rollen (z) )  die Rollen des Benutzers)
8.) Wieder ganz runter scrollen (nie vergessen), Spalte markieren, Rechte Maustaste->Kopieren und in Rollen(z) einfügen
9.) Den Button mit dem GLEICH-Zeichen klicken

Ergebnis

Wir erhalten jetzt 3 Ergebnismengen:

1.) Die Rollen, die in Rollen (x) und in Rollen (z) gleich sind
2.) Die Rollen, die in Rollen (x) und NICHT in Rollen (z)  sind
3.) Die Rollen, die in Rollen (z) und NICHT in Rollen (x)  sind

Mit diesen Mengen lassen sich ALLE Anwendungsfälle abdecken, die nicht durch die Transaktion SUIM unterstützt werden.

Download

Die Flash-Anwendung lässt sich hier herunterladen (rechte Maustaste -> Ziel speichern unter) …

Massenimport von Daten über MS SQL Server Management Studio

Problem

Eine große CSV Datei soll in eine Tabelle importiert werden. Hierbei sollen nicht alle Felder importiert werden.

Ansatz

Die Verwendung des Kommandos BULK INSERT wurde von Microsoft für sehr große Dateien vorgesehen, um den SQL Server ohne die Verwendung von langsamen Cursor-Iterationen zu füllen

Lösung

Vorgehensweise

Erstellen einer Tabelle mit CREATE TABLE in die importiert wird
Zunächst erstellt man eine Tabelle in die importiert werden soll, hier wird eine fiktive Tabelle RAWBEIL erstellt.

CREATE TABLE [dbo].[rawbeil](
	[gruppe] [varchar](255) NULL,
	[bezeichnung] [varchar](255) NULL,
	[feldname] [varchar](255) NULL,
	[sapcolumn] [varchar](50) NULL,
	[saptable] [varchar](50) NULL
)

Erstellen einer IMPORT-Datei für die Feldzuweisung

Der RECORD-Tag muss den kompletten Aufbau der CSV-Datei enthalten; der ROW-Teil darf enthält die Tabellenspalten, in die importiert wird. FIELD ID wird auf COLUMN SOURCE gemappt. ROW/COLUMN SOURCE kann auch Lücken enthalten, wenn man Felder aus der CSV Datei weglassen möchte..

&lt;?xml version="1.0" ?>
&lt;BCPFORMAT xmlns="http://schemas.microsoft.com/sqlserver/2004/bulkload/format" 
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	&lt;RECORD>
		&lt;FIELD ID="1" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255"/>
		&lt;FIELD ID="2" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="3" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="4" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="5" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="6" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="7" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="8" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="9" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="10" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="11" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="12" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="13" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="14" xsi:type="CharTerm" TERMINATOR="\r\n" 
                  MAX_LENGTH="255" />
	&lt;/RECORD>
	&lt;ROW>
		&lt;COLUMN SOURCE="1" NAME="gruppe" xsi:type="SQLVARYCHAR" />
		&lt;COLUMN SOURCE="2" NAME="bezeichnung" xsi:type="SQLVARYCHAR" />
		&lt;COLUMN SOURCE="3" NAME="feldname" xsi:type="SQLVARYCHAR" />
		&lt;COLUMN SOURCE="4" NAME="sapcolumn" xsi:type="SQLVARYCHAR" />
		&lt;COLUMN SOURCE="5" NAME="saptable" xsi:type="SQLVARYCHAR" />
	&lt;/ROW>
&lt;/BCPFORMAT>

SQL Anweisung

CODEPAGE wird verwendet um das Umlaute-Problem zu beheben, FIRSTROW ist die erste Spalte ab der aus CSV importiert wird (meistens will man den Header ausschließen).

DECLARE @sql nvarchar(4000)
SET @sql =
'BULK INSERT rawbeil FROM ''D:\web\karpbjde\import\db.csv''
WITH (
FORMATFILE = ''D:\web\karpbjde\import\beilmade.fmt'',
CODEPAGE=''raw'',
FIRSTROW = 2
)'
EXEC(@sql)

BULK INSERT mit Typecast in einen bestimmten Datentyp

/* Bulk-Import von einer .CSV-Datei in eine Tabelle incl. der Datentypen
* es werden Zahlen im Vormat
* xxx,yy
* xxx.yy
* -xxx,yy
* -xxx.yy
* xxx,yy-
* xxx.yy
* unterstützt. Diese werden als Money-Datentyp convertiert
* Bulk-Import
*
* gewidmet an Björn K.von Nico Gerbig
*/ 

-- Zuerst als Varchar lesen, dann richtig
create table ordersbulk (col1 varchar(50), col2 varchar(50), col3 varchar(50));
create table ordersbulk2 (col1 varchar(50), col2 money, col3 money) 

-- Eintragen in Tabelle mit Varchars
BULK INSERT OrdersBulk
    FROM 'c:\test.csv.txt'
    WITH
    (
        --FIRSTROW = 2,
        FIELDTERMINATOR = ';',
        ROWTERMINATOR = '\n'
    ) 

-- und Kopieren in die Tabelle mit den richtigen Datentypen
insert into ordersbulk2 (col1, col2, col3)
    select col1,
          dbo.ConvertVarcharToMoney(col2),
          dbo.ConvertVarcharToMoney(col3)
    from ordersbulk
go
-- testen
select * from ordersbulk2
go 

-- dafür notwendige funktion (c)  -=e!ninio=-
create function ConvertVarcharToMoney(@abc varchar(255))
    returns money
as begin
    declare @invert money;
    declare @index int;
    declare @newabc money;
    set @invert = 1;
    set @abc = Replace(@abc,',','.')
    set @abc = Replace(@abc,' ','')
    set @index = CHARINDEX('-',@abc)
    if @index &gt; 0
    begin
        set @invert = -1;
        set @abc = Replace(@abc, '-', '')
    end
    set @newabc = CAST(@abc as money) * @invert
    return @newabc
end
go 

-- Test für die Funktion selbst
select dbo.ConvertVarcharToMoney('13,41')
select dbo.ConvertVarcharToMoney('13.41')
select dbo.ConvertVarcharToMoney('-13,41')
select dbo.ConvertVarcharToMoney('13,41-')
select dbo.ConvertVarcharToMoney('- 13,41')
select dbo.ConvertVarcharToMoney('13,41 -')

by Björn Karpenstein