DAX 權(quán)威指南 | 02 DAX簡(jiǎn)介

在快速介紹上一章之后镊讼,現(xiàn)在是時(shí)候開始討論DAX語(yǔ)言了匹舞。在本章中排宰,您將學(xué)習(xí)語(yǔ)言的語(yǔ)法似芝、計(jì)算列和度量值(在Excel術(shù)語(yǔ)中也稱為計(jì)算字段)之間的差異,以及DAX中最常用的函數(shù)额各。

由于這是一個(gè)介紹性的章節(jié)国觉,所以許多函數(shù)沒(méi)有深入介紹吧恃。在本書后面的章節(jié)中虾啦,我們會(huì)更深入地解釋它們。現(xiàn)在,只需要介紹這些函數(shù)并開始研究DAX語(yǔ)言就足夠了傲醉。

2.1 了解DAX計(jì)算

要使用復(fù)雜的公式蝇闭,您需要學(xué)習(xí)DAX的基礎(chǔ)知識(shí),包括語(yǔ)法硬毕、DAX可以處理的不同數(shù)據(jù)類型呻引、基本運(yùn)算符以及如何引用列和表。下面幾節(jié)將討論這些概念吐咳。

使用DAX計(jì)算表中列的值逻悠。您可以聚合、計(jì)算和搜索數(shù)字韭脊,但是最終童谒,所有的計(jì)算都涉及表和列。因此沪羔,要學(xué)習(xí)的第一個(gè)語(yǔ)法是如何引用表中的列饥伊。

一般格式是寫表名,用單引號(hào)括起來(lái)蔫饰,后跟列名括在方括號(hào)中琅豆,如下所示:

'Sales'[Quantity]

如果表名不以數(shù)字開頭,不包含空格篓吁,也不是保留字(如Date或Sum)茫因,則可以省略單引號(hào)。

注意
最好不要在表名中使用空格杖剪。這樣节腐,您可以避免在公式中使用引號(hào),因?yàn)槭褂靡?hào)會(huì)使代碼難以閱讀摘盆。
但是翼雀,請(qǐng)記住,表格的名稱與使用數(shù)據(jù)透視表或任何其他客戶端工具(如Power View)瀏覽模型時(shí)的名稱相同孩擂。因此狼渊,如果您希望報(bào)表中的表名包含空格,則需要在代碼中使用單引號(hào)类垦。

如果要在定義公式的同一表中引用列或度量值狈邑,也可以完全避免編寫表名。因此蚤认,如果在計(jì)算列中或在Sales表中的度量值中寫入米苹,[Quantity]是有效的列引用。即使這種方法在語(yǔ)法上是正確的砰琢,并且當(dāng)您選擇列而不是編寫列時(shí)蘸嘶,用戶界面可能會(huì)建議您使用它良瞧,但是我們強(qiáng)烈建議您不要使用它。這樣的語(yǔ)法使得代碼難以閱讀训唱,因此在DAX表達(dá)式中引用列時(shí)最好始終使用表名褥蚯。

2.1.1 DAX數(shù)據(jù)類型

DAX可以使用七種不同的數(shù)字類型執(zhí)行計(jì)算。在下面的列表中况增,我們顯示了DAX名稱和此數(shù)據(jù)類型的常用名稱赞庶。例如,布爾值在DAX術(shù)語(yǔ)中稱為TRUE / FALSE澳骤。我們更喜歡遵循事實(shí)上的命名標(biāo)準(zhǔn)歧强,我們將它們稱為布爾值。

  • 整數(shù)(整數(shù))
  • 十進(jìn)制數(shù)(小數(shù))
  • 貨幣(貨幣)为肮,內(nèi)部存儲(chǔ)為整數(shù)的固定十進(jìn)制數(shù)
  • 日期(日期時(shí)間)
  • 布爾值(TRUE / FALSE)
  • 文本(字符串)
  • 二進(jìn)制大對(duì)象(二進(jìn)制)

DAX具有強(qiáng)大的類型處理系統(tǒng)誊锭,因此您不必?fù)?dān)心數(shù)據(jù)類型:編寫DAX表達(dá)式時(shí),結(jié)果類型基于表達(dá)式中使用的術(shù)語(yǔ)類型弥锄。如果從DAX表達(dá)式返回的類型不是預(yù)期的類型丧靡,則需要注意這一點(diǎn):那么您必須檢查表達(dá)式本身使用的術(shù)語(yǔ)的數(shù)據(jù)類型。

例如籽暇,如果求和的一個(gè)項(xiàng)是日期温治,那么結(jié)果也是一個(gè)日期;而如果相同的運(yùn)算符與整數(shù)一起使用戒悠,則結(jié)果為整數(shù)熬荆。這稱為運(yùn)算符重載,您可以在圖2-1中看到其行為的示例绸狐,其中OrderDatePlusOneWeek列的計(jì)算方法是將Order Date列的值加7卤恳。正如我們所說(shuō),結(jié)果是一個(gè)日期寒矿。

圖2-1 日期加一個(gè)整數(shù)會(huì)得到日期增加相應(yīng)的天數(shù)突琳。

除了運(yùn)算符重載之外,DAX還會(huì)在運(yùn)算符需要時(shí)自動(dòng)將字符串轉(zhuǎn)換為數(shù)字和將數(shù)字轉(zhuǎn)換為字符串符相。例如拆融,如果使用連接字符串的&運(yùn)算符,DAX會(huì)將其參數(shù)轉(zhuǎn)換為字符串啊终。以下公式:

= 5 & 4

它返回“54”作為字符串镜豹。然而,公式:

= "5" + "4"

返回值為9的整數(shù)結(jié)果蓝牲。

結(jié)果值取決于運(yùn)算符趟脂,而不是源列,它們根據(jù)運(yùn)算符的要求進(jìn)行轉(zhuǎn)換例衍。即使這個(gè)行為看起來(lái)很方便昔期,稍后您將看到在這些自動(dòng)轉(zhuǎn)換過(guò)程中可能發(fā)生的錯(cuò)誤類型已卸。我們建議您避免自動(dòng)轉(zhuǎn)換。如果需要進(jìn)行某種轉(zhuǎn)換镇眷,那么明確地控制其轉(zhuǎn)換會(huì)好得多咬最。為了更明確翎嫡,前面的例子應(yīng)該是:

= VALUE(“5”)+ VALUE(“4”)

對(duì)于使用Excel或其他語(yǔ)言的人來(lái)說(shuō),DAX數(shù)據(jù)類型可能很熟悉。您可以在http://msdn.microsoft.com/en-us/library/gg492146.aspx上找到DAX數(shù)據(jù)類型的規(guī)范橙弱。但是藕筋,分享有關(guān)每種數(shù)據(jù)類型的一些注意事項(xiàng)很有用。

整數(shù)

DAX只有一個(gè)可以存儲(chǔ)64位的整數(shù)數(shù)據(jù)類型圈驼。 DAX中整數(shù)值之間的所有內(nèi)部計(jì)算也使用64位人芽。

十進(jìn)制數(shù)

十進(jìn)制數(shù)始終存儲(chǔ)為雙精度浮點(diǎn)值。

不要將此DAX數(shù)據(jù)類型與Transact-SQL的十進(jìn)制和數(shù)字?jǐn)?shù)據(jù)類型混淆:SQL中DAX十進(jìn)制數(shù)的相應(yīng)數(shù)據(jù)類型是Float绩脆。

貨幣

貨幣數(shù)據(jù)類型存儲(chǔ)固定的十進(jìn)制數(shù)萤厅。它可以表示四個(gè)小數(shù)點(diǎn),并在內(nèi)部存儲(chǔ)為64位整數(shù)值除以10,000靴迫。貨幣數(shù)據(jù)類型之間執(zhí)行的所有計(jì)算只保留小數(shù)點(diǎn)后四位惕味。如果您需要更高的準(zhǔn)確性,則必須轉(zhuǎn)換為十進(jìn)制數(shù)據(jù)類型玉锌。

貨幣數(shù)據(jù)類型的默認(rèn)格式包括貨幣符號(hào)名挥。您還可以將貨幣格式應(yīng)用于整數(shù)和十進(jìn)制數(shù),并且可以使用貨幣數(shù)據(jù)類型不帶貨幣符號(hào)的格式主守。

日期(日期時(shí)間)

DAX將日期存儲(chǔ)在DateTime數(shù)據(jù)類型中禀倔。此格式在內(nèi)部使用浮點(diǎn)數(shù),其中整數(shù)對(duì)應(yīng)于自1899年12月30日以來(lái)的天數(shù)参淫,小數(shù)部分表示時(shí)間的占比救湖。小時(shí)、分鐘和秒轉(zhuǎn)換為一天的小數(shù)部分涎才。因此捎谨,以下表達(dá)式返回當(dāng)前時(shí)間加上一天(恰好24小時(shí)):

= NOW()+ 1

其結(jié)果是明天的同一時(shí)間。如果您只需要使用DateTime的日期部分憔维,請(qǐng)始終記住使用TRUNC來(lái)刪除小數(shù)部分涛救。

