QlikView /NoData im Contextmenü - Rightqlik

Wer erinnerst sich an unseren Blogpost über den QlikView /Nodata Shortcut am Desktop? Miguel García hat auf seinem Blog IQlik jetzt eine noch elegantere Lösung präsentiert: Rightqlik.

RightQlik in Action

Neben "Open without data" gibt es auch "Open in a new QV instance". Wunderbar! Bisher habe ich immer zuerst einen QVDeveloper geöffnet, und anschließend die .qvw via Drag&Drop  reingezogen. Ein Mouse-Klick Ersparnis bei ein paar tausend .qvws die ich wohl im Jahr öffne ist einiges :)

Zusammen mit einem komfortablen QVD-Viewer wie EasyQlik macht da das Arbeiten mit .qvws und .qvds auch abseits des QlikView Developers  richtig Spaß!

EasyQlik - Preview einer .qvd mit Doppelklick




Qlikview - Poor man's Print & Scroll Up Extension

Heute gibt es einen Ausflug in die dunklen Kammern der  "Quick and Dirty - getting it done" Heldendaten-Katakomben. Schon mal eine QlikView Extension programmiert? QlikView Workbench installieren, Property-Bags definieren, am Server deployen? Geht das nicht einfacher? Natürlich! Gibt es dafür Support: selbstverständlich nicht :-)

QlikView's "Full Browser Client" - vormals AJAX-Client oder ZeroFootprintClient - ist ein voller HTML5 Client. Auch der QlikView (Offline) IPAD-Client, der dieser Tage in Version 2.0 erschienen ist,  startet im Onlinemodus innerhalb der App einfach den Safari Browser.

Will man den Client funktionell und/oder graphisch erweitern, so hat man in QlikView 11 die Möglichkeit Document- bzw. Object Extensions zu entwickeln. Stefan Walther schreibt darüber gerade eine Blogserie auf qlikblog.at. Meine liebste Spaß-Extension findet sich im QlikMarket und auf unserer Demo-Seite.


Poor Man's Extensions

Ich weiß gar nicht ob es ein Feature ist, oder ein Bug. Ich weiß auch gar nicht wie ich auf die doofe Idee kam. Aber seit ich es in QlikView entdeckt habe, nenne ich es "Poor Man's" Extensions :)

Irgendwann in den 90er-Jahren, als viele Javascript noch nicht von Java unterscheiden konnten, fand man manchmal folgende verbrecherische Syntax um einen Messagebox bei Klick auf einen Hyperlink anzuzeigen:

<a href="javascript:alert('Hallo Welt!');">Dein Link</a>

Lang vergessen, kann man sich diese Syntax in QlikView zu nutzen machen. Wie? Das lesen Sie in den nächsten zwei Abschnitten!

Poor Man's JavaScript Print Extensions

In QlikView gibt es einige Aktionen die vom AJAX-Client ignoriert werden. Ein Beispiel ist die "Arbeitsblatt drucken"-Aktion.

QlikView Arbeitsblatt drucken|Print Sheet Aktion
Diese Aktion wird vom AJAX-Client ignoriert


Während im Fat-Client und im IEPlugin QlikView die Darstellung selbst in der Hand hat (und somit auch "selbst" drucken kann), ist im AJAX-Client das Rendering via HTML an den Browser ausgelagert. Somit kann man sich auch nur auf den "Druck" des jeweiligen Browser-Herstellers verlassen. Das führte wohl dazu, dass der QlikView AJAX Client diese Aktion gleich komplett ignoriert.


Dieses Verhalten ist natürlich nicht ganz zufriedenstellend für einen Anwendungsentwickler der beide QlikView-Clients (IE-Plugin und AJAX) für seine Analysten bereitstellen muss. Während die IEPlugin-User den Button benutzen können, muss man den AJAX-Usern erklären, dass sie umständlich im Browsermenü den Druck anstarten müssen, oder am einfachsten gleich einen Screenshot machen.

