きり丸の技術日記

技術検証したり、資格等をここに残していきます。

DataGripで接続先をまとめたい(JetBrains製ならどれでもOK)

始めに

現在、私は複数のプロジェクトに参画していて、保守作業する際には接続先をプロジェクトごとに切り替える必要があるのですが、すべて接続先をフラットに管理していました。接続先名で無理やりprefixを付与して管理していたのですがDataGripにはフォルダのように管理できる方法があると聞いてその方法をメモします。

なお、DataGripでしか無理、と思っていたのですが他のIDEでも同様の挙動を実装できそうです。

環境

  • PyCharm Professtional
    • 2024.03
  • DataGrip
    • 2024.03.2

実装

Database explorerにて、右クリックするとMove to Folderがあります。こちらをクリックすると、フォルダを作成できます。また、名称に/を入力するとフォルダを区切れるので多層構造も可能です。

私は最近この機能を知ったのでちゃんと有効活用はできていないのですが、次のようなフォルダ構造で使用する予定です。

- {システム名}
    - localhost
    - Remote
        - 検証環境全般
    - Prod
        - 本番環境

ソースコード

なし。

終わりに

A5m2では接続先名を/で区切ると自動でフォルダ管理できたので同じような機能があると思っていたのですが、DataGripでは明示的にフォルダを作成する必要があるのですね。

まだ知らないDataGripの機能はたくさんあると思うので、細かいところまで使いこなしていきたいです。

nullは不明値なのでnullで一意制約をかけられない

始めに

レコードの有効期間を表示したいときに、start_at, end_atのカラムを用いて表現していました。そして、end_atがnullの場合にアクティブなレコードとして表現しようとしていました。しかし、このやり方ではデータの管理方法に失敗するとアクティブなレコードが複数できてしまう可能性があります。

そのため、アクティブなレコードを1つだけに絞りたかったので、別の主キー + end_atでアクティブなレコードであること表現しようとしました。

しかし、その方法がうまくいかなかったので、同じ轍を踏まないようにブログに残しておきます。

環境

  • MySQL
    • 8.0
  • PostgreSQL
    • 17.2

前提

ユーザー間の関係を表現するために、user_id_1, user_id_2start_at, end_atで有効期間を表示するテーブルです。

  • user_id_1
  • user_id_2

さらに、アクティブなレコードは1つにしたいです。画像のようなレコードが生まれることを期待しています。

うまくいかない原因

DBにおいてnullは「存在しない値」ではありません。nullは「不明」な値です。そのため、nullをもとにハンドリングしようとしてもAレコードのnullとBレコードのnullは一致していると判断されず、nullを使用するとアクティブレコードが1つだけという判定ができません。

実装

PostgreSQLの場合

nullの場合に一意制約を作用させる書き方ができるので、次のように書いてください。

CREATE TABLE relation_timelines (
    id SERIAL PRIMARY KEY,
    user_id_1 INTEGER,
    user_id_2 INTEGER,
    start_at TIMESTAMP,
    end_at TIMESTAMP,
    CONSTRAINT uq_user_ids_start_end UNIQUE (user_id_1, user_id_2, start_at, end_at)
);

CREATE UNIQUE INDEX idx_relation_timelines_unique
ON relation_timelines (user_id_1, user_id_2, (end_at IS NULL))
WHERE end_at IS NULL;

MySQLの場合

MySQLの場合はUNIQUEにするための仮想カラムを追加してください。注意点としては、仮想カラムを0にすると有効なレコード、無効なレコードを1つずつしか許容しないので注意してください。

create table relation_timelines
(
    id          int auto_increment primary key,
    user_id_1   int      null,
    user_id_2   int      null,
    start_at    datetime null,
    end_at      datetime null,
    end_at_flag tinyint as (if((`end_at` is null), 1, NULL)),
    constraint uq_user_ids_start_end
        unique (user_id_1, user_id_2, start_at, end_at),
    constraint uq_user_ids_start_end_flag
        unique (user_id_1, user_id_2, end_at_flag)
);

ソースコード

  • なし

終わりに

nullが不明な値であることを今回の失敗で気付けました。MySQLの場合には仮想カラムを使用する一手間があるので、ちょっとたいへんですね。

参考情報

Pythonでflatmapをする

始めに

小ネタ。PythonでNestした配列に対して、flatなデータにしたいと思った時の処理を残します。

環境

  • Python 3.13

実装

構造が2階層だけならfrom itertools import chainlist(chain.from_iterable(input_))を使用する。

from itertools import chain

async def test_01(self):
    input_ = ["1", ["2", "3"]]

    actual = list(chain.from_iterable(input_))

    assert ["1", "2", "3"] == actual

それ以上に複雑な構造をflatにしたい場合は自作関数を使用して再起処理をするのがよい。

def flatten(lst):
    for el in lst:
        if isinstance(el, list):
            yield from flatten(el)
        else:
            yield el

async def test_04(self):
    input_ = [1, [2, [3, [4, 5]]]]

    actual = list(flatten(input_))

    assert [1, 2, 3, 4, 5] == actual

ソースコード

終わりに

Pythonは配列操作が微妙に面倒ですね。pandasとかpolarsとかを使ったほうが楽かもしれませんが、アプリケーションPythonだとわざわざ使うこともないので、さくっとID項目を抽出したいときはchainを使うのがオススメです。