閏年錯(cuò)誤
Lotus 1-2-3是1983年發(fā)布的流行電子表格,它在處理DateTime數(shù)據(jù)類型時(shí)存在錯(cuò)誤业扒。它認(rèn)為1900年是閏年检吆,即使它不是(一個(gè)世紀(jì)的最后一年只有前兩個(gè)數(shù)字可以除以4而沒(méi)有余數(shù))。那時(shí)程储,第一版Excel的開發(fā)團(tuán)隊(duì)故意復(fù)制了這個(gè)bug蹭沛,以保持與Lotus 1-2-3的兼容性臂寝。從那時(shí)起,由于兼容性摊灭,每個(gè)新版本的Excel都將bug作為一項(xiàng)功能維護(hù)咆贬。
現(xiàn)在,在2015年帚呼,該錯(cuò)誤仍然在DAX中掏缎,為了向后兼容Excel而引入。錯(cuò)誤的存在(我們應(yīng)該稱之為功能嗎煤杀?)可能會(huì)導(dǎo)致1900年3月1日之前的錯(cuò)誤眷蜈。因此,按照設(shè)計(jì)沈自,DAX的第一個(gè)官方支持日期是1900年3月1日酌儒。在該日期之前執(zhí)行的日期計(jì)算可能會(huì)導(dǎo)致錯(cuò)誤,應(yīng)被視為不準(zhǔn)確枯途。
如果需要在1900年之前執(zhí)行計(jì)算忌怎,則應(yīng)使用數(shù)學(xué)運(yùn)算1900之后的日期,執(zhí)行計(jì)算酪夷,然后將日期移回時(shí)間榴啸。

布爾值

布爾數(shù)據(jù)類型用于表示邏輯條件。例如捶索,由以下表達(dá)式定義的計(jì)算列的類型為布爾值:

= Sales[Unit Price] > Sales[Unit Cost]

您也可以將布爾數(shù)據(jù)類型看作數(shù)字插掂,其中TRUE等于1,F(xiàn)ALSE等于0腥例。有時(shí)候辅甥,這可能用于排序,因?yàn)門RUE> FALSE燎竖。

文本(字符串)

DAX中的每個(gè)字符串都存儲(chǔ)為Unicode字符串璃弄,其中每個(gè)字符以16位存儲(chǔ)。默認(rèn)情況下构回,字符串之間的比較是不區(qū)分大小寫的夏块,因此兩個(gè)字符串“Power Pivot”和“POWER PIVOT”被認(rèn)為是相等的。

2.1.2 DAX 運(yùn)算符

了解了運(yùn)算符在確定表達(dá)式類型中的重要性之后纤掸,您現(xiàn)在可以在表2-1中看到DAX中可用的運(yùn)算符列表脐供。
暫略

此外,邏輯運(yùn)算符也可以作為DAX函數(shù)使用借跪,其語(yǔ)法與Excel非常相似政己。例如,你可以寫:

AND ( [CountryRegion] = "USA", [Quantity] > 0 )
OR ( [CountryRegion] = "USA", [Quantity] > 0 )

這相當(dāng)于:

[CountryRegion] = "USA" && [Quantity] > 0
[CountryRegion] = "USA" || [Quantity] > 0

當(dāng)您必須編寫復(fù)雜的條件時(shí)掏愁,使用函數(shù)會(huì)優(yōu)與邏輯運(yùn)算符歇由。實(shí)際上卵牍,在格式化大部分代碼時(shí),函數(shù)比運(yùn)算符更容易格式化和讀取沦泌。但是糊昙,函數(shù)的一個(gè)主要缺點(diǎn)是一次只能傳入兩個(gè)參數(shù)。如果有兩個(gè)以上的條件需要計(jì)算谢谦,則需要嵌套函數(shù)释牺。

2.2 理解計(jì)算列和度量值

現(xiàn)在您已經(jīng)了解了DAX語(yǔ)法的基礎(chǔ)知識(shí),接下來(lái)您需要學(xué)習(xí)DAX中最重要的概念之一:計(jì)算列和度量值之間的差異他宛。即使它們看起來(lái)很相似船侧,因?yàn)槟憧梢酝ㄟ^(guò)兩種方式進(jìn)行一些計(jì)算欠气,但實(shí)際上它們是非常不同的厅各,理解兩者的區(qū)別是解開DAX力量的關(guān)鍵。

2.2.1 計(jì)算列

例如预柒,如果要在Excel中創(chuàng)建計(jì)算列队塘,只需移動(dòng)到表的最后一列(名為“添加列”),然后開始編寫公式宜鸯。當(dāng)然憔古,其他實(shí)現(xiàn)DAX的工具可能具有不同的用戶界面。您可以在公式欄中創(chuàng)建DAX表達(dá)式淋袖,在編寫表達(dá)式時(shí)智能感知可以幫助您鸿市。

計(jì)算列與表中的任何其他列一樣,可以在數(shù)據(jù)透視表或任何其他報(bào)表的行即碗、列焰情、篩選器或值中使用它。如果需要剥懒,還可以使用計(jì)算列來(lái)定義關(guān)系内舟。為計(jì)算列定義的DAX表達(dá)式在它所屬的表的當(dāng)前行上下文中操作。對(duì)列的任何引用都返回當(dāng)前行的該列的值初橘。您不能直接訪問(wèn)其他行的值验游。

備注:
正如您稍后將看到的,一些DAX函數(shù)聚合整個(gè)表的列值保檐。獲取行子集值的唯一方法是使用返回表然后對(duì)其進(jìn)行操作的DAX函數(shù)耕蝉。通過(guò)這種方式,您可以為一系列行聚合列值夜只,并可能通過(guò)篩選僅由一行組成的表對(duì)不同的行進(jìn)行操作垒在。您將在第4章“理解計(jì)算上下文”中學(xué)習(xí)有關(guān)此主題的更多信息。

您需要記住有關(guān)計(jì)算列的一個(gè)重要概念是它們?cè)跀?shù)據(jù)庫(kù)處理期間計(jì)算盐肃,然后存儲(chǔ)在模型中爪膊。如果您習(xí)慣于SQL計(jì)算列(非持久化)权悟,這可能看起來(lái)很奇怪,這些列在查詢時(shí)計(jì)算并且不使用內(nèi)存推盛。然而峦阁,在Tabular中,所有計(jì)算列占用存儲(chǔ)器中的空間并且在表處理期間計(jì)算耘成。

當(dāng)您創(chuàng)建非常復(fù)雜的計(jì)算列時(shí)榔昔,這種行為非常有用。計(jì)算它們所需的時(shí)間總是處理時(shí)間而不是查詢時(shí)間瘪菌,從而帶來(lái)更好的用戶體驗(yàn)撒会。然而,您始終必須記住計(jì)算列使用珍貴的RAM师妙。例如诵肛,如果您有一個(gè)計(jì)算列的復(fù)雜公式,您可能想要將不同中間列中的計(jì)算步驟分開默穴。雖然這種方法在項(xiàng)目開發(fā)過(guò)程中很有用怔檩,但它在生產(chǎn)中是一個(gè)壞習(xí)慣,因?yàn)槊總€(gè)中間計(jì)算都存儲(chǔ)在RAM中并浪費(fèi)寶貴的空間蓄诽。

2.2.2 度量值

在DAX模型中還有另一種定義計(jì)算的方法薛训,當(dāng)您不想為每一行計(jì)算值,而是希望聚合表中許多行的值時(shí)仑氛,這種方法很有用乙埃。我們稱之為度量值。

例如:您可以在Sales表中定義GrossMargin列以計(jì)算毛利:

Sales[GrossMargin] = Sales[SalesAmount] - Sales[TotalProductCost]

但是锯岖,如果您想將毛利率顯示為銷售額的百分比介袜,會(huì)發(fā)生什么?您可以使用以下公式創(chuàng)建計(jì)算列:

Sales[GrossMarginPct] = Sales[GrossMargin] / Sales[SalesAmount]

此公式計(jì)算行級(jí)別的正確值嚎莉,如圖2-2所示米酬。

圖2-2 GrossMarginPct列以百分比形式顯示GrossMargin,逐行計(jì)算趋箩。

然而赃额,當(dāng)您計(jì)算總計(jì)的百分比時(shí),您不能依賴計(jì)算列叫确。實(shí)際上跳芳,您需要用毛利總計(jì)除以銷售額總計(jì)。因此竹勉,在這種情況下飞盆,您需要計(jì)算聚合的比率,而不能使用計(jì)算列的聚合。換句話說(shuō)吓歇,您計(jì)算總計(jì)的比率孽水,而不是比率的總計(jì)。

GrossMarginPct的正確實(shí)現(xiàn)是用度量值:

Sales[GrossMarginPct] := 
SUM ( Sales[GrossMargin] ) / SUM(Sales[SalesAmount] )

但是城看,正如我們已經(jīng)說(shuō)過(guò)的女气,您無(wú)法將其輸入到計(jì)算列中。如果需要對(duì)聚合值進(jìn)行操作而不是逐行操作测柠,則必須創(chuàng)建度量值炼鞠。您可能已經(jīng)注意到我們使用:=來(lái)定義度量值,而不是等號(hào)(=)轰胁。這是我們?cè)诒緯惺褂玫臉?biāo)準(zhǔn)谒主,以便更容易在代碼中區(qū)分度量值和計(jì)算列。

度量值和計(jì)算列都使用DAX表達(dá)式赃阀,不同之處在于上下文霎肯。度量值是在數(shù)據(jù)透視表或DAX查詢的單元格上下文中計(jì)算的,而計(jì)算列是在它所屬的表的行級(jí)別計(jì)算的凹耙。單元格的上下文(本書后面姿现,您了解到這是一個(gè)篩選器上下文)取決于數(shù)據(jù)透視表中的用戶選擇或DAX查詢肠仪。因此肖抱,當(dāng)您在度量中使用SUM(Sales[SalesAmount])時(shí),表示在此單元格下計(jì)算所有單元格的和异旧,而在計(jì)算列中使用Sales [SalesAmount]時(shí)意述,則表示當(dāng)前行的SalesAmount列的值。

需要在表格中定義度量值吮蛹。這是DAX語(yǔ)言的要求之一荤崇。但是,該度量值并不屬于該表潮针。實(shí)際上术荤,您可以將度量值從一個(gè)表移動(dòng)到另一個(gè)表,而不會(huì)丟失其功能每篷。