Gibt es einen Ausweg? Ja, man könnte eine "Button"-Extension programmieren die den Browser-Druck mittels Javascript "window.print()" aufruft. Geht es auch Quick and Dirty? Ja, denn QlikView lässt eine Syntax zu, die ich eben seit den 90ern nicht mehr gesehen hatte:

Es gibt eine QlikView Aktion "URL Öffnen". Ich nehme an, dass diese Aktion gedacht ist um externe URLs, webbasierte CRMs oder etwa Documentmanagement-Systeme anzuspringen, und gegebenenfalls die aktuelle QlikView-Selektionen als GET-Parameter zu übergeben.

Interessanterweise schluckt diese Aktion aber auch folgenden Befehl:  javascript:window.print()

Quick&Dirty: Javascript in Qlikview


Der Aufruf von JavaScript macht im QlikView-Fatclient keinen Sinn, da hier keine Interpretation des Befehls stattfindet. Um den Button wirklich nur im AJAX-Client anzuzeigen, benutzen wir die Clientplatform()-Funktion. Wer QlikView für das IPAD entwickelt hat, hat diese Funktion zum Erkennen der Browser-Plattform vielleicht schon benutzt:


 FatClient und IEPlugin liefern einen leeren  String für ClientPlatform(). Wir zeigen das Objekt also nur an wenn ein Browser kommt!

Kleiner Tipp am Rand
Hat man die Anzeigefunktion definiert, so verschwindet der Button natürlich sofort im QlikView-Developer. Wenn Sie den Button während der Entwicklung sehen möchten, zeigt der Shortcut CTRL+SHIFT+S alle Objekte (und damit auch den Button) an! Vor dem Speichern bitte wieder CTRL+SHIFT+S, damit das normale Verhalten wieder hergestellt ist.

Der Test auf unsere Demo-Seite funktioniert: In QV11.20SR5 mit Firefox 27 und IE11 funktioniert meine "Poor Man's Print Extension".

IE 11 Druckdialog öffnet sich
PS: Um das Druckergebnis im Browser zu verbessern, sollten Sie den Druck der Hintergrund-Graphik aktivieren. Das ist in jedem Browser (ja sogar Browser-Version) ein wenig wo anders versteckt, hier die Einstellungen für IE11 und Browser.
Firefox Hintergrund drucken

IE 11: Hintergrundfarben und -bilder drucken

Poor Man's JavaScript ScrollUp Extension

Vor kurzem erhielt wir eine weitere Anfrage: Ein Kunde hat eine Applikation die sehr viele Diagramme nach unten anzeigt. Daraus resultiert ein langer vertikaler Scrollbalken. Kann man es bewerkstelligen, dass der User mittels eines Buttons wieder ganz rauf zum Start der Applikation springt?

In JavaScript gibt es dafür eine Funktion: window.scrollTo(0,0). Es bietet sich also wieder an eine Poor Man's Extension zu schreiben :-)

Hier kommt aber nun leider ins Spiel, dass diese Vorgehensweise von Seiten der QlikTech natürlich nicht supported ist. window.scrollTo(0,0) funktionierte wunderbar im Firefox, doch IE11 wehrte sich standhaft dagegen, nach oben zu scrollen.

Ein bisschen Code-Analyse des AJAX-Clients später stellte sich heraus, dass QlikTech sich scheinbar die Scrollposition merkt. Um also die Poor Man's Extension auch im IE11 lauffähig zu machen, muss ich die Aktions-Definition erweitern auf:

javascript:Qva.binders[""].ScrollLeftToRemember=null;window.scrollTo(0,0);

Bisher konnte ich keine Nebenwirkungen unseres Hacks feststellen. Der Button funktioniert in Firefox 27, IE11, Chrome 33 und auch am IPAD mit Safari. Die Benutzung ist natürlich trotzdem auf eigenes Risiko! Zum Testen und gibt es die Applikation wie immer auf unserem Demo-Accesspoint demo.heldendaten.net




Die Aktion für ScrollUp im IE11, Firefox 27 und IPAD IOS7



Die ScrollUp-Applikation in ihrer vollen Pracht. Der Poor Man's ScrollUp Button ganz unten.

