VMware: IP Adresse einer virtuellen Maschine ermitteln

Ich verwende VMware Workstation Player, um den Zugriff auf verschiedene Datenbanksysteme zu testen. Um unter Windows 7 die IP Adresse einer virtuellen Maschinen zu ermitteln, gehe ich folgendermaßen vor:

  1. MAC Adresse der virtuellen Maschine nachschlagen: Player → Manage → Virtual Machine Settings → Hardware → Network Adapter → Advanced → MAC Address
    Ermitteln der MAC Adresse einer VMware virtuellen Maschine.
  2. In der Ausgabe von arp -a die betreffende MAC Adresse suchen, und in der selben Zeile die IP Adresse finden.
    Ausgabe von "arp -a", in der die IP Adresse einer bestimmten MAC Adresse ersichtlich ist.

vim: Unicode Konfiguration

Kurze Zusammenfassung der Schritte, um gVim unter Windows 7 dazu zu bewegen, Unicode Glyphs anzuzeigen und neue Dateien mit UTF-8 zu kodieren:

  1. Schriftart, die möglichst viele Unicode Glyphs enthält, installieren; z.B. Deja Vu, oder Source Code Pro (schöner, aber unvollständiger).
  2. In _gvimrc – Schriftart konfigurieren; z.B.:
    set guifont=DejaVu\ Sans\ Mono:h11,Source\ Code\ Pro:h11,Andale\ Mono:h11,Courier\ New:h11
  3. In _vimrc – Statuszeile ständig sichtbar machen:
    set laststatus=2
  4. In _vimrc – fileencoding und BOM in Statuszeile anzeigen (Quelle):
    if has("statusline")
       set statusline=%<%f\ %h%m%r%=%{\"[\".(&fenc==\"\"?&enc:&fenc).((exists(\"+bomb\")\ &&\ &bomb)?\",B\":\"\").\"]\ \"}%k\ %-14.(%l,%c%V%)\ %P
    endif
  5. In _vimrc – Encodings anpassen (Quelle):
    if has("multi_byte")
       if &termencoding == ""
          let &termencoding = &encoding
       endif
       set encoding=utf-8
       setglobal fileencoding=utf-8
    endif

Zu beachten ist, daß vims Heuristik zum Erkennen der Dateikodierung öfters Schwierigkeiten mit dem Unterscheiden von UTF-8 und LATIN-1 hat. Daher stets die Statuszeile im Auge behalten und bei Bedarf manuell die Kodierung festlegen:

:set fileencoding=utf8
:w

Windows 7: Fenster effizient wechseln

In den gängigsten graphischen Benutzerumgebungen kann man mit der Tastenkombination ALT+TAB zwischen offenen Fenstern wechseln. In den letzten Jahren scheint ein wahrer Wettstreit mit dem Ziel, dieses Umschalten möglichst aufwendig und irritierend zu gestalten, entstanden zu sein. Ein Beispiel dafür ist der Gnome 3 Desktop mit seiner Voreinstellung, mehrere Fenster der selben Applikation zu einer Gruppe zusammenzufassen: Erstens sind damit nach dem ersten ALT+TAB nicht alle offenen Fenster sichtbar, und zweitend muß zwischen den Fenstern einer Gruppe mit den Pfeiltasten gewechselt werden.

Doch auch Windows 7 muß sich nicht verstecken – die „Aero Peek“ genannte Verschlimmbesserung sorgt dafür, daß der „Fensterwechsler“ nach Ablauf einer kurzen Zeitspanne ohne weiteren TAB Tastendruck verschwindet, und stattdessen das gerade ausgewählte Fenster angezeigt wird. Solange man die ALT Taste hält kann man zwar weiterhin zwischen Fenstern wechseln, aber mit deren Anzahl und unterschiedlicher Anordnung wächst der Grad der Verwirrung beim Anwender. Und wenn bei Auswahl der Arbeitsfläche der Fensterwechsler wieder erscheint, um mit dem nächsten TAB gleich wieder zu verschwinden wird es völlig grotesk. Nebenbei bemerkt trägt die schlechte Unterscheidbarkeit aktiver und inaktiver Fenster ebenfalls nicht zum Durchblick bei….

Somit zum eigentlichen Inhalt dieses Artikels – Wiederherstellung der Effizienz:

  1. „Aero Peek“ ausschalten: Start → Systemsteuerung → System → Erweiterte Systemeinstellungen → Visuelle Effekte → Aero Peek nicht aktivieren.
  2. Tastaturkürzel kennen:
    1. ALT+TAB … nächstes Fenster
    2. SHIFT+ALT+TAB … voriges Fenster
    3. ALT+ESC … Fensterwechsler schließen und im ursprünglichen Fenster bleiben
    4. STRG+ALT+TAB … Fensterwechsler in alternativem Modus aufrufen – man kann STRG+ALT loslassen und mit TAB, SHIFT+TAB oder den Pfeiltasten (oder dem Mausrad, aber wer will das schon) das gewünschte Fenster auswählen und dieses mit ENTER aktivieren.

vim: Dateien in separaten Tabs öffnen

Um unter Windows sicherzustellen, daß der Kontextmenüeintrag „Edit with existing Vim“ zusätzlich zu bearbeitende Dateien in separaten Tabs öffnet, füge man der Konfigurationsdatei _vimrc folgenden Abschnitt hinzu:

" 2015-03-12 AL open files in separate tabs
if (&diff==0)
    :autocmd BufReadPost * tab ball
endif

Quelle: Vim Tip 1440

Gnome 3: „focus follows mouse“

Mein bevorzugtes Fensterverhalten bei der Bedienung eines GUI mit der Maus ist, daß der Fokus auf das Fenster unter dem Mauszeiger übertragen wird und dieses Fenster nach einer gewissen Verzögerung – z.B. 0,35s – angehoben wird.

Unter Gnome 3 kann dieses Fensterverhalten im dconf-editor unter org.gnome.desktop.wm.preferences eingestellt werden:

  • focus-mode := mouse
  • auto-raise auswählen
  • auto-raise-delay := Verzögerung vor dem Anheben in ms

Die geänderten Einstellungen werden erst bei der nächsten Anmeldung wirksam.

R: Zugriff auf Teradata via RODBC

RODBC ermöglicht es, von R aus Teradata SQL Anweisungen durchzuführen. Abfrageergebnisse werden als Data Frames repräsentiert und können nahtlos in R weiterverwendet werden.

Zunächst importieren wir das Paket RODBC.

 library(RODBC)

Danach öffnen wir eine ODBC Verbindung zum Teradata RDBMS. Die Option USEREGIONALSETTINGS verhindert, daß RODBC – aufgrund der Ländereinstellung Österreich und des damit verbundenen Dezimalkommas – DECIMAL Spalten als Text interpretiert.

td <- odbcDriverConnect(connection="DRIVER=Teradata;DBCNAME=127.0.0.1;DATABASE=theDatabasename;UID=theUsername;PWD=thePassword;USEREGIONALSETTINGS=N")

SQL SELECT Anweisungen werden mit der Funktion sqlQuery() ausgeführt; diese liefert einen Data Frame zurück. Klarerweise achten wir in diesem Schritt auf die zu erwartende Datenmenge.

x<-sqlQuery(td,
"SELECT
    tablename                    AS name
    ,createtimestamp (DATE)      AS createdate
    ,characters(TRIM(Tablename)) AS namelength
FROM
    dbc.tables
WHERE
    tablekind='T'
    AND createtimestamp IS NOT NULL
    AND databasename='theDatabasename'
;")

Zuletzt schließen wir die nicht mehr benötigte Datenbankverbindung.

 odbcClose(td)

Der Data Frame steht weiterhin zur Verfügung:

# display the first lines
head(x)
                            name createdate namelength
1 SPxxxxxxxxxxxxxxxxxxxxxxxxxxxx 2011-11-09         30
2 MCxxxxxxxxxxxxxxxxxxxxxxxxxxx  2013-06-10         29
3 MCxxxxxxxxxxxxxxxxxxxxxxx      2013-03-12         25
4 Inxxxxxxxxxxxxxxxxxxxxx        2002-12-27         23
5 Inxxxxxxxxxxxxxxxxx            2002-02-28         19
6 BFxxxxxxxxxxxxxxxxxxxxxx       2005-03-16         24

# show column information
summary(x)
                             name       createdate           namelength   
 Abxxxxxxx                     :  1   Min.   :2000-05-16   Min.   : 3.00  
 Adxxxxx                       :  1   1st Qu.:2006-09-04   1st Qu.:14.00  
 Adxxxxxxxxxxxxxxxxxxxxx       :  1   Median :2010-07-02   Median :19.00  
 Adxxxxxxxxxxx                 :  1   Mean   :2009-05-30   Mean   :18.87  
 Adxxxxxxxxxxxxxx              :  1   3rd Qu.:2012-12-12   3rd Qu.:24.00  
 Agxxxxxxxxxxxxxxxxxx          :  1   Max.   :2014-02-07   Max.   :30.00  
 (Other)                       :543      

# plot density of table age
tableage=as.numeric(x[,2] - Sys.Date())
plot(density(tableage))

R_tableage_densityDamit ist die grundsätzliche Vorgangsweise auch schon beschrieben.
Die folgende Abfrage ist hilfreich um zu überprüfen, ob die Datentypumwandlungen zwischen RDBMS, ODBC und R funktionieren:

x<-sqlQuery(td,
"SELECT
    -128 (BYTEINT)                  AS vminByteint
    ,127 (BYTEINT)                  AS vmaxByteint
    ,-32768 (SMALLINT)              AS vminSmallint
    , 32767 (SMALLINT)              AS vmaxSmallint
    ,-2147483648 (INTEGER)          AS vminInteger
    , 2147483647 (INTEGER)          AS vmaxInteger
    ,-9223372036854775808 (BIGINT)  AS vminBigint 
    , 9223372036854775807 (BIGINT)  AS vmaxBigint
    ,-9.9 (DECIMAL(2,1))            AS vminDecimal1B
    , 9.9 (DECIMAL(2,1))            AS vmaxDecimal1B
    ,-9.999 (DECIMAL(4,3))          AS vminDecimal2B
    , 9.999 (DECIMAL(4,3))          AS vmaxDecimal2B
    ,-9.99999999 (DECIMAL(9,8))     AS vminDecimal4B
    , 9.99999999 (DECIMAL(9,8))     AS vmaxDecimal4B
    ,-9.99999999999999999 (DECIMAL(18,17))  AS vminDecimal8B
    , 9.99999999999999999 (DECIMAL(18,17))  AS vmaxDecimal8B
    ,-9.9999999999999999999999999999999999999 (DECIMAL(38,37))  AS vminDecimal16B
    , 9.9999999999999999999999999999999999999 (DECIMAL(38,37))  AS vmaxDecimal16B
    ,123.456789 (FLOAT)             AS vFloat 
    ,CURRENT_DATE                   AS vDate
    ,CURRENT_TIME                   AS vTime
    ,CURRENT_TIMESTAMP              AS vTimestamp
    ,'aZöäüÖÄÜß?!*' (VARCHAR(25))   AS vVarchar
    ,'aZöäüÖÄÜß?!*' (CHAR(25))      AS vChar
 ;")

Als Merkhilfe einige nützliche RODBC bzw. R Funktionen:

# regarding the ODBC connection ("channel")
odbcGetErrMsg(td)
odbcGetInfo(td)
sqlTypeInfo(td)

# regarding data types ("modes")
class(x)
[1] "data.frame"
mode(x)
[1] "list"
mode(x[,3])
[1] "numeric"

Teradata SQL: Median ermitteln

Falls einer Datenreihe eine schiefe Verteilung zugrunde liegt oder extreme Ausreisser vorhanden sind kann sich die Betrachtung des Medians – anstelle des auch als Durchschnitt bezeichneten, allgegenwärtigen arithmetischen Mittels – lohnen.

Während man das arithmetische Mittel mit der SQL Aggregatfunktion AVG leicht ermitteln kann, gibt es in den meisten SQL Dialekten keine Aggregatfunktion zur Bestimmung des Medians. Man muß daher „zu Fuß“ die Datenreihe wertmäßig sortieren, und dann im Fall einer ungeraden Anzahl von Werten den den genau in der Mitte liegenden Wert auswählen bzw. im Fall einer geraden Anzahl von Werten das arithmetische Mittel der beiden genau in der Mitte liegenden Werte bestimmen. Diese Aufgabenstellung kann in Teradata SQL recht elegant mit Hilfe von OLAP Funktionen gelöst werden.

Ermitteln wir zur Veranschaulichung den Median-Rechnungsbetrag jedes Kunden aus einer hypothetischen, denormalisierten Rechnungstabelle:

CREATE SET TABLE Invoice
    ,NO FALLBACK
    ,NO BEFORE JOURNAL
    ,NO AFTER JOURNAL
(
    ItemId         INTEGER       NOT NULL
    ,Quantity      DECIMAL(12,3) NOT NULL
    ,InvoiceAmount DECIMAL(11,2) NOT NULL
    ,InvoiceId     INTEGER       NOT NULL
    ,CustomerId    INTEGER       NOT NULL
)
PRIMARY INDEX(InvoiceId)
UNIQUE INDEX(InvoiceId, ItemId)
;

Verkaufte Waren werden durch die ItemId identifiziert, und für jeden Verkauf werden Verkaufsmenge und verrechneter Betrag gespeichert. Alle gemeinsam verkauften Waren erkennt man über die InvoiceId. Die Zuordnung zu einem Kunden erfolgt via CustomerId.

Fügen wir einige Rechnungen in diese Tabelle ein:

-- customer 7001
 INSERT INTO Invoice(100, 1,  30, 222, 7001)  -- invoice 222
;INSERT INTO Invoice(101, 5,  55, 222, 7001)
;INSERT INTO Invoice(102, 2,  10, 222, 7001)
;INSERT INTO Invoice(100, 2,  60, 298, 7001)  -- invoice 298
;INSERT INTO Invoice(101, 1,  11, 298, 7001) 
;INSERT INTO Invoice(188, 1, 500, 755, 7001)  -- invoice 755
-- customer 7444
;INSERT INTO Invoice(100,  7, 210, 225, 7444) -- invoice 225
;INSERT INTO Invoice(102, 50, 250, 225, 7444)
;INSERT INTO Invoice(188,0.5, 250, 679, 7444) -- invoice 679
;INSERT INTO Invoice(102, 10,  50, 679, 7444)
;

Zunächst ermitteln wir je Kunde eine sortierte Liste aller Rechnungsbeträge:

SELECT
    InvoiceId
    ,CustomerId
    ,SUM(InvoiceAmount) AS InvoiceAmount
FROM Invoice
GROUP BY InvoiceId, CustomerId
ORDER BY CustomerId, SUM(InvoiceAmount)
;
InvoiceId CustomerId InvoiceAmount
298 7001 71,00
222 7001 95,00
755 7001 500,00
679 7444 300,00
225 7444 460,00

Diesem Zwischenergebnis entnehmen wir nun die relevanten Zeilen. Für Kundennummer 7001 ist dies (ungerade Anzahl an Rechnungen!) die Rechnung 222. Da für Kundennummer 7444 eine gerade Anzahl an Rechnungen vorliegt benötigen wir sowohl Rechnung 679 als auch Rechnung 225, um das arithmetische Mittel derer Rechnungsbeträge zu bilden.

Unter Zuhilfenahme ganzzahliger Division kann man die Position der relevanten Zeile(n) im sortierten Zwischenergebnis mit n Zeilen berechnen:

  • m1=(n+1)/2
  • m2=(n+2)/2

Sowohl die Gesamtanzahl an Rechnungen als auch Position jeder einzelnen Zeile lassen sich mit OLAP Funktionen bestimmen. Gleichzeitig eliminiert die QUALIFY Klausel alle nicht benötigten Zeilen des Zwischenergebnisses:

SELECT
    InvoiceId
    ,CustomerId
    ,SUM(InvoiceAmount) AS InvoiceAmount
FROM Invoice
GROUP BY InvoiceId, CustomerId
QUALIFY
             COUNT(*) OVER (PARTITION BY CustomerId ORDER BY SUM(InvoiceAmount) ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
    BETWEEN (COUNT(*) OVER (PARTITION BY CustomerId ORDER BY SUM(InvoiceAmount) ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) + 1) / 2
    AND     (COUNT(*) OVER (PARTITION BY CustomerId ORDER BY SUM(InvoiceAmount) ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) + 2) / 2
;
InvoiceId CustomerId InvoiceAmount
222 7001 95,00
679 7444 300,00
225 7444 460,00

Zuletzt bilden wir noch je Kunde das arithmetische Mittel aller verbliebenen Rechnungsbeträge. Die gesamte Abfrage lautet:

SELECT
    CustomerId
    ,SUM(InvoiceAmount) / COUNT(*) AS MedianInvoiceAmount -- or simply AVG(InvoiceAmount)
FROM
(
    SELECT
        InvoiceId
        ,CustomerId
        ,SUM(InvoiceAmount) AS InvoiceAmount
    FROM Invoice
    GROUP BY InvoiceId, CustomerId
    QUALIFY
                 COUNT(*) OVER (PARTITION BY CustomerId ORDER BY SUM(InvoiceAmount) ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
        BETWEEN (COUNT(*) OVER (PARTITION BY CustomerId ORDER BY SUM(InvoiceAmount) ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) + 1) / 2
        AND     (COUNT(*) OVER (PARTITION BY CustomerId ORDER BY SUM(InvoiceAmount) ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) + 2) / 2
) t
GROUP BY 1
ORDER BY 1    
;
CustomerId MedianInvoiceAmount
7001 95,00
7444 380,00

lftp

Wer auf Kommandozeilenebene komplexere Aufgaben auf einem (S)FTP Server zu verrichten hat sollte sich den FTP client lftp ansehen. Dieser berücksichtigt die Konfigurationsdatei .netrc, kann mehrere Verbindungen parallel nutzen und Dateibefehle rekursiv auf ganze Verzeichnisbäume ausführen.

Durch den Befehl

mirror --verbose=3 --parallel=3 --no-umask --delete-first src dst

spiegelt lftp beispielsweise den gesamten Quellverzeichnisbaum src in das lokale Verzeichnis dst. Die Übertragung erfolgt parallel mit 3 Verbindungen. Auch Datei- sowie Verzeichnisberechtigungen werden 1:1 übernommen.

Vgl. auch: man lftp

OVERLAPS: Definition vs. Intuition

Die ANSI SQL Funktion OVERLAPS prüft, ob zwei Zeiträume einander überlappen. OVERLAPS betrachtet die beiden Zeiträume dabei als halboffene Intervalle: Der jeweilige Endzeitpunkt wird als nicht im Zeitraum liegend betrachtet.

Beispiele:

  1. (current_date – 1, current_date) OVERLAPS (current_date, current_date + 1)
  2. (current_date, current_date) OVERLAPS (current_date, current_date + 1)

Die meisten Menschen meinen zunächst, daß beide ANSI SQL Ausdrücke wahr wären. Tatsächlich evaluiert ANSI SQL 1. zu falsch und 2. zu wahr. Der Irrtum entsteht dadurch, daß unser intuitives Verständnis überlappender Zeiträume i.d.R. auf geschlossenen Intervallen basiert: Wir betrachten auch den Endzeitpunkt als zum Zeitraum gehörend.

Im Folgenden seien P und Q zwei Zeiträume und stehen a bzw. e für den Anfangs- bzw. Endzeitpunkt eines Zeitraumes. Es gelte a <= e.

Um in ANSI SQL zu prüfen, ob die zwei geschlossenen Intervalle [Pa, Pe] und [Qa, Qe] einander überlappen, werte man folgende Bedingung aus:

Pe >= Qa AND Qe >= Pa

Begründung:

Es gibt sieben grundsätzliche Möglichkeiten, wie zwei geschlossene Intervalle zueinander liegen können. Davon sind fünf überlappend (1. – 5.) und nur zwei überlappungsfrei (6. – 7.):

  1. P und Q sind gleich
  2. P liegt vollständig in Q
  3. Q liegt vollständig in P
  4. P beginnt vor Q und endet in Q
  5. Q beginnt von P und endet in P
  6. P beginnt und endet vor Q
  7. Q beginnt und endet vor P

Ein allgemeiner Test auf Überlappung wäre: (Fall 1. trifft zu) oder (Fall 2. trifft zu) … oder (Fall 5. trifft zu). Einfacher ist es, Überlappung als Gegenteil von „überlappungsfrei“ zu betrachten: Weder (Fall 6. trifft zu) noch (Fall 7. trifft zu).

In ANSI SQL lautet dieser Ausdruck NOT(Pe < Qa OR Qe < Pa). Dies läßt sich unter Anwendung der DeMorganschen Regeln umformen zu (Pe >= Qa AND Qe >= Pa).