計(jì)算列和度量值之間的差異
計(jì)算列和度量值之間存在很大差異瓣戚,即使它們看起來(lái)非常相似。計(jì)算列在數(shù)據(jù)刷新期間計(jì)算焦读,并將當(dāng)前行作為上下文子库,它不依賴于數(shù)據(jù)透視表上的用戶活動(dòng)。度量值對(duì)當(dāng)前上下文定義的數(shù)據(jù)進(jìn)行聚合操作矗晃。例如仑嗅,在數(shù)據(jù)透視表中,根據(jù)單元格的坐標(biāo)篩選源表,并使用這些篩選器聚合和計(jì)算數(shù)據(jù)仓技。換句話說(shuō)鸵贬,度量值總是在計(jì)算上下文下對(duì)數(shù)據(jù)進(jìn)行聚合操作,因此默認(rèn)執(zhí)行模式不引用任何單行脖捻。計(jì)算上下文將在第4章中進(jìn)一步解釋恭理。

在計(jì)算列和度量值之間進(jìn)行選擇

現(xiàn)在您已經(jīng)看到了計(jì)算列和度量值之間的差異,您可能想知道何時(shí)使用這個(gè)而不是另一個(gè)郭变。有時(shí)兩者都是一種選擇颜价,但在大多數(shù)情況下,您的計(jì)算需求決定了您的選擇诉濒。

只要您想執(zhí)行以下操作周伦,就必須定義計(jì)算列:

  • 將計(jì)算結(jié)果放在Excel切片器中,或在數(shù)據(jù)透視表中的行或列中顯示結(jié)果(與“值”區(qū)域相對(duì))未荒,或?qū)⒔Y(jié)果用作DAX查詢中的篩選條件专挪。
  • 定義嚴(yán)格綁定到當(dāng)前行的表達(dá)式。(例如片排,價(jià)格*數(shù)量不能平均或兩列的總和寨腔。)
  • 分類文本或數(shù)字。(例如率寡,度量值的范圍迫卢,客戶的年齡范圍,例如0-18,18-25等冶共。)

但是乾蛤,每當(dāng)要顯示反映用戶選擇的結(jié)果,并在數(shù)據(jù)透視表的值區(qū)域中查看它們時(shí)捅僵,必須定義度量值家卖,例如:

  • 計(jì)算數(shù)據(jù)透視表選擇的利潤(rùn)百分比時(shí)。
  • 當(dāng)您計(jì)算產(chǎn)品與所有產(chǎn)品的比率庙楚,并保持按年份和地區(qū)篩選器時(shí)上荡。

即使在這些情況下需要使用不同的DAX表達(dá)式,也可以使用計(jì)算列和度量值表達(dá)一些計(jì)算馒闷。
例如酪捡,您可以將GrossMargin定義為計(jì)算列:

Sales[GrossMargin] = Sales[SalesAmount] - Sales[TotalProductCost]

但它也可以定義為一個(gè)度量值:

[GrossMargin] := SUM ( Sales[SalesAmount] ) – SUM (Sales[TotalProductCost] )

我們建議您在這種情況下使用度量值,因?yàn)槎攘恐翟诓樵儠r(shí)進(jìn)行計(jì)算窜司,它不會(huì)占用內(nèi)存和磁盤空間沛善,但這僅在大型數(shù)據(jù)集中非常重要。當(dāng)模型的大小不是問(wèn)題時(shí)塞祈,您可以使用您更熟悉的方法金刁。

交叉引用
很明顯,一個(gè)度量值可以引用一個(gè)或多個(gè)計(jì)算列。相反的情況可能不那么直觀尤蛮。計(jì)算列可以引用度量值:以這種方式媳友,它強(qiáng)制計(jì)算當(dāng)前行定義的上下文的度量值。此操作將度量結(jié)果轉(zhuǎn)換并合并到列中产捞,該列不受用戶操作的影響醇锚。顯然,只有某些操作才能產(chǎn)生有意義的結(jié)果坯临,因?yàn)橥ǔG闆r焊唬,度量值的計(jì)算強(qiáng)烈依賴于用戶在數(shù)據(jù)透視表中所做的選擇。

2.3 變量

在編寫DAX表達(dá)式時(shí)看靠,可以通過(guò)使用變量避免重復(fù)相同的表達(dá)式赶促。例如,看以下表達(dá)式:

VAR TotalSales = SUM ( Sales[SalesAmount] )
RETURN ( TotalSales - SUM ( Sales[TotalProductCost] ) ) / TotalSales

您可以定義許多變量挟炬,它們是您定義它們的表達(dá)式的本地變量鸥滨。變量對(duì)于簡(jiǎn)化代碼非常有用,因?yàn)槟梢员苊庵貜?fù)相同的子表達(dá)式谤祖。變量使用延遲計(jì)算來(lái)計(jì)算婿滓。這意味著,如果您定義了一個(gè)變量粥喜,由于任何原因凸主,該變量在您的代碼中沒(méi)有使用,那么該變量將永遠(yuǎn)不會(huì)被求值容客。如果需要計(jì)算秕铛,則只發(fā)生一次:變量的后續(xù)用法將讀取先前計(jì)算的值。因此缩挑,當(dāng)您多次使用復(fù)雜表達(dá)式時(shí),它們也可用作優(yōu)化技術(shù)鬓梅。

此外供置,正如您將在第4章中學(xué)到的,變量非常有用绽快,因?yàn)樗鼈兪褂枚x時(shí)的上下文而不是使用變量時(shí)的上下文芥丧。

2.4 處理DAX表達(dá)式中的錯(cuò)誤

現(xiàn)在您已經(jīng)了解了DAX語(yǔ)法的一些基礎(chǔ)知識(shí),接下來(lái)將學(xué)習(xí)如何優(yōu)雅地處理無(wú)效計(jì)算坊罢。DAX表達(dá)式可能包含無(wú)效計(jì)算续担,因?yàn)樗玫臄?shù)據(jù)對(duì)公式無(wú)效。

例如活孩,您可能有零除物遇,或不是數(shù)字的列值,并且在算術(shù)運(yùn)算中使用,例如乘法询兴。您必須了解默認(rèn)情況下如何處理這些錯(cuò)誤乃沙,以及如果您需要一些特殊處理,如何攔截這些錯(cuò)誤诗舰。

在您學(xué)習(xí)如何處理錯(cuò)誤之前警儒,有必要花一些時(shí)間來(lái)描述在DAX公式計(jì)算期間可能出現(xiàn)的各種錯(cuò)誤。他們是:

  • 轉(zhuǎn)換錯(cuò)誤
  • 算術(shù)運(yùn)算錯(cuò)誤
  • 空值或缺少值

2.4.1 轉(zhuǎn)換錯(cuò)誤

第一類錯(cuò)誤是轉(zhuǎn)換錯(cuò)誤眶根。正如您在本章前面看到的蜀铲,只要運(yùn)算符需要,DAX就會(huì)自動(dòng)在字符串和數(shù)字之間轉(zhuǎn)換值属百。用例子回顧一下這個(gè)概念蝙茶,下面這些都是有效的DAX表達(dá)式:

"10" + 32 = 42
"10" & 32 = "1032"
10 & 32 = "1032"
DATE (2010,3,25) = 3/25/2010
DATE (2010,3,25) + 14 = 4/8/2010
DATE (2010,3,25) & 14 = "3/25/201014"

這些公式總是正確的,因?yàn)樗鼈兪怯贸?shù)值運(yùn)算的诸老。但是隆夯,如果VatCode是一個(gè)字符串,那么下面的代碼是什么呢别伏?

SalesOrders[VatCode] + 100

因?yàn)榇饲蠛偷牡谝粋€(gè)操作數(shù)是一個(gè)列蹄衷,在本列中是一個(gè)Text數(shù)據(jù)類型,您必須確保DAX可以將該列中的所有值轉(zhuǎn)換為數(shù)字厘肮。如果DAX無(wú)法轉(zhuǎn)換某些內(nèi)容以滿足運(yùn)算符的需求愧口,則會(huì)產(chǎn)生轉(zhuǎn)換錯(cuò)誤。下面是一些典型的情況:

"1 + 1" + 0 = 無(wú)法將字符串類型的值 "1+1" 轉(zhuǎn)換為數(shù)值
DATEVALUE ("25/14/2010") = 類型不匹配

要避免這些錯(cuò)誤类茂,您需要在DAX表達(dá)式中添加錯(cuò)誤檢測(cè)邏輯耍属,以攔截錯(cuò)誤并始終返回有意義的結(jié)果。

2.4.2 算術(shù)運(yùn)算錯(cuò)誤

第二類錯(cuò)誤是算術(shù)運(yùn)算錯(cuò)誤巩检,例如除以零或負(fù)數(shù)的平方根厚骗。這些不是與轉(zhuǎn)換相關(guān)的錯(cuò)誤:每當(dāng)您嘗試調(diào)用函數(shù)或使用具有無(wú)效值的運(yùn)算符時(shí),DAX都會(huì)提示這些錯(cuò)誤兢哭。

除零需要特殊處理领舰,因?yàn)樗男袨榉绞讲皇欠浅V庇^(數(shù)學(xué)家可能除外)。當(dāng)你用一個(gè)數(shù)字除以0時(shí)迟螺,DAX通常會(huì)返回特殊值Infinity冲秽。此外,當(dāng)遇到0除以0或無(wú)窮大除以無(wú)窮大的特殊情況矩父,DAX返回特殊的NaN(非數(shù)字)值锉桑。

由于這是一種奇怪的行為,所以值得在表2-2中進(jìn)行總結(jié)窍株。
暫略
表2-2除以零的特殊結(jié)果值民轴。