Dublicate Checker - Datenbereinigung mit QlikView

Unsere Kunden setzen QlikView gerne ein um die Datenqualität Ihrer Systeme zu sichten. Das Assoziative Grün-Weiss-Grau Datenmodell, und QlikView's Eigenschaft jeden distinkten Wert eines Feldes in einer Listbox anzuzeigen, hilft oft einen schnellen Überblick über den Datenbestand zu bekommen.

Typische Datenqualitätsprobleme in QlikView sichtbar gemacht:  Unterschiedliche Schreibweisen 

Auch bei Migrationen, Umstieg auf neue Systeme und Zusammenführung von Alt-Daten kommt QlikView immer wieder zum Einsatz. Zuletzt bekam ich eine interessante Anfrage von einem Kunden: Ist es mit QlikView einfach möglich Duplikate zu finden? Pflegt man etwa den Artikelstamm in zwei (oder vielleicht sogar mehr) Systemen, kann ich überprüfen ob die Artikel-Ausprägungen für eine ArtikelNr in beiden Systemen gleich ist?

In meinem kleinen Beispiel hat jede Artikel  (ID) drei Ausprägungen: Name, Preis, ShortName.
Aus diesen beiden Quellsystemen sollen Duplikate gefiltert werden


Ziel ist es nun zu überprüfen ob die Kombination aus "Name, Preis und ShortName" sich in den beiden Systemen unterscheiden. QlikView bietet dafür eine praktische Funktion: autonumberhash256. An dieser Funktion kann man eine beliebige Anzahl an Feldern übergeben. Für jede Wert-Kombination wird innerhalb eines QlikView Scripts ein eindeutiger Wert berechnet.

In unserem Beispiel rufen wir also die Funktion mit 
autonumberhash256(Name,Preis,ShortName) 
auf um ein neues Feld "hash" zu generieren.


Wie man sehen kann wird für die Ausprägung (1,Apple,10,A) in beiden Datenquellen der Hashwert 1 generiert. Dieser Datensatz ist also in beiden Quellsystemen gleich, somit keine Dublette. Im Gegensatz dazu liefert die Funktion für die Werte (2,Orange, 11, ORANG) und (2, Orange,11, O) zwei unterschiedliche Hashwerte. Damit hat die Artikel mit ID=2 eine Dublette.

Im nächsten Schritt des QlikView Scripts markiert man sich nun mittels eines group by und einem load above load alle IDs die mehr als einen unterschiedlichen Hashwert aufweisen --> denn genau das sind meine Dubletten. In unserem Fall sind es die zwei Produkte Orange und Banana.

Die zwei gefundenen Dublikate. Artikel 2 und Artikel 3

Die .qvw mit dem Dublicate Checker finden Sie unter diesem Link. Einfach die Felder der autonumberhash256()-Funktion an Ihre Datenquellen anpassen. Der Ansatz funktioniert übrigens auch mit mehr als 2 Source Systemen - autonumberhash256() sei Dank.

Weiters schlummert auf meiner Festplatte ein altes Beispiel (von einem unbekannten Author) mit einem phonetischen Dublettenchecker . Gelöst mit einem Macro das im Skript aufgerufen wird. Sicherlich sehr interessant wenn man mehrere Adressbestände abgleichen möchte. Vielleicht wird das ja mal ein eigener Blogeintrag ;-)





Karten in QlikView & A brief history of time

Landkarten in QlikView - eine historische Aufarbeitung

Benötigte man in QlikView 8 eine Kartendarstellung, dann war die Antwort recht einfach: Man nehme ein fixes Hintergrundbild und benutze ein Punktdiagramm (Scatter Chart) um die Punkte entsprechend darauf zu positionieren.  Die Punktgröße kann zusätzlich genutzt werden um die Kennzahl in Relation zu stellen.

Die Einwohnerzahlen auf der Weltkarte mittels Punktdiagramm

Ausgehend von diesem Grundgedanken gab es tolle Verfeinerungen dieses Verfahrens (Drilldown von Land auf Bundesländer indem man pro Bundesland ein verstecktes Chart vorbereitete; Kabinenbelegungen auf dem Grundrissplan eines Kreuzfahrtschiffes, etc.). Auf meiner Festplatte schlummern dazu noch einige Schätze :).

