本文的学习资源来自网络。(需要科学上网)
https://medium.com/@martijnlentink/azure-table-storage-in-power-bi-with-a-shared-access-signature-token-dde6524b01c
文章标题为
Azure Table Storage in Power BI with a Shared Access Signature Token
学习笔记系列,只写我看完文章的理解和应用。
什么是SASURI数据源?
SAS的全称是Shared Access Signature token(SAS)。
SASURI包括SASURL和SAStoken两部分。SASURL就是https到?之间的信息,SAStoken是token的具体信息。
对于访问Azure(微软云)中存储的数据的两种方式,Account Key和Shared Access Signature token(SAS) 。Access Keys有点像管理员Admin,会给你全部的权限( full access to the Storage account)。
SAS可以提供有过期时限的(expiry date)token。token过期,连接就会失效,不能访问数据。也可以分配控制数据读写删除更新的权限。
直接通过Power BI获取SASURI数据源?
虽然Power BI和Azure都是微软的产品,但是目前不支持Power BI直接连接SAS的数据源。
打开Power BI Desktop,在Get Data中虽然有Azure Tables Storage的选项。但是输入SASURI的链接,会报URL过长的错误。
输入SASURI的URL,超出字符长度错误。
如何获取SASURI数据?
解:M Query里面的Web.Contents 函数和API。
目前,因为数据安全的原因,对于访问SASURI,并不能一次全部获得大量数据(>1000条)。当调用REST API访问SASURI,每次只能获取1000条数据,并得到下次访问需要的continuation token. 通过continuation token,我们可以继续访问下一个1000条数据,当continuation token返回为空的时候,所有的数据就全部取到了。
Power BI取SASURI就是模拟这个过程。其中如何得到不同的continuation token,作者通过观察找到了规律。
不懂也没有关系,其实也不影响后面的实现,只是这个规律有助于理解作者在M query中的function的定义。
实现步骤:
创建空白的Query
打开Power BI, 点击Transform data,转到Power Query Editor页面。在Queries里面,右键新建一个Blank Query。
点击Advanced Editor,把以下代码直接贴上去。什么都不需要修改。
有兴趣的可以读读,这里就是建立一个function来获取数据,并存到一个record list里面。
let Base64Encode = (str as text) => Binary.ToText(Text.ToBinary(str, BinaryEncoding.Base64)), CreateContinuationTokens = (partitionkey as text, rowkey as text) => let tokenPrefix = "1", combineToken = "!", encodedPartition = Base64Encode(partitionkey), encodedRow = Base64Encode(rowkey), nextPartKey = Text.Combine({tokenPrefix, Text.From(Text.Length(encodedPartition)), encodedPartition}, combineToken), nextRowKey = Text.Combine({tokenPrefix, Text.From(Text.Length(encodedRow)), encodedRow}, combineToken), result = [ NextPartitionKey = nextPartKey, NextRowKey = nextRowKey] in result, Func = (url as text, query as record, optional continuationTokens as record) => let queryCombine = if continuationTokens = null then query else Record.Combine({query, continuationTokens}), result = Json.Document(Web.Contents(url, [ Headers=[Accept="application/json;odata=nometadata"], Query = queryCombine ])), resultAsList = result[value], lastInList = List.Last(resultAsList), tokens = CreateContinuationTokens(lastInList[PartitionKey], lastInList[RowKey]), resultList = if List.Count(resultAsList) > 1 then List.Combine({resultAsList, RecursiveTableStorage(url, query, tokens)}) else resultAsList in resultListin Func
3. 修改function函数的名字为RecursiveTableStorage
4. 新建一个blank query用RecursiveTableStorage函数获取数据。
以下代码是文章作者给的例子。URL部分,需要替换到你的SASURL的内容。也就是http到?之前的内容。st,se,sig是在token中用decode获取的。案例代码如下:
let URL = "https://storagename.table.core.windows.net/tablename", queryParameters = [ st = "2019-08-20T10:00:00Z", se = "2030-08-25T10:00:00Z", sp = "r", sv = "2018-05-30", tn = "tablename", sig = "abcdefghijklmnopqrstuvwxyz" ], tabledata = Table.FromRecords(RecursiveTableStorage(URL, queryParameters)), distinctRows = Table.Distinct(tabledata, {"PartitionKey", "RowKey"}),in distinctRows
Decode转换网站https://www.urldecoder.org/
举个例子。上面空里贴的是SAStoken部分的一个内容。下面decode会生成正常的时间内容。然后把decode之后的内容替换掉上面代码相应位置。
如果Azure中存储的数据字段全部都有数据,这个代码没有问题。但是如果某一列某一行有一个空数据,则直接用table.fromrecords就会整行报错。
具体怎么处理参考Tips-6:如何从List of Record生成一个数据表
先调用函数RecursiveTableStorage(URL, queryParameters)来获得list of record,然后再生成表。最后不要忘记Dictinct table。因为每次调用API访问数据的时候,每次会用到最后一行数据来重构continuation token,所以每次最后一行数据都会重复出现。所以要用到distinct.
通过Tip6得到了table之后,用以下代码替换掉 in #"Expanded Column1" 部分
distinctRows = Table.Distinct(#"Expanded Column1" , {"PartitionKey", "RowKey"})in distinctRows
就这样了。不太懂也没有关系。会用就可以了。
谢谢关注,分享。
感谢Sandy同学的review以及Azure数据方向的技术答疑!!谢谢!!