GEAVANCEERDE VISUALISATIES MET SQL SERVER 2016 EN D3.JS door Willem Otten, Microsoft BI-consultant bij Kadenza Lees het volledige artikel en andere blogs op www.playitsmart.nl/blog Met self service BI-tools als Tableau, Qlikview en Power BI kun je prachtige visualisaties maken op de data in je SQL Server datawarehouse. Soms wil je echter een bijzondere visualisatie maken die niet in de standaard set van de tool zit. SQL Server 2016 maakt dat een stuk eenvoudiger door de ondersteuning van JSON-output. Met data in JSON-formaat kun je gebruik maken van grafische libraries als Google Charts en D3.js. Met behulp van (bestaande) visualisaties die gemaakt zijn met D3 en JSONgeformatteerde data uit SQL Server 2016 maak je betrekkelijk eenvoudig in het oog springende, interactieve visualisaties. In deze blog werk ik enkele voorbeelden uit om een beeld te geven van de nieuwe mogelijkheden door deze feature van SQL Server 2016. Ik maak gebruik van de AdventureWorks database van Microsoft en de Community Technology Preview 3 van Microsoft SQL Server 2016. De uitgewerkte voorbeelden zijn beschikbaar om te downloaden en zelf mee te experimenteren. JSON: DATA DIE JE BROWSER BEGRIJPT JSON staat voor JavaScript Object Notation, een notatie waarmee gestructureerde data op een eenvoudig leesbare manier kunnen worden beschreven. Je kunt JSON zien als een eenvoudige variant op XML. Een belangrijk voordeel van JSON is dat het formaat direct geïnterpreteerd kan worden door JavaScript. Een JSON-beschrijving kan door een JavaScript-engine, bijvoorbeeld die in je webbrowser, worden vertaald naar een objectdefinitie. Veel webservices, met name REST-services, maken gebruik van het JSON-formaat voor gegevensuitwisseling. Meer weten over JSON of de JSON-ondersteuning in SQL Server 2016? Kijk op onderstaande links. http://www.w3schools.com/json/ https://msdn.microsoft.com/en-us/library/dn921897.aspx https://msdn.microsoft.com/en-us/library/dn921882.aspx
NETWORK CHART De eerste visualisatie die ik uitwerk, is een network chart. Deze visualisatie is geschikt om relaties tussen gegevens inzichtelijk te maken. Ik gebruik de visualisatie om een beeld te geven van de type fietsaccessoires die tegelijkertijd worden verkocht op een verkooporder. Het is niet het doel van dit blog om een uitgebreide beschrijving van D3.js te geven. Ik maak daarom gebruik van een bestaande, door iemand anders ontwikkelde visualisatie, namelijk deze. De visualisatie verwacht een databron in het volgende JSON-formaat: [ source: "Microsoft", target: "Amazon", type: "licensing"}, source: "Microsoft", target: "HTC", type: "licensing"}, source: "Samsung", target: "Apple", type: "suit"}, source: "Motorola", target: "Apple", type: "suit"},...... source: "Kodak", target: "RIM", type: "suit"}, source: "Nokia", target: "Qualcomm", type: "suit"} ]; Voor het overzicht heb ik niet veel meer nodig dan een lijstje met paartjes van type fietsaccessoires (subcategorieën binnen categorie Accessories ) die samen voorkomen op dezelfde verkooporder. Dat lijstje vraag ik op met de volgende query: select s.name as 'FirstAccessoryType', s1.name as 'SecondAccessoryType', count (distinct d.salesorderid) as 'NrOfSales' from production.productsubcategory s join production.productcategory c on s.productcategoryid = c.productcategoryid join production.product p on s.productsubcategoryid = p.productsubcategoryid join sales.salesorderdetail d on p.productid = d.productid join sales.salesorderdetail d1 on d1.salesorderid = d.salesorderid and d1.productid <> d.productid join sales.salesorderheader so on so.salesorderid = d.salesorderid join production.product p1 on d1.productid = p1.productid join production.productsubcategory s1 on p1.productsubcategoryid = s1.productsubcategoryid and s1.productsubcategoryid > s.productsubcategoryid --eliminate duplicate records and s1.productcategoryid = c.productcategoryid where c.name = 'Accessories' -- filter on Accesoires group by c.name, s.name, s1.name for json path
Door toevoeging van de for json path clause aan het eind van de query krijg ik het lijstje in JSON-formaat. Handig! De output van de query: Een aandachtspunt is dat SQL Server de JSON-output bij een grotere resultset opdeelt in meer records. In dat geval moet je de totale JSON-output eerst weer ontdoen van de newlines voordat je syntactisch correcte JSON tot je beschikking hebt. Nadat je de newlines hebt verwijderd, kun je de JSON-output formatten met een online formatter zoals JSONLint. Dit laatste is voor toepassing in JavaScript niet nodig, maar vergroot wel de leesbaarheid. De geformatteerde output ziet er dan als volgt uit (fragment): [ },.... ; "FirstAccessoryType": "Bike Racks", "SecondAccessoryType": "Bottles and Cages", "NrOfSales": 435 "FirstAccessoryType": "Bike Stands", "SecondAccessoryType": "Bottles and Cages", "NrOfSales": 4 "FirstAccessoryType": "Bike Racks", "SecondAccessoryType": "Cleaners", "NrOfSales": 394 "FirstAccessoryType": "Hydration Packs", "SecondAccessoryType": "Tires and Tubes", "NrOfSales": 350
Met een kleine aanpassing in de visualisatie ziet deze er, gevoed door de JSON-data van de query, als volgt uit:
De network chart geeft inzicht in de relaties tussen verschillende type accessoires die samen verkocht worden. Zo is bijvoorbeeld te zien dat fietspompen alleen maar tegelijk met sloten en helmen worden verkocht, maar nooit, en dat is toch verassend, tegelijk met een fietsband. De visualisatie is ook nog eens interactief. Door met de muis over een categorie te bewegen, wordt de focus gelegd op de relaties van deze categorie, zoals hier onder weergegeven (Bike Racks geselecteerd):
TREE CHART De volgende visualisatie is een tree view waarin ik de verkopen per accountmanager, onderverdeeld in verkoopregio s, inzichtelijk maak. De visualisatie is wat minder in het oog springend dan de network chart, maar maakt wel gebruik van een wat complexere JSON-structuur. Ook hier maak ik weer gebruik van een bestaande visualisatie, die ik voed met JSON-data vanuit de Adventureworks-database. De visualisatie verwacht gegevens in de volgende structuur: [ } ] "name": "Top Level", "parent": "null", "children": [ ] "name": "Level 2: A", "parent": "Top Level", "children": [ "name": "Son of A", "parent": "Level 2: A" }, "name": "Daughter of A", "parent": "Level 2: A" } ] }, "name": "Level 2: B", "parent": "Top Level" }
Voor dit voorbeeld wil ik een boomstructuur opbouwen bestaande uit: Verkoopregio Accountmanager Omzet per jaar Hiervoor gebruik ik de volgende query: Select 'SalesDivision' as 'name', (Select SalesTerritory.Name as 'name', -- Accountmanager, voornaam en achternaam (Select Person.FirstName + ' ' + Person.LastName as 'name', -- Omzetgegevens per jaar (Select 'Sales ' + cast(year(salesorderheader.orderdate) as varchar(4)) + ': ' + FORMAT(sum(TotalDue), 'C', 'en-us') as 'name' from Sales.SalesOrderHeader where SalesOrderHeader.SalesPersonID = SalesPerson.BusinessEntityID and SalesOrderHeader.TerritoryID = SalesTerritory.TerritoryID group by SalesOrderHeader.SalesPersonID, year(salesorderheader.orderdate) for JSON PATH) as children from Sales.SalesPerson join Person.Person on SalesPerson.BusinessEntityID = Person.BusinessEntityID where SalesPerson.TerritoryID = SalesTerritory.TerritoryID for JSON PATH) as children from Sales.SalesTerritory as SalesTerritory for JSON PATH ) as children for JSON PATH
Om de geneste structuur te krijgen, maak ik gebruik van subqueries in de select clause. De van newlines ontdane en geformatteerde JSON ziet er dan als volgt uit:...... } "name": "SalesDivision", "children": [ "name": "Australia", "children": [ "name": "Lynn Tsoflias", "children": [ "name": "Sales 2007: $795,341.17" "name": "Sales 2008: $811,100.28" "name": "Canada", "children": [ "name": "Garrett Vargas", "children": [ "name": "Sales 2005: $532,111.88" "name": "Sales 2006: $1,340,860.00" "name": "Sales 2007: $1,554,236.19" "name": "Sales 2008: $642,214.14" "name": "José Saraiva", "children": [ "name": "Sales 2005: $1,170,079.07" "name": "Sales 2006: $1,184,324.69" }, "name": "United Kingdom", "children": [ "name": "Jae Pak"
Wanneer de data gevoed worden aan de visualisatie geeft dit het volgende resultaat:
De sales regions en accountmanagers kunnen worden in- of uitgeklapt, zoals hieronder weergegeven.
KORTOM JSON-support in SQL Server 2016 maakt het eenvoudig om data uit een SQL Server database in JSON-formaat beschikbaar te maken. De functionaliteit om dit te doen, is vrijwel gelijk aan het omzetten van data naar een XML-formaat. BESCHIKBAAR STELLEN DATA In de voorbeelden heb ik de data statisch opgeslagen in een JavaScript-bestand waarmee de visualisatie wordt gevoed. Het is natuurlijk ook denkbaar om een dergelijk bestand periodiek te actualiseren of een (eenvoudige) webservice te definiëren die de JSON-data realtime opvraagt uit de AdventureWorks-database. Omdat SQL Server de data al in JSON-formaat aanlevert, hoeft de webserver niet veel meer te doen dan de output ontdoen van newlines, security-aspecten buiten beschouwing gelaten. EXIT SELF SERVICE BI-TOOL? Visualisaties met behulp van producten als D3.js zijn wat mij betreft geen vervanging van self service BI-producten als Power BI of Tableau. Deze producten bieden veel functionaliteiten die niet in een grafische library als D3.js te vinden zijn. De combinatie van JSON-ondersteuning in SQL Server 2016 en producten als D3.js zijn wel een goede aanvulling op self service BI-producten om bijvoorbeeld niet-alledaagse visualisaties te maken voor een website of externe communicatie. ZELF EENS MEE EXPERIMENTEREN? JE VINDT DE VOORBEELDEN UIT DEZE BLOG HIER. MEER INFORMATIE? NEEM CONTACT OP MET KADENZA MAIL INFO@KADENZA.NL OF BEL +31 (0)35-5394490 WERKEN BIJ KADENZA? KIJK OP PLAYITSMART.NL