Kreuzfahrtschiff Kabinenbelegung mit einem Punktdiagramm

QlikView 9 beinhaltete dann die Google Maps Integration. Das ermöglichte den dynamischen Kartendrilldown bis auf Straßenebene, brachte aber auch die Problematik des Lizenzthemas mit sich: Google erlaubt zwar das Kartenmaterial gratis im Internet wiederzuverwenden; da viele QlikView Applikationen aber reine Intranet-Applikationen sind, sollte man hier die Google Lizenzvereinbarungen genauer studieren. Und natürlich benötigt der Anwender eine aktive Internetverbindung um das Kartenmaterial On The Fly von Google nachzuladen.

Rechts die "gezoomte" GoogleMaps Integration im Vergleich zur "starren" Punktdiagramm Lösung


Beschäftigt man sich heute mit Kartendarstellungen in QlikView, findet man mittlerweile eine Vielzahl von interessanten Beispielen in der QlikCommunity bzw. Lösungen von professionellen Anbietern im QlikView Market (inklusive unser eigenen HD Heatmap Extension :-)). Auch QlikTech selbst fördert dieses Thema im aktuellen Release QV11.20 SR5 mit einer weiteren Karten-Extension.

Wer noch keine Möglichkeit hatte einen Blick auf die neue QV11.20SR5 Extension zu werfen, findet das Beispiel auf unserem Demo-Accesspoint. Die neue Extension bietet 3 Möglichkeiten Informationen auf der Karte darzustellen:
  • Punktdarstellung
  • Routendarstellung
  • Polygondarstellung
Die Extension erlaubt das Einbinden von unterschiedlichen (freien) Kartenanbietern, Zoom bis auf Straßenebene sowie eine bidirektionale Kommunikation (Auswahl in der Karte, selektiert den Wert in der QlikView Applikation und vice versa).  Momentan noch "unfertig" sieht die Tooltip-Implementierung aus - da muss man manuell nachbessern.

Die verschiedenen Darstellungen der QV11.20SR5 Extension

Routen und Flächen im IEPlugin

Extensions sind nur im QlikView FullBrowser (AJAX) Client verfügbar. Benutzten die Endanwender das ActiveX/IE-Plugin, kann man also die neue Extension leider nicht einsetzen. Für die Darstellung von Punkten auf Landkarten funktioniert die genannte Punktediagramm-Methode mittels fixen Hintergrundbild bzw. Google Maps Integration. Die Frage ist nun: klappt es auch Routen und Flächen im IEPlugin darzustellen?

Für Routen kann man sich eine Eigenschaft des ScatterCharts zu Nutzen machen: Will man in QlikView etwa sehen, wie sich ein Produkt über die Jahre entwickelt, kann man das Jahr als erste Dimension im Chart hinzufügen. Stellt man dann unter "Präsentation" noch die Pfeildarstellung ein, erreicht man folgende Darstellung:

Punkdiagramm mit 2 Dimensionen: das Produkt Minnki Pälsii fällt beim Bruttogewinn seit 3 Jahren ab
Wenn man also Pfeile im Punktdiagramm darstellen kann, warum diese nicht auch für Routen benutzen? Bereitet man sich im Datenmodell ein entsprechendes Routen-Feld vor, kann man folgende Darstellung erreichen.

Routen im Punktdiagramm
Mit sehr vielen Routen wird die fixe Kartendarstellung mangels dynamischen Zooms etc. nicht alle Anforderungen erfüllen können. Für kleinere Anwendungsgebiete ist diese Möglichkeit jedoch schnell & einfach zu implementieren. Nutzt man das Diagramm in Kombination mit der Google Maps Integration, hat man sogar eine "zoombare" Lösung. Das gesamte Beispiel können Sie sich hier ansehen, bzw. über http://demo.heldendaten.net herunterladen

