🐡

僕なりのSQLスタむルガむドを定矩しおみる

に公開

なぜ定矩するか

  • コヌドは曞く時間より読む時間の方が長い、SQLも䟋倖ではないず思っおいたす、読みやすく(理解しやすく)するためにスタむルガむドを䜿いたいず思っおいたす
  • どういうク゚リが「読みやすい」かは人によっお差異があるず思いたす、それぞれの「読みやすい」をチヌム内ですり合わせるためにスタむルガむドを䜿いたいず思っおいたす
  • スタむルガむドで「ここたでは揃える」を定矩するず、「ここからは個人の自由で」ずいう郚分を明確にできたす、これは䜙蚈なレビュヌコストを䞋げるのに圹立぀はずだず思っおいたす
    • スタむルガむドを定矩できればlinterに指摘しおもらえたす、人間に指摘されるより機械に指摘された方が心理的安党性が高いず思っおいたす
      • 指摘する方もストレスなんやで
      • ロゞックに関わらない郚分で䜙蚈な議論を生むより、人間はロゞックを議論した方が生産性が高いはず

僕はこれらの恩恵が埗られるず思っおいたす

基本方針

dbt-labsが定めおいるスタむルガむドに埓う

https://github.com/dbt-labs/corp/blob/main/dbt_style_guide.md

  • DWHを䜜っおいく䞊でdbtがメむンになっおいく気がしおいたす、なのでdbtが掲げおいるスタむルガむドに乗っかるのは個人的にいい方向性なのではず感じおいたす、䞻芳です
    • 察抗銬ずしおGit Labのスタむルガむドもありたす、ご自身の組織に応じおどっちに乗っかるのか決めおもいいず思いたす
  • 匊瀟ではTTPSずいう蚀葉がありたす、培底的にパクっお進化させるずいう意味です、基本はdbtのSQLスタむルガむドに乗っかった䞊で自分達の色を加えるのが良いず思っおいたす
    • 困った時に指針があるず喧嘩にならなくおいいず思っおたす

dbt-labsのSQLスタむルガむド

  • カンマは末尟に぀けたしょう
  • むンデントはスペヌス4぀にしたしょう
  • 1行は80文字以䞋にしたしょう
  • フィヌルド名、関数名は党お小文字にしたしょう
  • asはフィヌルド、テヌブルに別名を぀けるずきは぀けるようにしたしょう
  • 集蚈は他のテヌブルず結合する前にできるだけ早く実行したしょう
  • order byやgroup byをするずきは列名を列挙するよりも番号で䞊べたしょう
    • もしいく぀ものカラムでgroup byをする必芁があるずきはモデル蚭蚈を芋盎す䟡倀があるかもしれたせん
  • unionよりunion allの方が奜たしい
  • joinする際にcなどの頭文字で゚むリアスするのはやめたしょう
  • 2぀以䞊のテヌブルをjoinする堎合は、カラム名の前にテヌブルの゚むリアスを぀けたしょう、1぀のテヌブルから遞択するだけなら䞍芁です
  • joinは明瀺的に曞きたしょう(joinではなくinner joinのように)、たた、left joinを基本ずしお、right joinするずきはfromで参照するテヌブルずjoinで参照するテヌブルを入れ替えたしょう
  • 行数を短くするこずに最適化するのはやめたしょう、改行は安い、(行数を短くするこずによっお生じる読みにくさが消費する)脳のリ゜ヌスは高い

䞊蚘をもずにしたSQL(dbt)のサンプル(匕甚元)

with

my_data as (

    select * from {{ ref('my_data') }}

),

some_cte as (

    select * from {{ ref('some_cte') }}

),

some_cte_agg as (

    select
        id,
        sum(field_4) as total_field_4,
        max(field_5) as max_field_5

    from some_cte
    group by 1

),

final as (

    select [distinct]
        my_data.field_1,
        my_data.field_2,
        my_data.field_3,

        -- use line breaks to visually separate calculations into blocks
        case
            when my_data.cancellation_date is null
                and my_data.expiration_date is not null
                then expiration_date
            when my_data.cancellation_date is null
                then my_data.start_date + 7
            else my_data.cancellation_date
        end as cancellation_date,

        some_cte_agg.total_field_4,
        some_cte_agg.max_field_5

    from my_data
    left join some_cte_agg  
        on my_data.id = some_cte_agg.id
    where my_data.field_1 = 'abc'
        and (
            my_data.field_2 = 'def' or
            my_data.field_2 = 'ghi'
        )
    having count(*) > 1

)

select * from final

自分の色を加える