值得注意的是攻柠,Infinity和NaN不是錯(cuò)誤,而是DAX中的特殊值杉武。實(shí)際上辙诞,如果將數(shù)字除以Infinity,則表達(dá)式不會(huì)生成錯(cuò)誤但返回0:

9954 /(7/0)= 0

除了這種特殊情況轻抱,當(dāng)使用錯(cuò)誤的參數(shù)調(diào)用函數(shù)時(shí)飞涂,DAX還會(huì)返回算術(shù)錯(cuò)誤,比如負(fù)數(shù)的平方根:

SQRT ( -1 ) = 函數(shù)'SQRT'的參數(shù)具有錯(cuò)誤的數(shù)據(jù)類型或結(jié)果太大或太小

如果DAX檢測(cè)到這樣的錯(cuò)誤祈搜,它會(huì)阻止表達(dá)式的任何進(jìn)一步計(jì)算较店,并引發(fā)錯(cuò)誤。您可以使用ISERROR函數(shù)來(lái)檢查表達(dá)式是否會(huì)導(dǎo)致錯(cuò)誤容燕,您將在本章后面使用它梁呈。

最后,請(qǐng)記住蘸秘,NaN等特殊值在Power Pivot或Visual Studio窗口中以這種方式顯示官卡,但是當(dāng)某些客戶端工具(如Excel數(shù)據(jù)透視表)顯示時(shí),它們可能會(huì)顯示為錯(cuò)誤醋虏。而且寻咒,這些特殊值將被錯(cuò)誤檢測(cè)函數(shù)檢測(cè)為錯(cuò)誤。

2.4.3 空值或缺失值

我們檢查的第三個(gè)類別不是特定的錯(cuò)誤條件颈嚼,而是存在空值毛秘,當(dāng)將這些空值與計(jì)算中的其他元素組合時(shí),可能會(huì)導(dǎo)致意外返回或計(jì)算錯(cuò)誤阻课。您需要了解DAX如何處理這些特殊值叫挟。

DAX使用BLANK以相同的方式處理缺失值、空值或空單元格限煞。BLANK不是一個(gè)真正的值抹恳,而是一種識(shí)別這些條件的特殊方法。您可以通過(guò)調(diào)用BLANK函數(shù)獲取DAX表達(dá)式中的BLANK晰骑,該函數(shù)與空字符串不同适秩。例如,以下表達(dá)式始終返回一個(gè)空白值硕舆,該值將在數(shù)據(jù)透視表中顯示為空單元格:

= BLANK ()

就其本身而言,這個(gè)表達(dá)式是無(wú)用的骤公,但是每當(dāng)你要返回空值時(shí)抚官,BLANK函數(shù)就會(huì)變得有用了。例如阶捆,您可能希望顯示一個(gè)空單元格而不是0凌节,如下面的表達(dá)式計(jì)算銷售交易的總折扣钦听,如果折扣為0則將單元格留空:

= IF ( Sales[DiscountPerc] = 0, BLANK (), Sales[DiscountPerc] * Sales[Amount] )

BLANK本身不是錯(cuò)誤,而是空值倍奢。因此朴上,包含BLANK的表達(dá)式可能會(huì)返回值或空白,具體取決于所需的計(jì)算卒煞。例如痪宰,只要Sales [Amount]為BLANK,以下表達(dá)式就會(huì)返回BLANK:

= 10 * Sales[Amount]

換句話說(shuō)畔裕,當(dāng)一項(xiàng)或兩項(xiàng)都為空時(shí)衣撬,算術(shù)乘積的結(jié)果就是BLANK。 BLANK在DAX表達(dá)式中的傳遞在其他幾個(gè)算術(shù)和邏輯運(yùn)算中也會(huì)發(fā)生扮饶,如以下示例所示:

BLANK () + BLANK () = BLANK ()
10 * BLANK () = BLANK ()
BLANK () / 3 = BLANK ()
BLANK () / BLANK () = BLANK ()
BLANK () || BLANK () = FALSE
BLANK () && BLANK () = FALSE
BLANK () = BLANK () = TRUE

但是具练,表達(dá)式結(jié)果中的BLANK傳遞并不適用于所有公式。有些計(jì)算不會(huì)傳遞BLANK甜无,而是根據(jù)公式的其他項(xiàng)返回一個(gè)值扛点。這些示例包括加法、減法岂丘、BLANK除法以及BLANK和有效值之間的邏輯運(yùn)算陵究。在以下表達(dá)式中,您可以看到這些條件的一些示例及其結(jié)果:

BLANK () - 10 = -10
18 + BLANK () = 18
4 / BLANK () = Infinity
0 / BLANK () = NaN
FALSE || BLANK () = FALSE
FALSE && BLANK () = FALSE
TRUE || BLANK () = TRUE
TRUE && BLANK () = FALSE

Excel和SQL Excel中的空值
Excel用不同方法的處理空值元潘。在Excel中畔乙,在求和或乘法中使用的所有空值都被認(rèn)為是0,但如果它們是除法或邏輯表達(dá)式的一部分翩概,則返回一個(gè)錯(cuò)誤牲距。
在SQL中,值在表達(dá)式中傳遞的方式與在DAX中使用空值傳遞的方式不同钥庇。正如您在前面的示例中所看到的牍鞠,DAX表達(dá)式中BLANK的存在并不總是會(huì)產(chǎn)生BLANK結(jié)果,而SQL中存在的NULL通常會(huì)為整個(gè)表達(dá)式求值為NULL评姨。

了解DAX表達(dá)式中空值或缺失值的行為以及使用BLANK在計(jì)算中返回空單元格也是控制DAX表達(dá)式結(jié)果的重要技巧难述。當(dāng)您檢測(cè)到錯(cuò)誤的值或其他錯(cuò)誤時(shí),通惩戮洌可以使用BLANK胁后,這將在下一節(jié)中介紹。

2.4.4 攔截錯(cuò)誤

現(xiàn)在您已經(jīng)看到了可能發(fā)生的各種錯(cuò)誤嗦枢,您將學(xué)習(xí)攔截錯(cuò)誤并糾正錯(cuò)誤的方法攀芯,或者至少顯示包含一些有意義信息的錯(cuò)誤消息。 DAX表達(dá)式中存在的錯(cuò)誤通常取決于表達(dá)式本身引用的表和列中包含的值文虏。因此侣诺,您可能希望控制這些錯(cuò)誤條件的存在并返回錯(cuò)誤消息殖演。標(biāo)準(zhǔn)方法是檢查表達(dá)式是否返回錯(cuò)誤,如果是年鸳,則將錯(cuò)誤替換為消息或默認(rèn)值趴久。這有幾個(gè)DAX函數(shù)可用于此方法。

第一個(gè)是IFERROR函數(shù)搔确,它與IF函數(shù)非常相似彼棍,但它不計(jì)算布爾條件,而是檢查表達(dá)式是否返回錯(cuò)誤妥箕。您可以在此處看到IFERRROR函數(shù)的兩種典型用法:

= IFERROR ( Sales[Quantity] * Sales[Price], BLANK () )
= IFERROR ( SQRT ( Test[Omega] ), BLANK () )

在第一個(gè)表達(dá)式中滥酥,如果Sales [Quantity]或Sales [Price]是無(wú)法轉(zhuǎn)換為數(shù)字的字符串,則返回的表達(dá)式為空值畦幢;否則返回?cái)?shù)量和價(jià)格的乘積坎吻。
在第二個(gè)表達(dá)式中,每當(dāng)TestΩ列包含負(fù)數(shù)時(shí)宇葱,結(jié)果為空單元格瘦真。

當(dāng)您以這種方式使用IFERROR時(shí),您遵循需要使用ISERROR和IF的更一般的模式:

= IF (
      ISERROR ( Sales[Quantity] * Sales[Price] ),
      BLANK (),
      Sales[Quantity] * Sales[Price]
)

= IF (
      ISERROR ( SQRT ( Test[Omega] ) ),
      BLANK (),
      SQRT ( Test[Omega] )
)

當(dāng)返回的表達(dá)式與測(cè)試的錯(cuò)誤相同時(shí)黍瞧,應(yīng)該使用IFERROR诸尽;您不必在兩個(gè)地方復(fù)制表達(dá)式,并且生成的公式在將來(lái)發(fā)生更改時(shí)更易于閱讀印颤,也更安全您机。但是,當(dāng)您想要返回不同表達(dá)式的結(jié)果時(shí)年局,應(yīng)該使用IF际看。例如,您可以檢測(cè)SQRT的參數(shù)是否為有效參數(shù)矢否,僅計(jì)算正數(shù)的平方根仲闽,并為負(fù)數(shù)計(jì)算BLANK:

= IF ( Test[Omega] >= 0, SQRT ( Test[Omega] ), BLANK () )

考慮到IF語(yǔ)句的第三個(gè)參數(shù)具有默認(rèn)值BLANK,您還可以編寫相同的表達(dá)式:

= IF ( Test[Omega] >= 0, SQRT ( Test[Omega] ) )

一個(gè)特例是針對(duì)空值的測(cè)試僵朗。 ISBLANK函數(shù)檢測(cè)空值條件赖欣,如果參數(shù)為BLANK則返回TRUE。
這一點(diǎn)很重要验庙,尤其是當(dāng)缺失的值與設(shè)置為0的值含義不同時(shí)亲茅。在以下示例中横堡,我們計(jì)算銷售交易的運(yùn)輸成本组题,如果交易沒(méi)有指定重量抒和,則使用產(chǎn)品的默認(rèn)運(yùn)費(fèi):

= IF (
    ISBLANK ( Sales[Weight] ),
    Sales[DefaultShippingCost],
    Sales[Weight] * Sales[ShippingPrice]
)

