LoginSignup
8
7

More than 3 years have passed since last update.

Power Query のトレースログを見て、どのように処理されているか推測するなど

Last updated at Posted at 2019-04-13

すべてではないですよ。経験上であったり他の方法で知っていた分から、Power Queryでの内部的な動作(処理される順番とか)ってはどうなっているのかをトレースログから推測するなど。"適用したステップ" の順で処理されるわけではないよという話を検証するような内容。ただし、ログ自体の仕様は不明なので絶対ではない感じでよくて、あーそんな感じなのねとわかればよいかなと。

Power BI Desktop に "クエリ診断" 機能が追加されました
Power Query エディターで診断開始から終了、パースされたファイル(JSON)からログなど参照できるようになりました :smile:
image.png

Diagnostics.Trace

Diagnostics.Trace
Diagnostics.Trace(traceLevel as number, message as anynonnull, value as any, optional delayed as nullable logical) as any
トレースが有効になっていて値を返す場合は、トレース エントリを記述します。
トレースが有効になっていて value を返す場合は、トレース message を記述します。オプション パラメーター delayed は、メッセージがトレースされるまで value の評価を遅らせるかどうかを指定します。traceLevel には次のいずれかの値を指定できます:
TraceLevel.Critical
TraceLevel.Error
TraceLevel.Warning
TraceLevel.Information
TraceLevel.Verbose

いくつかのケース

Table

Table.AddColumn(カスタム列をテーブルに追加)

いくつかのカスタム列をテーブルに追加するとき、全行にカスタム列を追加が終わってから続いてカスタム列を追加するのではなくて、可能であれば行ごとにすべてのカスタム列を追加していく動作。

let
    Source = #table(
        type table [Column1 = Int64.Type],
        List.Transform({1 .. 5},  each {_})
    ),
    AddedCustom1 = Table.AddColumn(
        Source,
        "Custom1",
        each [Column1] + 1,
        Int64.Type
    ),
    AddedCustom2 = Table.AddColumn(
        AddedCustom1,
        "Custom2",
        each [Column1] + 2,
        Int64.Type
    )
in
    AddedCustom2

image.png

let
    Source = #table(
        type table [Column1 = Int64.Type],
        List.Transform({1 .. 5},  each {_})
    ),
    AddedCustom1 = Diagnostics.Trace(
        TraceLevel.Information,
        "AddedCustom1",
        ()=> Table.AddColumn(
            Source,
            "Custom1",
            each Diagnostics.Trace(
                TraceLevel.Information,
                "AddedCustom1 #" & Text.From([Column1]),
                ()=> [Column1] + 1,
                true
            ),
            Int64.Type
        ),
        true
    ),
    AddedCustom2 = Diagnostics.Trace( 
        TraceLevel.Information,
        "AddedCustom2",
        ()=> Table.AddColumn(
            AddedCustom1,
            "Custom2",
            each Diagnostics.Trace(
                TraceLevel.Information,
                "AddedCustom2 #" & Text.From([Column1]),
                ()=> [Column1] + 2,
                true
            ),
            Int64.Type
        ),
        true
    )
in
    AddedCustom2

image.png

Table.SelectRows(行の選択)

Power Query エディターでの列の値を条件にするフィルタ操作。当然ながら全行を走査する動作

Table.SelectRows
let
    Source = #table(
        type table [Column1 = Int64.Type, Column2 = Int64.Type],
        List.Transform({1 .. 5},  each {_, _})
    ),
    MultipliedColumn = Table.TransformColumns(
        Source,
        {"Column2", each _ * 10, Int64.Type}
    ),
    FilteredRows = Table.SelectRows(
        MultipliedColumn,
        each [Column2] < 40
    )
in
    FilteredRows

image.png

let
    Source = #table(
        type table [Column1 = Int64.Type, Column2 = Int64.Type],
        List.Transform({1 .. 5},  each {_, _})
    ),
    MultipliedColumn = Diagnostics.Trace(
        TraceLevel.Information,
        "MultipliedColumn",
        ()=> Table.TransformColumns(
            Source,
            {
                "Column2",
                each Diagnostics.Trace(
                    TraceLevel.Information,
                    "MultipliedColumn2 #" & Text.From(_),
                    ()=> _ * 10,
                    true
                ),
                Int64.Type
            }
        ),
        true
    ),
    FilteredRows = Diagnostics.Trace(
        TraceLevel.Information,
        "FilteredRows",
        ()=> Table.SelectRows(
            MultipliedColumn,
            each Diagnostics.Trace(
                TraceLevel.Information,
                "FilteredRows #" & Text.From([Column1]),
                ()=> [Column2] < 40,
                true
            )
        ),
        true
    )
in
    FilteredRows

image.png
カスタム列の追加が終わってから行を選択する評価ではなく、カスタム列の追加をしつつ行を選択する動作。

Table.FirstN(先頭行の選択)

Table.SelectRows と異なり全行を走査しない。 先頭行から条件が満たされる行のみを選択。
なので。データソース側で行の順番が確定できる場合に使うとよいことがある。

Table.FirstN
let
    Source = #table(
        type table [Column1 = Int64.Type, Column2 = Int64.Type],
        List.Transform({1 .. 5},  each {_, _})
    ),
    MultipliedColumn = Table.TransformColumns(
        Source,
        {"Column2", each _ * 10, Int64.Type}
    ),
    FirstRows = Table.FirstN(
        MultipliedColumn,
        each [Column2] < 40
    )
in
    FirstRows