Für die Darstellung von Flächen kann man das QlikTech Punktdiagramm leider nicht nutzen. Punkte und Linien sind die einzigen beiden Darstellungsformen. Hier kenne ich lediglich die Möglichkeit, die Landkarte über die Google Chart API einfärben zu lassen. Exemplarisch sei hier ein Beitrag aus der QlikCommunity gezeigt:

Beispiel aus Rob Wunderlich's QlikView Cookbook

Spanische Provinzen Heatmap


Die Lösung setzt eine aktive Internetverbindung voraus, da bei jeder QlikView-Selektion eine URL bei Google aufgerufen wird, und das gerenderte Bild zurückkommt. Folgende zwei Dinge sollten bei dieser Lösung beachtet werden:
  • Bitte Vorsicht bei sensiblen Unternehmensdaten!  Diese Daten gehen an Google.
  • Meiner Erfahrung nach kann ein GET-Parameter nicht unendlich lang werden (zumindest war das in QV9 der Fall). Also für sehr viele Flächenfärbungen könnte man hier an Grenzen stoßen.



  





The Lost Datarows: DISTINCT - Verhalten bei Join, Concatenate

Normalerweise erzähle ich meinen QlikView-Novizen immer, dass man ein QlikView Skript von oben nach unten (und - falls Reiter vorhanden - von links nach rechts) liest. Innerhalb eines Load&Select-Statements gilt die Ausnahme, dass man diesen Statement-Block von unten nach oben lesen muss. Zuerst kommen die Daten aus einer Datenquelle (unten), dann werden sie mit einem (oder mehreren) Load-Statement weiterverarbeitet.

Hier ein einfaches Beispiel - aus der Ursprungstabelle lade ich mit einem oberhalb liegenden LOAD DISTINCT die eindeutigen Preise für die 3 Produkte. Das Statement muss von unten (Inputtabelle hat 4 Zeilen*3 Spalten) nach oben  (Ergebnistabelle hat 3 Zeilen*2 Spalten) gelesen werden.

Es gibt jedoch einen Fall den man genauer betrachten muss: LOAD DISTINCT in Kombination mit JOIN bzw. CONCATENATE:

Ergänzt man das Skript um einen Left Join auf die Faktentabelle, sieht das Skript folgendermaßen aus:

Facts:
LOAD * INLINE [
    Year, Product, Quantity
    2013, ProductA, 500
    2013, ProductA, 500 
    2013, ProductB, 300
    2012, ProductA, 700
    2012, ProductB, 400
];

left join (Facts)
Load
Distinct
 Product,
 Price;
LOAD * INLINE [
    Product, Price, Variante
    ProductA, 100, X
    ProductA, 100, Y
    ProductB, 10, A
    ProductC, 700, A
];


Liest man das Skript nach den genannten Regeln, würde man folgendes Join-Verhalten erwarten:
  • (Schritt -1: Die Faktentabelle wird  mit 5 Zeilen geladen)
  • Schritt 0: Die Produkttabelle wird mit 4 Zeilen INLINE geladen
  • Schritt 1: Load Distinct reduziert die Produkttabelle auf 3 Zeilen
  • Schritt 2: Der Left Join beläst per Definition die Faktentabelle (linke Tabelle) gleich und ergänzt die passenden Preise aus der Produkttabelle (rechten Tabelle).
Symbolische Darstellung des "zu erwartenden" Verhalten

Als Ergebnis würde man sich also eine Quantity von (500+500+300+700+400) = 2400 erwarten.

Lässt man das Script aber in QlikView laufen, erhält man überraschenderweise ein Chart mit der Quantity 1900:


Quanity für ProductA ist 2013 nur 500
Was ist passiert? Die Quantity für ProductA ist im Jahr 2013 nur 500, statt der ursprünglichen 1000 aus der Faktentabelle. Die Faktentabelle enthält auch nur 4 statt der ursprünglichen 5 Zeilen. Es scheint also, als ob der LEFT JOIN Einfluss auf unsere Faktentabelle genommen hat.

