月: 2025年12月

  • 大量更新SQL の性能改善

    業務で大量更新処理の速度改善を行ったので、大切な点を残しておきます。
    プロジェクトを離れた前任者の一括処理が、20時間以上経っても終わらないという状況で相談を受けました。
    結果としては複雑なチューニングよりも、業務要件の洗い出しと、基本的なSQLの組み方が最も効果的でした。

    状況調査

    まず、処理内容とSQL に登場するテーブル件数を確認しました。
    一括処理は、1億レコードのテーブルを、その他のテーブルから集めた情報で更新するというものでした。
    1億件ものレコードを全件洗い替えするため、20時間要していました。

    SQL のイメージはこちらです。

    SQL
    -- 1億レコードを全件更新する処理(改善前)
    UPDATE heavy_table t
    INNER JOIN (
        SELECT ...
    ) info ON ...
    SET t.column = info.value;

    対象レコードを業務要件から絞り込み、大幅高速化

    業務要件を確認したところ、全件の洗い替えではなく、その日に更新が入ったデータだけを洗い替えれば十分だと分かりました。
    そのため、本当に更新が必要な対象は、1億ではなく数千件程度でした。
    SQL のwhere 句で更新対象を絞り込むことで、約10分の1まで短縮されました。
    この時点で、20時間かかっていた処理が2時間まで短縮されました。

    SQL
    -- 1億レコードのうち、本日更新分だけに絞り込む(約10分の1に短縮)
    UPDATE heavy_table t
    INNER JOIN (
        SELECT ...
    ) i ON ...
    SET t.column = i.value
    WHERE t.id IN (
        SELECT id
        FROM other_table ...
    );

    update 文でサブクエリを使わない

    その日に更新が入ったデータの抽出をサブクエリで行っていたのですが、update 文とサブクエリを同時に使うよりも、サブクエリ結果を一時テーブルに入れてからupdate を行った方が速いことが分かりました。
    私にはデータベース内部の挙動を正確に説明できないのですが、update 文にサブクエリを含めるとデータベースに適切な最適化を難しくさせたり、不必要なロックが発生するようです。

    こちらの対策を行うことで、処理時間が30分になりました。

    SQL
    -- 本日分のIDを事前に一時テーブルへ投入(サブクエリの事前計算)
    INSERT INTO tmp_id
    SELECT id
    FROM other_table ...;
    
    -- 一時テーブルと結合して高速に更新(最終的に30分まで短縮)
    UPDATE heavy_table t
    INNER JOIN (
        SELECT ...
    ) i ON ...
    INNER JOIN tmp_id tmp ON t.id = tmp.id
    SET t.column = i.value;

    まとめ

    以前、SQLチューニングについて、先輩から教わったことを思い出しました。
    「業務影響がある対策が一番効果があり、業務影響がない対策では大きな改善は難しい。」
    今回は、はじめに業務要件を確認したのが良かったと思います。
    チームでは並行して、ヒント句を入れたりクラウドなのでRDB のスケールアップを試みたりしましたが、おそらくそれらだけでは、1億件の更新を30分で終わらせることはできなかったと思います。
    改めて先輩の知見に感謝です。

    【広告】

  • SwiftUI DatePicker でできないこと

    DatePicker は使いやすく、日付と時間の両方をピッカーで選択できるようになり、とても便利です。しかしながら、できそうでできないことがいくつかあり、初心者である私は採用を見送りました。2025年12月時点の話なのと、私が個人で調査した範囲の話である点を前提としていただければと思います。

    基本的な使い方

    Swift
    @State private var date = Date()
    
    DatePicker(
        "日時",
        selection: $date,
        // 日付と時間のピッカーを表示する。ここに問題がある
        displayedComponents: [.date, .hourAndMinute]
    )
    .datePickerStyle(.compact)
    .environment(.locale, Locale(identifier: "ja_JP"))

    このように書くだけで、日付と時間が横並びで表示されそれぞれをピッカーで選択できます。

    テキストの背景に素敵な灰色の楕円を描画してくれます。
    カスタマイズしないのであれば、素晴らしいと思います。

    1. 左寄せにすると謎の隙間ができる

    こちらを左寄せにしようとすると、途端に難しくなりました。

    Swift
    HStack(spacing: 0) {
        Image(systemName: "(toDay(date)).calendar")
            .foregroundColor(.green)
            .font(.system(size: 24))
        DatePicker(
            "",
            selection: $date,
            displayedComponents: [.date, .hourAndMinute]
        )
        .datePickerStyle(.compact)
        .environment(
            .locale,
            Locale(identifier: "ja_JP")
        )
        .labelsHidden()
    }

    .labelsHidden() を入れたので、本来ラベルが表示される領域がなくなるはずですが、なぜか領域が残っています。(赤線部分)
    背景色を付けてもう少し分かりやすくします。

    Swift
    HStack(spacing: 0) {
        Image(systemName: "(toDay(date)).calendar")
            .foregroundColor(.green)
            .font(.system(size: 24))
            .background(.blue)  // 追加
        DatePicker(
            "",
            selection: $date,
            displayedComponents: [.date, .hourAndMinute]
        )
        .datePickerStyle(.compact)
        .environment(
            .locale,
            Locale(identifier: "ja_JP")
        )
        .labelsHidden()
        .background(.orange)  // 追加
    }

    コードにはないスペースが存在していることが分かります。DatePicker の内部でスペースを描画しているようです。
    不思議なことに、何度か再起動するとスペースが入らないこともありました。確実に再現しない点も悩ましいです。

    2. タップ可能領域を調整できない

    DatePicker 自体を隠して、別の表示領域を作り、そちらのタップイベントでDatePicker を呼び出す方式を考えました。Javascript ではよくあるやり方と思います。
    しかし、DatePicker にPicker だけ表示するインターフェースが存在しないため、この方式実現できないようです。

    3. macos は別の実装となっている

    DatePicker に限りませんが、SwiftUI は全体的にmacos の見た目はios と同じレベルまで洗練されていはいない印象があります。
    DatePicker については、.wheel がコンパイルレベルで選択できなくて、.compact は.graphical と同じ挙動となります。.graphical はios とは見た目が大きく異なります。
    こちらも残念です。

    参考 macos の見た目

    まとめ

    長い時間悩みましたが、私には解決できないため、独自に実装することとしました。
    こちらのコードがとても綺麗で参考にさせてもらいました。
    https://github.com/SimformSolutionsPvtLtd/SSDateTimePicker

    また、iPhone のGoogle カレンダーアプリも、カレンダー部分は独自実装と思われます。せっかく見た目が綺麗なDatePicker なので、カスタマイズができれば少し活躍できそうな気がします

    参考 iPhone 版google カレンダーアプリ

    アップルさんの今後に期待しています。

    【広告】