image.png

let
    Source = #table(
        type table [Column1 = Int64.Type, Column2 = Int64.Type],
        List.Transform({1 .. 5},  each {_, _})
    ),
    MultipliedColumn = Diagnostics.Trace(
        TraceLevel.Information,
        "MultipliedColumn",
        ()=> Table.TransformColumns(
            Source,
            {
                "Column2",
                each Diagnostics.Trace(
                    TraceLevel.Information,
                    "MultipliedColumn2 #" & Text.From(_),
                    ()=> _ * 10,
                    true
                ),
                Int64.Type
            }
        ),
        true
    ),
    FirstRows = Diagnostics.Trace(
        TraceLevel.Information,
        "FirstRows",
        ()=> Table.FirstN(
            MultipliedColumn,
            each Diagnostics.Trace(
                TraceLevel.Information,
                "FirstRows #" & Text.From([Column1]),
                ()=> [Column2] < 40,
                true
            )
        ),
        true
    )
in
    FirstRows

image.png
4行目で条件を満たさなくなり、それ以降の評価は実施されない。

List

リスト(list)の場合もテーブルとほぼ等しくリストアイテムを順に評価していく動作。

List.Contains

let
    ListContainsFalse = List.Contains(
        {true, false, true, false},
        false
    ),
    ConvertedToTable = #table(1, {{ListContainsFalse}})
in
    ConvertedToTable
let
    ListContainsFalse = Diagnostics.Trace(
        TraceLevel.Information,
        "ListContains",
        ()=> List.Contains(
            {
                Diagnostics.Trace(TraceLevel.Information, "#1 TRUE", ()=> true, true),
                Diagnostics.Trace(TraceLevel.Information, "#2 FALSE", ()=> false, true),
                Diagnostics.Trace(TraceLevel.Information, "#3 TRUE", ()=> true, true),
                Diagnostics.Trace(TraceLevel.Information, "#4 FALSE", ()=> false, true)
            },
            false
        ),
        true
    ),
    ConvertedToTable = #table(1, {{ListContainsFalse}})
in
    ConvertedToTable

image.png
ふたつめのリストアイテムで条件を満たしたので、それ以降の評価は実施されない。

List.AllTrue / List.AnyTrue

特殊な List.Contains って感覚ではあったがちょっと違うようだ。

let
    ListAnyTrue = List.AnyTrue(
        {true, false, true, false}
    ),
    ConvertedToTable = #table(1, {{ListAnyTrue}})
in
    ConvertedToTable
let
    ListAnyTrue = Diagnostics.Trace(
        TraceLevel.Information,
        "ListAnyTrue",
        ()=> List.AnyTrue(
            {
                Diagnostics.Trace(TraceLevel.Information, "#1 TRUE", ()=> true, true),
                Diagnostics.Trace(TraceLevel.Information, "#2 FALSE", ()=> false, true),
                Diagnostics.Trace(TraceLevel.Information, "#3 TRUE", ()=> true, true),
                Diagnostics.Trace(TraceLevel.Information, "#4 FALSE", ()=> false, true)
            }
        ),
        true
    ),
    ConvertedToTable = #table(1, {{ListAnyTrue}})
in
    ConvertedToTable

image.png

ひとつめのリストアイテムで条件を満たすのだけど、すべてのリストアイテムを評価しているので、List.Contains に書き換えるとよいときがあるかと。

List.Select

Table.SelectRows 同様、すべてのリストアイテムが評価される。

let
    ListSelect = List.Select(
        {true, false, true, false},
        each _ = true
    ),
    ConvertedToTable = Table.FromColumns({ListSelect})
in
    ConvertedToTable
let
    ListSelect = Diagnostics.Trace(
        TraceLevel.Information,
        "ListSelect",
        ()=> List.Select(
            {
                Diagnostics.Trace(TraceLevel.Information, "#1 TRUE", ()=> true, true),
                Diagnostics.Trace(TraceLevel.Information, "#2 FALSE", ()=> false, true),
                Diagnostics.Trace(TraceLevel.Information, "#3 TRUE", ()=> true, true),
                Diagnostics.Trace(TraceLevel.Information, "#4 FALSE", ()=> false, true)
            },
            each _ = true
        ),
        true
    ),
    ConvertedToTable = Table.FromColumns({ListSelect})
in
    ConvertedToTable

image.png

Query Folding

"適用するステップ"は可能な限り折りたたみが行われる。

OData.Feed

とくに特別な動作ではないが、Request 内容からすると、全件を取得する動作はしない。

let
    Source = OData.Feed(
        "https://services.odata.org/v4/northwind/northwind.svc",
        null,
        [Implementation="2.0"]
    ),
    Products_table = Source{[Name="Products",Signature="table"]}[Data],
    FilteredRows = Table.SelectRows(
        Products_table,
        each ([Discontinued] = false)
    ),
    ExpandedCategoryName = Table.ExpandRecordColumn(
        FilteredRows,
        "Category",
        {"CategoryName"}, {"CategoryName"}
    ),
    RemovedOtherColumns = Table.SelectColumns(
        ExpandedCategoryName,
        {"ProductID", "ProductName", "CategoryID", "CategoryName"}
    )
in
    RemovedOtherColumns

image.png

思ったこと🙄

できれだけ "行" 単位で処理が進むようにして、条件がそろっているなら Table.SelectRows や List.Select をではなく代替の関数を使うことで不要な評価を減らす感じなのかな。いろいろあるよねー。

その他

8
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7