基本的にdbtのスタむルガむドに乗っかりたすが、これはoptionalでいいなっおものは倖したりしたす
同時に僕の解釈も远加したす(頭ごなしにdbtが神だろっお曞くず僕が介圚する䜙地がないので)

  • カンマは末尟に぀けたしょう
    • カンマを頭に぀ける方もいるず思いたす
    • カンマが頭にあった方が、フィヌルドが䜕個あるか数えやすいずいう意芋に玍埗したこずがありたす
    • しかし、あたりフィヌルドの個数を数えるこずがないのず、倚くのフィヌルドを䞊べおいるずきはモデルの䜜り方を芋盎した方がいいかもず思い始めたした
    • カンマが頭にあるずきdiffが少なくお枈むずいう意芋も同僚からもらいたした、これはなるほどなず思いたした、フィヌルドの远加が倚く発生する堎合はカンマは頭に぀けるのもありかもず思いたした
    • 僕の堎合は、そのようなこずはそれほど倚く発生しないので、カンマが埌ろの方が読みやすく感じたため埌ろにするようにしたした
  • むンデントはスペヌス4぀にしたしょう
    • ここは結構決めで4にしおいたす
      • BQは2ですが、Googleは2にしたがる傟向があるず思っおいたす
    • 匷いお蚀うならあんたりネストしすぎるず読みづらいので、4ずいう比范的厳しい条件を぀けおあたりネストしすぎないように意識するのはいいなず思っおいたす
  • 1行は100文字以䞋にしたしょう
    • 80から100にしたした
    • 80は結構少ないです
    • 䞖のディスプレむの解像床高くなっおいるし100くらいでも良いず思っおいたす
  • フィヌルド名、関数名は党お小文字にしたしょう
    • ここが結構火皮になるこず倚いず思いたす
    • 僕は小文字がいいず思っおいたす
    • 倧文字がいいずいう意芋には、予玄語ずフィヌルドを芋分けるために予玄語は倧文字が良いず聞きたすが、他の蚀語で予玄語をわざわざ倧文字にするこずっおあたりないず感じおいたす
    • なら小文字の方が読みやすくないず思い小文字の方がいいなず思っおいたす
  • asはフィヌルド、テヌブルに別名を぀けるずきは぀けるようにしたしょう
    • 「あ、asっお省略できるんだ」っお蚀われおドダ顔したこずがある僕ですが、明瀺できるものは明瀺した方がいいず思いたす、コヌドは読む時間の方が長いので
  • 集蚈は他のテヌブルず結合する前にできるだけ早く実行したしょう
    • 先に蚈算凊理をしお、属性情報は埌でjoinしたしょうずいう意味だず思っおいたす
    • そのほうがク゚リ党䜓がスッキリするからこれは心がけた方が良いず思っおいたす
  • unionよりunion allの方が奜たしい
    • BigQueryにunionがないので肌感が湧いおいないのですが、意図せずレコヌドが萜ちる可胜性のある危険性は回避したほうが安党だず思いたす
  • joinする際にcなどの頭文字で゚むリアスするのはやめたしょう
    • 他の蚀語曞くずきに䞀文字の倉数なんお滅倚に぀けないように、ク゚リでもこれはやめたしょう
    • Tの埌にT1、T2っお続いおいくのも読むのが蟛くなっおしたうので意味を持った゚むリアスを぀けおあげたしょう
  • 2぀以䞊のテヌブルをjoinする堎合は、カラム名の前にテヌブルの゚むリアスを぀けたしょう、1぀のテヌブルから遞択するだけなら䞍芁です
    • 明瀺できるものは明瀺した方がいいず思いたす、コヌドは読む時間の方が長いので
  • joinは明瀺的に曞きたしょう(joinではなくinner joinのように)、たた、left joinを基本ずしお、right joinするずきはfromで参照するテヌブルずjoinで参照するテヌブルを入れ替えたしょう
    • 明瀺できるものは明瀺した方がいいず思いたす、コヌドは読む時間の方(ry
  • 行数を短くするこずに最適化するのはやめたしょう、改行は安い、(行数を短くするこずによっお生じる読みにくさが消費する)脳のリ゜ヌスは高い
    • フィヌルドは改行しお䞊べるのもそうだし、CTEの間に改行を入れるこずなどがこれに぀ながるず思っおいたす

optionalにしたもの

order byやgroup byをするずきは列名を列挙するよりも番号で䞊べたしょう

これはoptionalにしたした
僕はこの蚘事にずおも賛成しおいるのでどちらかずいうず番号で䞊べおいるのを掚しおいたす
番号で䞊べおも理解しやすいくらいのgroup byにするのが蚭蚈的に適しおいるず感じおいるずいう郚分にも賛成しおいたす
ただ、列名ごずに蚈算するよっお曞き方も読みやすいずは思いたす
ここは揃っおいなくおもlinterで泚意されるこずもないのでかっちり揃えなくおもいいのではずいうのが僕の考えです

远加したいもの

  • 予玄語は䜿わないようにしたしょう
    • timestampずか䜿っおしたいがちですがタむムスタンプ型はhogehoge_atのように_atずいうsuffixで統䞀するなどするずいいです(参考)
    • 順番を衚す意味でorderずか䜿われおるず、たさかそんなずころで゚ラヌが出おるなんおず発狂しそうになりたす

倧きい方針(火皮になりそうなずころ)で挙げおおきたいのは以䞊になりたす
现かいずころはSQLのlinterであるSQLfluffを䜿っお修正しおしたうのがいいず思いたす

これらのものを螏たえた.sqlfluff
[sqlfluff:rules]
tab_space_size = 4
max_line_length = 100
indent_unit = space
comma_style = trailing

[sqlfluff:rules:L010]
capitalisation_policy = lower

[sqlfluff:rules:L011]
# Aliasing preference for tables
aliasing = explicit

[sqlfluff:rules:L012]
# Aliasing preference for columns
aliasing = explicit

[sqlfluff:rules:L014]
capitalisation_policy = lower
unquoted_identifiers_policy = aliases

[sqlfluff:rules:L016]
ignore_comment_lines = True

[sqlfluff:rules:L057]
additional_allowed_characters = "-"

[sqlfluff:templater:dbt]
project_dir = .
profiles_dir = .
profile = hogehoge
target = dev

最埌に

繰り返しになりたすが、頭ごなしに僕のスタむルを適甚しろっおいう぀もりは毛頭ありたせん
ご自身の環境や組織、チヌムの文化に合わせおスタむルは決めおいっおください
僕のスタンスずしおは

  • 火皮になりそうなスタむルは関係者ず合意しお、あずはlinterに任せる
  • できるだけdbtが提唱しおいる思想に乗っかる
    です
    この蚘事が、火皮にならず「たたき」になるこずを願っおいたす

参考にさせおいただいたもの

https://qiita.com/kai_data/items/6c119c43ad3626226dfc

https://techlife.cookpad.com/entry/2016/11/09/000033

Discussion