如果我們只是將產(chǎn)品重量和運(yùn)輸價(jià)格相乘,那么對(duì)于所有缺少重量數(shù)據(jù)的銷售交易汗菜,我們將得到一個(gè)空成本让禀。

盡量避免使用錯(cuò)誤處理函數(shù)
即使現(xiàn)在尚未討論DAX代碼優(yōu)化,您需要注意錯(cuò)誤處理函數(shù)可能會(huì)在代碼中產(chǎn)生嚴(yán)重的性能問(wèn)題陨界。并不是說(shuō)他們自己運(yùn)算很慢巡揍。問(wèn)題是當(dāng)錯(cuò)誤發(fā)生時(shí),DAX引擎不能在代碼中使用優(yōu)化的路徑菌瘪。在大多數(shù)情況下腮敌,檢查操作數(shù)以查找可能的錯(cuò)誤比使用錯(cuò)誤處理引擎更有效。例如俏扩,不寫這個(gè):

= IFERROR (
SQRT ( Test[Omega] ),
BLANK ()
)

寫這個(gè)更好:

= IF (
Test[Omega] >= 0,
SQRT ( Test[Omega] ),
BLANK ()
)

第二個(gè)表達(dá)式不需要檢測(cè)錯(cuò)誤,它比前一個(gè)更快录淡。當(dāng)然捌木,這是一個(gè)通用方法。有關(guān)詳細(xì)說(shuō)明嫉戚,請(qǐng)參見第16章“優(yōu)化DAX”刨裆。

2.5 格式化DAX代碼

在繼續(xù)解釋DAX語(yǔ)言之前,有必要介紹DAX的一個(gè)非常重要的方面彬檀,即格式化代碼帆啃。 DAX是一種函數(shù)式語(yǔ)言,這意味著無(wú)論它有多復(fù)雜窍帝,DAX表達(dá)式總是帶有一些參數(shù)的單個(gè)函數(shù)調(diào)用努潘。代碼的復(fù)雜性轉(zhuǎn)化為您用作最外層函數(shù)的參數(shù)的表達(dá)式的復(fù)雜性。

由于這個(gè)原因坤学,通撤枥ぃ可以看到超過(guò)10行或更多的表達(dá)式∮德停看到一個(gè)20行的DAX表達(dá)并不奇怪贴膘,你會(huì)熟悉它。然而略号,隨著公式的長(zhǎng)度和復(fù)雜性的增長(zhǎng)刑峡,學(xué)習(xí)如何格式化代碼,使其變得可讀玄柠,這是非常重要的突梦。

格式化DAX代碼沒(méi)有“官方”標(biāo)準(zhǔn),但我們認(rèn)為描述我們?cè)诖a中使用的標(biāo)準(zhǔn)非常重要羽利。它可能不完美宫患,你可能更喜歡不同的東西,我們并不反對(duì)这弧。你唯一需要記住的是:“格式化你的代碼娃闲,永遠(yuǎn)不要把所有內(nèi)容寫在一行上虚汛,否則你馬上會(huì)遇到麻煩』拾铮”

為了理解格式化代碼的重要性卷哩,我們展示了一個(gè)計(jì)算時(shí)間智能的公式。這是一個(gè)有點(diǎn)復(fù)雜的公式属拾,但肯定不是您將編寫的最復(fù)雜的公式将谊。如果不以某種方式對(duì)表達(dá)式進(jìn)行格式化,則表達(dá)式如下所示:

IF (COUNTX (BalanceDate, CALCULATE (COUNT( Balances[Balance]
), ALLEXCEPT ( Balances,
BalanceDate[Date] ))) > 0, SUMX (ALL ( Balances[Account] ),
CALCULATE (SUM(
Balances[Balance] ), LASTNONBLANK (DATESBETWEEN
(BalanceDate[Date], BLANK(),LASTDATE(
BalanceDate[Date] )), CALCULATE ( COUNT( Balances[Balance]
))))), BLANK ())

試圖理解這個(gè)公式計(jì)算的內(nèi)容幾乎是不可能的渐白,因?yàn)槟悴恢滥膫€(gè)是最外面的函數(shù)尊浓,以及DAX如何計(jì)算不同的參數(shù)來(lái)創(chuàng)建完整的執(zhí)行流程。我們已經(jīng)看到了太多的客戶以這種方式編寫公式纯衍,他們?cè)谀承r(shí)候?qū)で髱椭俺荩瑸槭裁垂綍?huì)返回不正確的結(jié)果。你猜怎么著托酸?我們要做的第一件事就是格式化表達(dá)式褒颈,然后才開始研究它。

相同的表達(dá)式励堡,格式化后谷丸,看起來(lái)是這樣的:

=
IF (
    COUNTX (
        BalanceDate,
        CALCULATE (
            COUNT ( Balances[Balance] ),
            ALLEXCEPT ( Balances, BalanceDate[Date] )
        )
    ) > 0,
    SUMX (
        ALL ( Balances[Account] ),
        CALCULATE (
            SUM ( Balances[Balance] ),
            LASTNONBLANK (
                DATESBETWEEN ( BalanceDate[Date], BLANK (), LASTDATE ( BalanceDate[Date] ) ),
                CALCULATE ( COUNT ( Balances[Balance] ) )
            )
        )
    ),
    BLANK ()
)

代碼是相同的,但這次更容易查看IF的三個(gè)參數(shù)应结,最重要的是刨疼,遵循縮進(jìn)行自然產(chǎn)生的塊,以及它們?nèi)绾谓M成完整的執(zhí)行流程鹅龄。是的揩慕,代碼仍然難以閱讀,但現(xiàn)在問(wèn)題是DAX扮休,而不是格式迎卤。

DAXFormatter.com
我們創(chuàng)建了一個(gè)專門用于格式化DAX代碼的網(wǎng)站。我們自己做了玷坠,因?yàn)楦袷交a是一項(xiàng)耗時(shí)的操作蜗搔,我們不想花時(shí)間來(lái)格式化我們編寫的每個(gè)公式。一旦該工具工作八堡,我們決定將其捐贈(zèng)給公共領(lǐng)域樟凄,以便用戶可以格式化他們自己的DAX代碼(順便說(shuō)一句,我們已經(jīng)能夠以這種方式推廣我們的格式化規(guī)則)兄渺。
您可以在www.daxformatter.com上找到它缝龄。用戶界面很簡(jiǎn)單:只需復(fù)制DAX代碼,單擊FORMAT,頁(yè)面刷新即可顯示代碼格式正確的版本叔壤,然后可以在原始窗口中復(fù)制和粘貼瞎饲。

這是我們用來(lái)格式化DAX的一組規(guī)則:

  • IF、COUNTX百新、CALCULATE等關(guān)鍵字始終使用空格分隔任何其他術(shù)語(yǔ)企软,并且始終以大寫字母書寫。
  • 所有列引用都以TableName [ColumnName]的形式編寫饭望,表名和開始方括號(hào)之間沒(méi)有空格。始終包含表名形庭。
  • 所有度量引用都以[MeasureName]的形式編寫铅辞,沒(méi)有任何表名。
  • 逗號(hào)總是后面跟著一個(gè)空格萨醒,前面沒(méi)有空格斟珊。
  • 如果公式適合一行,則不需要應(yīng)用其他規(guī)則富纸。
  • 如果公式不適合單行囤踩,則:
    • 函數(shù)名稱單獨(dú)顯示一行,并帶有左括號(hào)晓褪。
    • 所有參數(shù)都在單獨(dú)的行中堵漱,用四個(gè)空格縮進(jìn),并在表達(dá)式的末尾加上逗號(hào)涣仿。
    • 右括號(hào)與函數(shù)調(diào)用對(duì)齊勤庐,并且單獨(dú)排成一行。

這些是我們使用的基本規(guī)則好港。有關(guān)這些規(guī)則的更詳細(xì)列表愉镰,請(qǐng)?jiān)L問(wèn)http://sql.bi/daxrules

如果您找到一種最適合您閱讀公式的表達(dá)方式钧汹,那么請(qǐng)使用它丈探。格式化的目標(biāo)是使公式更易于閱讀,因此請(qǐng)使用更適合您的方法拔莱。定義個(gè)人格式規(guī)則時(shí)要記住的最重要的事情是碗降,您始終需要盡快查看錯(cuò)誤。如果在之前顯示的未格式化代碼中辨宠,DAX提示缺少右括號(hào)遗锣,則很難找到錯(cuò)誤所在的位置。在格式化的公式中嗤形,更容易看到右括號(hào)如何與開始函數(shù)調(diào)用匹配精偿。

有關(guān)格式化DAX的幫助
格式化DAX不是一件容易的事,因?yàn)槟枰谖谋究蛑惺褂眯∽煮w來(lái)編寫它。不幸的是笔咽,在撰寫本文時(shí)搔预,Excel和Visual Studio都沒(méi)有為DAX提供良好的文本編輯器。
盡管如此叶组,一些提示可能有助于編寫DAX代碼:

  • 如果要增加字體大小拯田,可以在旋轉(zhuǎn)鼠標(biāo)滾輪按鈕的同時(shí)按住Ctrl鍵,以便更容易查看代碼甩十。
  • 如果要在公式中添加新行船庇,可以按Shift + Enter。
  • 如果在文本框中進(jìn)行編輯確實(shí)很麻煩侣监,您可以隨時(shí)在另一個(gè)編輯器(如記事本)中復(fù)制代碼鸭轮,然后在文本框中再次粘貼公式。

最后橄霉,每當(dāng)您查看DAX表達(dá)式時(shí)窃爷,乍一看很難理解它是計(jì)算列還是度量值。因此姓蜂,我們使用(=)來(lái)定義計(jì)算列按厘,使用(:=)來(lái)定義度量值:

CalcCol = SUM (Sales[SalesAmount]) 這是一個(gè)計(jì)算列
CalcFld := SUM (Sales[SalesAmount]) 這是一個(gè)度量值

2.6 常用的DAX函數(shù)

現(xiàn)在您已經(jīng)了解了DAX的基本原理以及如何處理錯(cuò)誤條件,接下來(lái)簡(jiǎn)要介紹DAX最常用的函數(shù)和表達(dá)式钱慢。在本章的剩余部分中逮京,您將看到一些最常用的DAX函數(shù),您可能會(huì)在自己的數(shù)據(jù)模型中使用這些函數(shù)滩字。

2.6.1 聚合函數(shù)

幾乎每個(gè)數(shù)據(jù)模型都需要對(duì)聚合數(shù)據(jù)進(jìn)行操作造虏。 DAX提供了一組函數(shù),這些函數(shù)聚合表中列的值并返回單個(gè)值麦箍。我們稱這組函數(shù)為聚合函數(shù)漓藕。例如,以下度量值計(jì)算Sales表的SalesAmount列中所有數(shù)字的總和:

Sales := SUM ( Sales[SalesAmount] )

如果在計(jì)算列中使用此表達(dá)式(SUM)挟裂,則它將聚合表的所有行享钞,但在度量值中,它只考慮在數(shù)據(jù)透視表中使用時(shí)诀蓉,由切片器栗竖、行、列和篩選條件篩選的行渠啤。

聚合函數(shù)(SUM狐肢、AVERAGE、MIN沥曹、MAX份名、STDEV和VAR)僅對(duì)數(shù)值或日期起作用碟联。

備注:
MIN和MAX具有另一個(gè)功能:如果使用兩個(gè)參數(shù),它們將返回兩個(gè)參數(shù)的最小值或最大值僵腺。因此鲤孵,MIN(1,2)將返回1而MAX(1,2)返回2。此功能在2015年引入辰如,在您需要計(jì)算復(fù)雜表達(dá)式的最小值或最大值時(shí)非常有用普监,因?yàn)樗梢员苊庠贗F語(yǔ)句中多次編寫相同的表達(dá)式。

與Excel類似琉兜,DAX為這些函數(shù)提供了另一種語(yǔ)法凯正,用于計(jì)算同時(shí)包含數(shù)值和非數(shù)值的列,例如文本列呕童。該語(yǔ)法只是將后綴A添加到函數(shù)名稱中漆际,只是為了獲得與Excel相同的名稱和行為。但是夺饲,這些函數(shù)僅對(duì)包含TRUE / FALSE的列有用,因?yàn)門RUE計(jì)算為1施符,F(xiàn)ALSE計(jì)算為0往声。文本列始終視為0。因此戳吝,無(wú)論列的內(nèi)容是什么浩销,如果使用,例如听哭,文本列上的MAXA慢洋,結(jié)果總是得到0。此外陆盘,DAX在執(zhí)行聚合時(shí)從不考慮空單元格普筹。

即使這些函數(shù)可以用于非數(shù)字列而不會(huì)返回錯(cuò)誤,它們的結(jié)果也沒(méi)有用處隘马,因?yàn)槲谋玖袥](méi)有自動(dòng)轉(zhuǎn)換為數(shù)字太防。這些函數(shù)分別名為AVERAGEA、COUNTA酸员、MINA和MAXA蜒车。

備注:
盡管統(tǒng)計(jì)函數(shù)的名稱相同,但它們?cè)贒AX和Excel中的使用方式存在差異幔嗦,因?yàn)樵贒AX中酿愧,列具有類型,其類型決定了聚合函數(shù)的行為邀泉。Excel為每個(gè)單元格處理不同的數(shù)據(jù)類型嬉挡,而DAX為每列處理單個(gè)數(shù)據(jù)類型钝鸽。 DAX以表格形式處理數(shù)據(jù),每列都有明確定義的類型棘伴,而Excel公式適用于異構(gòu)單元格值寞埠,沒(méi)有明確定義的類型。如果Power Pivot中的列具有數(shù)值數(shù)據(jù)類型焊夸,則所有值都只能是數(shù)字或空單元格仁连。如果列是文本類型,則對(duì)于這些函數(shù)(COUNTA除外)始終為0阱穗,即使文本可以轉(zhuǎn)換為數(shù)字饭冬,而在Excel中,該值也被視為逐個(gè)單元格的數(shù)字揪阶。由于這些原因昌抠,這些DAX函數(shù)對(duì)Text列不是很有用。

前面學(xué)習(xí)的函數(shù)對(duì)于執(zhí)行值的聚合很有用鲁僚。有時(shí)炊苫,您對(duì)聚合值不感興趣,而只對(duì)計(jì)數(shù)感興趣冰沙。因此侨艾,DAX提供了一組用于對(duì)行或值進(jìn)行計(jì)數(shù)的函數(shù):

  • COUNT 僅對(duì)數(shù)字列進(jìn)行操作
  • COUNTA 對(duì)任何類型的列進(jìn)行操作
  • COUNTBLANK 返回列中空單元格的數(shù)量
  • COUNTROWS 返回表行數(shù)
  • DISTINCTCOUNT 返回列中不同值的數(shù)量

COUNTA是A后綴函數(shù)組中唯一有趣的函數(shù),因?yàn)樗祷胤强樟械闹档闹低鼗樱m用于任何類型的列唠梨。如果要計(jì)算包含空值的列中的所有值,可以使用COUNTBLANK函數(shù)侥啤。最后当叭,如果要計(jì)算表的行數(shù),可以使用COUNTROWS函數(shù)盖灸。請(qǐng)注意蚁鳖,COUNTROWS需要將表作為參數(shù),而不是列糠雨。

備注:
對(duì)于任何表中的任何列才睹,COUNTA(表[列])+ COUNTBLANK(表[列])的結(jié)果將始終與COUNTROWS(表)相同。

最后一個(gè)函數(shù)DISTINCTCOUNT非常有用甘邀,因?yàn)樗耆凑掌涿Q建議:對(duì)列中的不同值計(jì)數(shù)琅攘,它將其作為唯一參數(shù)。 DISTINCTCOUNT將BLANK值計(jì)為可能值之一松邪。

備注:
DISTINCTCOUNT是2012版DAX中引入的函數(shù)坞琴。早期版本的DAX不包括DISTINCTCOUNT,為了計(jì)算列的不同值的數(shù)量逗抑,您必須使用COUNTROWS( DISTINCT( table [column] ) )剧辐。這兩種模式返回的結(jié)果完全相同寒亥,但DISTINCTCOUNT更容易閱讀,只需要一個(gè)函數(shù)調(diào)用荧关。

到目前為止溉奕,您學(xué)習(xí)的所有聚合函數(shù)都在列上工作(COUNTROWS除外,它適用于表)忍啤。因此加勤,他們只能聚合來(lái)自單個(gè)列的值。聚合函數(shù)可以聚合表達(dá)式而不是單個(gè)列同波。這組函數(shù)非常有用鳄梅,尤其是當(dāng)您想使用不同相關(guān)表的列進(jìn)行計(jì)算時(shí)。例如未檩,如果Sales表包含所有銷售交易戴尸,相關(guān)Product表包含有關(guān)產(chǎn)品的所有信息(包括其成本),那么您可以通過(guò)使用此表達(dá)式定義度量值來(lái)計(jì)算銷售交易的總內(nèi)部成本:

Cost := SUMX ( Sales, Sales[Quantity] * RELATED (Product[StandardCost] ) )

此度量計(jì)算Sales表中每行的Quantity(來(lái)自Sales表)和售出產(chǎn)品的StandardCost(來(lái)自相關(guān)Product表)的乘積冤狡。最后孙蒙,它返回所有這些計(jì)算值的總和。

所有以X后綴結(jié)尾的聚合函數(shù)都以這種方式運(yùn)行:它們?yōu)楸恚ǖ谝粋€(gè)參數(shù))的每一行計(jì)算表達(dá)式(第二個(gè)參數(shù))悲雳,并返回由相應(yīng)的聚合函數(shù)(SUM马篮,MIN, MAX或COUNT)應(yīng)用于這些計(jì)算的結(jié)果怜奖。

您將在第4章中進(jìn)一步了解這種行為,因?yàn)橐_理解它們的行為翅阵,我們需要引入計(jì)算上下文的概念歪玲。可用的X后綴函數(shù)是SUMX掷匠、AVERAGEX滥崩、PRODUCTX、COUNTX讹语、COUNTAX钙皮、CONCATENATEX、MINX和MAXX顽决。還有一些沒(méi)有X后綴的迭代器短条,如FILTER和ADDCOLUMNS。稍后將詳細(xì)解釋所有這些內(nèi)容才菠。

2.6.2 邏輯函數(shù)

有時(shí)您希望在表達(dá)式中構(gòu)建邏輯條件 茸时。例如,根據(jù)列的值實(shí)現(xiàn)不同的計(jì)算或攔截錯(cuò)誤赋访。在這些情況下可都,您可以使用DAX中的邏輯函數(shù)缓待。您已經(jīng)在上一節(jié)“處理DAX表達(dá)式中的錯(cuò)誤”中學(xué)習(xí)了這個(gè)組中最重要的兩個(gè)函數(shù),即IF和IFERROR渠牲。

邏輯函數(shù)非常簡(jiǎn)單旋炒,按照它們的名稱所暗示的去做,它們是AND签杈、FALSE瘫镇、IF、IFERROR芹壕、NOT汇四、TRUE和OR。例如踢涌,如果您希望僅在“價(jià)格”列包含正確的數(shù)值時(shí)將金額計(jì)算為數(shù)量乘以價(jià)格通孽,則可以使用以下模式:

Amount = IFERROR ( Sales[Quantity] * Sales[Price], BLANK ( ) )

如果您未使用IFERROR且價(jià)格列包含無(wú)效數(shù)字,則計(jì)算列的結(jié)果將是錯(cuò)誤睁壁,因?yàn)槿绻麊涡猩捎?jì)算錯(cuò)誤背苦,則錯(cuò)誤將傳遞到整個(gè)列。但是潘明,使用IFERROR可以攔截錯(cuò)誤并將其替換為空值行剂。

此類別中另一個(gè)有趣的函數(shù)是SWITCH,當(dāng)您有一個(gè)列包含少量不同的值钳降,并且您希望根據(jù)其值獲得不同的行為時(shí)厚宰,SWITCH非常有用。例如遂填,Product表中的Column Size包含L铲觉、M、S吓坚、XL撵幽,您可能希望生成一個(gè)更有意義的列。您可以使用嵌套的IF調(diào)用獲取結(jié)果:

SizeDesc =
IF (
    DProduct[Size] = "S",
    "Small",
    IF (
        Product[Size] = "M",
        "Medium",
        IF (
            Product[Size] = "L",
            "Large",
            IF ( Product[Size] = "XL", "Extra Large", "Other" )
        )
    )
)

一個(gè)更方便的方法來(lái)表達(dá)相同的公式礁击,使用SWITCH盐杂,是:

SizeDesc =
SWITCH (
    Product[Size],
    "S", "Small",
    "M", "Medium",
    "L", "Large",
    "XL", "Extra Large",
    "Other"
)

后一個(gè)表達(dá)式中的代碼更易讀,即使不是更快哆窿,因?yàn)樵趦?nèi)部链烈,DAX將SWITCH語(yǔ)句轉(zhuǎn)換為一組嵌套的IF函數(shù)。

提示
這里有一種有趣的方法更耻,可以使用SWITCH函數(shù)檢查同一個(gè)表達(dá)式中的多個(gè)條件测垛。因?yàn)镾WITCH被轉(zhuǎn)換為一組嵌套的IF,其中第一個(gè)匹配優(yōu)先秧均,所以您可以使用此模式測(cè)試多個(gè)條件:

SWITCH (
    TRUE (),
    Product[Size] = "XL"
        && Product[Color] = "Red", "Red and XL",
    Product[Size] = "XL"
        && Product[Color] = "Blue", "Blue and XL",
    Product[Size] = "L"
        && Product[Color] = "Green", "Green and L"
)

實(shí)際上食侮,使用TRUE作為第一個(gè)參數(shù)表示:“返回條件計(jì)算為TRUE的第一個(gè)結(jié)果号涯。”

2.6.3 信息函數(shù)

每當(dāng)需要分析表達(dá)式的類型锯七,您都可以使用信息函數(shù)链快。所有信息函數(shù)都返回TRUE / FALSE值,可以在任何邏輯表達(dá)式中使用眉尸。它們是:ISBLANK域蜗、ISERROR、ISLOGICAL噪猾、ISNONTEXT霉祸、ISNUMBER和ISTEXT。

需要注意的是袱蜡,當(dāng)一個(gè)列(而不是一個(gè)表達(dá)式)作為參數(shù)傳遞時(shí)丝蹭,函數(shù)ISNUMBER,ISTEXT和ISNONTEXT總是返回TRUE或FALSE坪蚁,這取決于列的數(shù)據(jù)類型和每個(gè)單元格的空條件奔穿。

您可能想知道是否可以在文本列中使用ISNUMBER來(lái)檢查是否可以轉(zhuǎn)換為數(shù)字。
不幸的是敏晤,你不能使用這種方法贱田;如果要測(cè)試文本值是否可轉(zhuǎn)換為數(shù)字,則必須嘗試轉(zhuǎn)換并在錯(cuò)誤時(shí)處理錯(cuò)誤嘴脾。例如男摧,要測(cè)試列Price(文本類型)是否包含有效數(shù)字,您必須編寫:

IsPriceCorrect = NOT ( ISERROR ( Sales[Price] + 0 ) )

DAX嘗試向Price添加零以強(qiáng)制從Text值轉(zhuǎn)換為數(shù)字译打;如果成功彩倚,則返回TRUE(因?yàn)镮SERROR將返回FALSE),否則返回FALSE(因?yàn)镮SERROR返回TRUE)扶平。轉(zhuǎn)換將失敗,例如蔬蕊,如果某些行的價(jià)格具有“N / A”字符串值结澄。

但是,如果您嘗試使用ISNUMBER岸夯,如下面的表達(dá)式中所示麻献,您將始終收到FALSE:

IsPriceCorrect = ISNUMBER ( Sales[Price] )

在這種情況下,ISNUMBER始終返回FALSE猜扮,因?yàn)楦鶕?jù)元數(shù)據(jù)勉吻,Price列不是數(shù)字而是字符串,而不管每行的內(nèi)容如何旅赢。

2.6.4 數(shù)學(xué)函數(shù)

DAX中可用的數(shù)學(xué)函數(shù)集與Excel中的相同集非常相似齿桃,具有相同的語(yǔ)法和行為惑惶。常用的數(shù)學(xué)函數(shù)是ABS、EXP短纵、FACT带污、LN、LOG香到、LOG10鱼冀、MOD、PI悠就、POWER千绪、QUOTIENT、SIGN和SQRT梗脾。隨機(jī)函數(shù)是RAND和RANDBETWEEN荸型。 EVEN和ODD讓你測(cè)試數(shù)字。 GCD和LCM可用于計(jì)算兩個(gè)數(shù)字的最大公分母和最小公倍數(shù)藐唠。 QUOTIENT返回兩個(gè)數(shù)字的整數(shù)除法帆疟。

最后,有幾個(gè)舍入函數(shù)值得舉個(gè)例子宇立;實(shí)際上踪宠,您可能會(huì)使用多種方法來(lái)獲得相同的結(jié)果÷栲冢考慮這些計(jì)算列柳琢,以及圖2-3中的結(jié)果:

FLOOR = FLOOR ( Tests[Value], 0.01 )
TRUNC = TRUNC ( Tests[Value], 2 )
ROUNDDOWN = ROUNDDOWN ( Tests[Value], 2 )
MROUND = MROUND ( Tests[Value], 0.01 )
ROUND = ROUND ( Tests[Value], 2 )
CEILING = CEILING ( Tests[Value], 0.01 )
ISO.CEILING = ISO.CEILING ( Tests[Value], 0.01 )
ROUNDUP = ROUNDUP ( Tests[Value], 2 )
INT = INT ( Tests[Value] )
FIXED = FIXED ( Tests[Value], 2, TRUE )
圖2-3 不同舍入函數(shù)的摘要。

如您所見润脸,F(xiàn)LOOR柬脸,TRUNC和ROUNDDOWN非常相似,除了您可以指定要舍入的位數(shù)的方式毙驯。在相反的方向上倒堕,CEILING和ROUNDUP的結(jié)果非常相似。您可以看到在MROUND和ROUND函數(shù)之間進(jìn)行舍入的方式存在一些差異爆价。

2.6.5 三角函數(shù)

DAX提供了一組豐富的三角函數(shù)垦巴,可用于某些計(jì)算。我們不會(huì)詳細(xì)介紹這些函數(shù)铭段,因?yàn)槿绻枰栊鼈兊氖褂煤芎?jiǎn)單。它們是COS序愚、COSH憔披、COT、COTH、SIN芬膝、SINH望门、TAN和TANH。用A對(duì)它們進(jìn)行前綴計(jì)算弧形版本(反正弦蔗候,反余弦等)怒允。

DEGREES和RADIANS分別執(zhí)行轉(zhuǎn)換為度和弧度,SQRTPI在乘以π后計(jì)算其參數(shù)的平方根锈遥。

2.6.6 文本函數(shù)

幾乎所有DAX中可用的文本函數(shù)都與Excel中可用的類似纫事,只有少數(shù)例外。它們是CONCATENATE所灸,EXACT丽惶,F(xiàn)IND,F(xiàn)IXED爬立,F(xiàn)ORMAT钾唬,LEFT,LEN侠驯,LOWER抡秆,MID,REPLACE吟策,REPT儒士,RIGHT,SEARCH 檩坚,SUBSTITUTE着撩,TRIM,UPPER和VALUE匾委。這些函數(shù)對(duì)于處理文本和從包含多個(gè)值的字符串中提取數(shù)據(jù)非常有用拖叙。例如,在圖2-4中赂乐,您可以看到從包含以逗號(hào)分隔的這些值的字符串中提取名字和姓氏的示例薯鳍,標(biāo)題位于中間,我們要?jiǎng)h除它們挨措。

圖2-4 在這里辐啄,您可以看到使用文本函數(shù)提取名和姓的示例。

我們開始計(jì)算兩個(gè)逗號(hào)的位置运嗜,然后我們使用這些數(shù)字來(lái)提取文本的正確部分。如果字符串中的逗號(hào)少于兩個(gè)悯舟,則SimpleConversion列實(shí)現(xiàn)可能返回錯(cuò)誤值的公式(如果根本沒(méi)有逗號(hào)則會(huì)引發(fā)錯(cuò)誤)担租,而FirstLastName列實(shí)現(xiàn)一個(gè)不會(huì)失敗的更復(fù)雜的表達(dá)式丟失逗號(hào)的情況:

Comma1 = IFERROR ( FIND ( ",", People[Name] ), BLANK ( ) )
Comma2 = IFERROR ( FIND ( ",", People[Name], People[Comma1] + 1 ), BLANK ( ) )
SimpleConversion = MID ( People[Name], People[Comma2] + 1, LEN
( People[Name] ) )
& " " & LEFT ( People[Name], People[Comma1] - 1 )
FirstLastName = TRIM (
MID (
People[Name],
IF (
ISNUMBER ( People[Comma2] ),
People[Comma2],
People[Comma1]
) + 1,
LEN ( People[Name] )
))&
IF (
ISNUMBER ( People[Comma1] ),
" " & LEFT ( People[Name], People[Comma1] - 1 ),
""
)

如您所見,F(xiàn)irstLastName列由一個(gè)長(zhǎng)DAX表達(dá)式定義抵怎,但您必須使用它來(lái)避免可能的錯(cuò)誤奋救,即使單個(gè)值生成錯(cuò)誤也會(huì)傳遞到整個(gè)列岭参。

2.6.7 轉(zhuǎn)換函數(shù)

您之前了解到,DAX執(zhí)行數(shù)據(jù)類型的自動(dòng)轉(zhuǎn)換尝艘,以根據(jù)運(yùn)算符的需要進(jìn)行調(diào)整演侯。即使它是自動(dòng)發(fā)生的,一組函數(shù)仍然可以執(zhí)行類型的顯式轉(zhuǎn)換背亥。

CURRENCY可以轉(zhuǎn)換貨幣類型的表達(dá)式秒际,而INT將表達(dá)式轉(zhuǎn)換為整數(shù)。DATE和TIME將日期和時(shí)間部分作為參數(shù)并返回正確的DATETIME狡汉。 VALUE將字符串轉(zhuǎn)換為數(shù)字格式娄徊,而FORMAT將數(shù)值作為第一個(gè)參數(shù),將字符串格式作為第二個(gè)參數(shù)盾戴,并將數(shù)值轉(zhuǎn)換為字符串寄锐。 FORMAT常用于DateTime。例如尖啡,以下表達(dá)式返回“2015 Jan 12”橄仆。

= FORMAT ( DATE ( 2015, 01, 12 ), "yyyy mmm dd" )

通過(guò)使用DATEVALUE函數(shù)執(zhí)行相反的操作,即將字符串轉(zhuǎn)換為DateTime值衅斩。

2.6.8 日期和時(shí)間函數(shù)

幾乎在所有類型的數(shù)據(jù)分析中盆顾,處理時(shí)間和日期都是工作的一個(gè)重要部分。 DAX具有大量按日期和時(shí)間運(yùn)行的函數(shù)矛渴。其中一些函數(shù)與Excel中類似的函數(shù)相對(duì)應(yīng)椎扬,并對(duì)DateTime數(shù)據(jù)類型進(jìn)行簡(jiǎn)單的轉(zhuǎn)換。日期和時(shí)間函數(shù)分別是DATE具温、DATEVALUE蚕涤、DAY、EDATE铣猩、EOMONTH揖铜、HOUR、MINUTE达皿、MONTH天吓、NOW、SECOND峦椰、TIME龄寞、TIMEVALUE、TODAY汤功、WEEKDAY物邑、WEEKNUM、YEAR和YEARFRAC。

為了對(duì)日期進(jìn)行更復(fù)雜的操作色解,例如逐年比較聚合值或計(jì)算度量值的年初至今值茂嗓,還有另一組時(shí)間智能函數(shù),將在第7章“時(shí)間智能計(jì)算”中介紹科阎。

正如本章前面提到的述吸,DateTime數(shù)據(jù)類型在內(nèi)部使用浮點(diǎn)數(shù),其中整數(shù)部分對(duì)應(yīng)于1899年12月30日之后的天數(shù)锣笨,小數(shù)部分則表示當(dāng)天的時(shí)間占比蝌矛。小時(shí)、分鐘和秒被轉(zhuǎn)換為當(dāng)天的小數(shù)部分票唆。因此朴读,向DateTime值添加整數(shù)會(huì)使該值增加相應(yīng)的天數(shù)唠帝。但是审孽,您可能會(huì)發(fā)現(xiàn)使用轉(zhuǎn)換函數(shù)從日期中提取日渊鞋、月和年更方便陨享。在圖2-5中馆揉,您可以看到如何從包含日期列表的表中提取此信息:

Day = DAY ( Calendar[Date] )
Month = FORMAT ( Calendar[Date], "mmmm" )
Year = YEAR ( Calendar[Date] )
圖2-5 您可以在此處看到使用日期和時(shí)間函數(shù)提取日期信息的示例潭兽。

2.6.9 關(guān)系函數(shù)

RELATED和RELATEDTABLE兩個(gè)函數(shù)使您能夠在DAX公式內(nèi)跨越關(guān)系轧简。

您已經(jīng)知道計(jì)算列可以引用定義它的表的列值良风。因此姨伟,Sales中定義的計(jì)算列可以引用同一表的任何列惩琉。但是,如果必須引用另一個(gè)表中的列夺荒,您可以做什么瞒渠?通常,您不能在其他表中使用列技扼,除非在兩個(gè)表之間的模型中定義關(guān)系伍玖。如果兩個(gè)表存在一個(gè)關(guān)系,則RELATED函數(shù)允許您訪問(wèn)相關(guān)表中的列剿吻。

例如窍箍,您可能希望在Sales表中創(chuàng)建一個(gè)計(jì)算列,檢查已售出的產(chǎn)品是否屬于“手機(jī)”類別丽旅,并且此類別下椰棘,減少標(biāo)準(zhǔn)成本。要計(jì)算此列榄笙,必須使用檢查產(chǎn)品類別的值邪狞,該值不在Sales表中。然而茅撞,一系列關(guān)系從Sales開始帆卓,通過(guò)Product和Product Subcategory到達(dá)Product Category杆逗,如圖2-6所示。

圖2-6 Sales與Product Category具有一個(gè)關(guān)系鏈鳞疲。

從原始表到相關(guān)表需要多少步驟無(wú)關(guān)緊要,DAX將遵循完整的關(guān)系鏈并返回相關(guān)的列值蠕蚜。因此尚洽,AdjustedCost列的公式可以是:

Sales[AdjustedCost] =
IF (
    RELATED ( 'Product Category'[Category] ) = "Cell Phone",
    Sales[UnitCost] * 0.95,
    Sales[UnitCost]
)

在一對(duì)多關(guān)系中,RELATED可以從多端訪問(wèn)一端靶累,因?yàn)樵谶@種情況下腺毫,相關(guān)表中只有一行存在(如果有)。如果不存在這樣的行挣柬,RELATED只返回BLANK潮酒。

如果您處于關(guān)系的一端并且想要訪問(wèn)多端,那么不能用RELATED邪蛔,因?yàn)槎喽说脑S多行需要用于單行急黎。在這種情況下,您可以使用RELATEDTABLE侧到。RELATEDTABLE返回一個(gè)表勃教,其中包含與當(dāng)前行相關(guān)的所有行。例如匠抗,如果您想知道每個(gè)類別中有多少產(chǎn)品故源,可以使用以下公式在Product Category中創(chuàng)建一個(gè)列:

= COUNTROWS ( RELATEDTABLE ( Product ) )

對(duì)于每個(gè)產(chǎn)品類別,此計(jì)算列將顯示相關(guān)產(chǎn)品的數(shù)量汞贸,如圖2-7所示绳军。

圖2-7 使用RELATEDTABLE計(jì)算產(chǎn)品數(shù)。

與RELATED的情況一樣矢腻,RELATEDTABLE可以遵循一個(gè)關(guān)系鏈门驾,從一端指向多端。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末踏堡,一起剝皮案震驚了整個(gè)濱河市猎唁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌顷蟆,老刑警劉巖诫隅,帶你破解...
    沈念sama閱讀 221,430評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異帐偎,居然都是意外死亡逐纬,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門削樊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)豁生,“玉大人兔毒,你說(shuō)我怎么就攤上這事〉橄洌” “怎么了育叁?”我有些...
    開封第一講書人閱讀 167,834評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)芍殖。 經(jīng)常有香客問(wèn)我豪嗽,道長(zhǎng),這世上最難降的妖魔是什么豌骏? 我笑而不...
    開封第一講書人閱讀 59,543評(píng)論 1 296
  • 正文 為了忘掉前任龟梦,我火速辦了婚禮,結(jié)果婚禮上窃躲,老公的妹妹穿的比我還像新娘计贰。我一直安慰自己,他們只是感情好蒂窒,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評(píng)論 6 397
  • 文/花漫 我一把揭開白布躁倒。 她就那樣靜靜地躺著,像睡著了一般刘绣。 火紅的嫁衣襯著肌膚如雪樱溉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,196評(píng)論 1 308
  • 那天纬凤,我揣著相機(jī)與錄音福贞,去河邊找鬼。 笑死停士,一個(gè)胖子當(dāng)著我的面吹牛挖帘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播恋技,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼拇舀,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蜻底?” 一聲冷哼從身側(cè)響起骄崩,我...
    開封第一講書人閱讀 39,671評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎薄辅,沒(méi)想到半個(gè)月后要拂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,221評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡站楚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評(píng)論 3 340
  • 正文 我和宋清朗相戀三年脱惰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窿春。...
    茶點(diǎn)故事閱讀 40,444評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拉一,死狀恐怖采盒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蔚润,我是刑警寧澤磅氨,帶...
    沈念sama閱讀 36,134評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站嫡纠,受9級(jí)特大地震影響悍赢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜货徙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望皮胡。 院中可真熱鬧痴颊,春花似錦、人聲如沸屡贺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)甩栈。三九已至泻仙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間量没,已是汗流浹背玉转。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留殴蹄,地道東北人究抓。 一個(gè)月前我還...
    沈念sama閱讀 48,837評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像袭灯,于是被迫代替她去往敵國(guó)和親刺下。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容