Betrachtet man das Ergebnis genauer, so ist es nicht der LEFT JOIN, sondern vielmehr das Keyword DISTINCT auf der Produkttabelle das Schuld an unserem fehlenden Datensatz ist. Das DISTINCT hat nicht nur Einfluss auf die geladene Produkttabelle, sondern reduziert auch alle Einträge in der Faktenabelle auf eindeutige Werte.  


Beim Lesen eines QlikView Skrips muss also folgende zusätzliche Regel beachtet werden:


DISTINCT wird in QlikView nicht auf die Inputtabelle angewandt, sondern am auf die gesamte, geladene Tabelle! Wird eine Tabelle also einmalig als DISTINCT markiert (direkt oder indirekt [durch Join/Concatenate]) sind die Zeilen diese Tabelle in allen weiteren Operationen IMMER DISTINCT!

Um aus der ursprünglichen Faktentabelle nicht "versehentlich" Zeilen zu entfernen, benötigen wir somit eine Zwischentabelle. Das Skript sieht dann folgendermaßen aus

FixFacts:
LOAD * INLINE [
    FixYear, FixProduct, FixQuantity
    2013, ProductA, 500
    2013, ProductA, 500 
    2013, ProductB, 300
    2012, ProductA, 700
    2012, ProductB, 400
];

Temp:
Load
Distinct
 FixProduct,
 FixPrice;
LOAD * INLINE [
    FixProduct, FixPrice, FixVariante
    ProductA, 100, X
    ProductA, 100, Y
    ProductB, 10, A
    ProductC, 700, A
];

left join (FixFacts)
load
*
resident Temp;

drop table Temp;
 
 
Quantity 2400 als korrektes Ergebnis

Hier klappt es nun wie erwartet, weil nur die "Temp"-Tabelle mit dem Keyword DISTINCT markiert wurde. Die "FixFacts" Tabelle ist an keiner Stelle mit dem Keyword DISTINCT versehen. Somit bleibt das ProduktA im Jahr 2013 mit einer Quantity von 500 zweimal in der Tabelle erhalten.

Ein zweites Szenario wo man dieses Verhalten manchmal unabsichtlich auslöst, ist der Befehl CONCATENATE. Das Skript unterhalb löscht Zeilen aus meiner Istdaten-Tabelle "ConcatFacts", obwohl ich eigentlich nur die Plandaten DISTINCT daran "anhängen" wollte:

ConcatFacts:
LOAD * INLINE [
    CYear, CProduct, CQuantity
    2013, ProductA, 500
    2013, ProductA, 500 
    2013, ProductB, 300
];

//Append Plan Data
concatenate(ConcatFacts)
LOAD distinct * INLINE [
    CYear, CProduct, Plan
    2013,ProductA, 900
    2013,ProductA, 900
    2013,ProductB, 200
]; 
 
Ist-Quantity von ProduktA ist nach CONCATENATE & DISTINCT nur 500

Auch hier hilft wieder der Umweg über eine temporäre "DISTINCT"-Tabelle
FixConcatFacts:
LOAD * INLINE [
    CCYear, CCProduct, CCQuantity
    2013, ProductA, 500
    2013, ProductA, 500 
    2013, ProductB, 300
];

//Append Plan Data
Tmp:
LOAD distinct * INLINE [
    CCYear, CCProduct, CCPlan
    2013,ProductA, 900
    2013,ProductA, 900
    2013,ProductB, 200
];

Concatenate(FixConcatFacts)
load
*
Resident Tmp;

drop table Tmp;


Quantity 1000 für ProductA als korrektes Ergebnis


Zuletzt hatten wir dieses "Phänomen" am MasterSummit for QlikView diskutiert. Das Verhalten ist zumindest seit QlikView 8.* (meiner ersten QlikView Version) konsistent. Falls Sie diese Eigenschaft von DISTINCT noch nicht kannten, und Ihnen in Ihren Skripten Datenzeilen abgehen, durchsuchen Sie Ihre QlikView Applikationen nach den beschriebenen Mustern! Das komplette Beispiel zu diesem Blogeintrag findet sich unter http://content.heldendaten.eu/DistinctVerhalten.zip




heldendaten GmbH,2020