読者です 読者をやめる 読者になる 読者になる

/* あとでなおす */

友達募集中

【Excel計算式】ファイル名とシート名を取得する

ファイル名
=MID(CELL("filename",A1),FIND("[",CELL("filename",A1))+1,FIND("]",CELL("filename",A1))-(FIND("[",CELL("filename",A1))+1))
シート名
=RIGHT(CELL("filename",A1),LEN(CELL("filename",A1))-FIND("]",CELL("filename",A1)))

【Salesforce】レコードが5万件以上存在する2つのオブジェクトに対して一度に処理を行う

表題は厳密には50,001件以上ですが、記事にすると書きにくいので5万件にしています。
Salesforce で開発を行っている方ならご存知の通り SOQL で Salesforce 上の
オブジェクトに対して5万件以上のレコードを一度に取得しようとするとガバナ制限に
引っかかってしまいます。
5万件以上の場合は Salesforce 上では Apex バッチを使用します。

Apex バッチとは

一般的なシステムのバッチのようなもので、大量データを処理するプログラムだと思ってください。
Apex バッチではなくApex 一括処理とも呼ばれています。
「5万件を超えたらApex バッチを使用することをお勧めします。」的な記事を過去に
見かけたような気がしますが、5万件を超えたらApex バッチを使用する以外解決方法は
無いと考えた方が良いかと思います。*1
Apex バッチではガバナ制限が一部緩和され、5万件以上のレコードを一度に取得することが可能です。*2
書き方など細かい点に関しては trailhead 等を参照していただければと思います。

複数オブジェクトは扱えない

Apex バッチは5万件以上のレコードを取得することは可能ですが、複数のオブジェクトへ
同時にアクセスすることはできません。
参照関係または主従関係の場合は可能ですが、そうでもない限り1バッチにつきアクセス
できるのは1オブジェクトまでです。

では複数オブジェクトを扱うにはどうするか?
答えは「1バッチで1オブジェクトしか扱えない」のならばオブジェクトの数だけ
バッチを作成することで複数の5万件以上のオブジェクトに対して処理を行うことが可能です。

サンプル

取引先 (Account) と取引先責任者 (Contact) が5万件以上存在した場合のサンプルです。
取引先と取引先責任者は参照関係があるためバッチを分ける必要はありませんが
例としてバラバラで取得して処理しています。

簡単に説明しますと
 1.取引先を取得し、取得したレコードを取引先責任者のバッチに渡して呼び出し
 2.取引先責任者を取得し、前バッチで渡された取引先と何かしらの処理を実行
といった感じです。
1つ目のバッチが2つ目のバッチを実行しているので、下記の処理を行う場合は1つ目の
バッチさえ呼び出せば処理が完了します。

1.取引先を取得し、取得したレコードを取引先責任者のバッチに渡して呼び出し
public class DoAccount implements Database.Batchable<sObject>, Database.Stateful {
    
    // 取引先のクラス変数
    List<Account> accounts = new List<Account>();
    
    public Database.QueryLocator start(Database.BatchableContext bc) {
        // 取引先取得のSOQL
        return Database.getQueryLocator('SELECT XXX FROM Account');
    }
    
    public void execute(Database.BatchableContext bc, List<Account> scope) {
        // scope には start で記載した SOQL に該当する取引先レコードが
        // 代入されているので、それをクラス変数へ代入
        this.accounts = scope;
    }
    
    public void finish(Database.BatchableContext bc) {
        // 取引先のクラス変数を次の取引先責任者へのバッチへ渡して実行
        DoContact batch = new DoContact(this.accounts); 
        Database.executeBatch(batch);
    }
    
}
2.取引先責任者を取得し、前バッチで渡された取引先と何かしらの処理を実行
public class DoContact implements Database.Batchable<sObject>, Database.Stateful {
    
    // 取引先のクラス変数
    List<Account> accounts = new List<Account>();
    
    public DoContact(List<Account> accounts) {
        // 前の取引先のバッチにて取得したレコードを受け取ってクラス変数へ代入
        this.accounts = accounts
    }
    
    public Database.QueryLocator start(Database.BatchableContext bc) {
        // 取引先責任者取得のSOQL
        return Database.getQueryLocator('SELECT XXX FROM Contact');
    }
    
    public void execute(Database.BatchableContext bc, List<Account> scope) {
        // 何かしらの処理
        // 前バッチと同様 scope には start で記載した SOQL に該当する取引先責任者のレコードが代入されている
        // scope と取引先のクラス変数を用いることでレコードの比較などを行うことが可能
        // ガバナ制限にひっかからなけらば insert, upsert 等の処理を行うことも可能
    }
    
    public void finish(Database.BatchableContext bc) {
        // 終了処理
        // 何かしら必要であれば記載
    }
    
}


取引先 → 取引先責任者の順で処理を実行していますが、上記の例だと逆順でも実行可能です。

まとめ

全然綺麗なやり方ではありませんが、一応一度に複数の5万件以上のオブジェクトから
レコードを取得して処理を行うことが可能です。
今私が行っている仕事では50万件以上のレコードが存在しますが、この方法で処理可能です。

それにしても5万件が大量レコードとは…
Salesfoce, もうちょっとなんとかならんのかね…

*1:分割して無理くりも不可ではないかと思いますが、運用開始後のレコードの増加に耐えられない可能性もあります。

*2:公式通りだと5,000万件まで取得可能です。その前にヒープサイズでエラーになりそうですが…

【Java】JSONObject と JsonObject をパースする

JavaJson(JSONObject と JsonObject) をパースしてみました。
JSONObject(org.json.JSONObject) と JsonObject(com.google.gson.JsonObject) は異なります。
パースの方法も異なります。

JSONObject (org.json.JSONObject)

コード
public static void main(String[] args) {
	String str = "{\"name\":\"Taro\",\"age\":21,\"friend\":[{\"name\":\"Jiro\",\"age\":20}]}";
	try {
		JSONObject json = new JSONObject(str);
		System.out.println(json.toString(4));
	} catch (JSONException e) {
		e.printStackTrace();
	}
}
出力結果
{
    "age": 21,
    "friend": [{
        "age": 20,
        "name": "Jiro"
    }],
    "name": "Taro"
}
補足

json.toString(4)」の 4 は固定です。

JsonObject (com.google.gson.JsonObject)

コード
public static void main(String[] args) {
	String str = "{\"name\":\"Taro\",\"age\":21,\"friend\":[{\"name\":\"Jiro\",\"age\":20}]}";
	JsonParser parser = new JsonParser();
	JsonObject json = parser.parse(str).getAsJsonObject();
	Gson gson = new GsonBuilder().setPrettyPrinting().create();
	System.out.println(gson.toJson(json));
}
出力結果
{
  "name": "Taro",
  "age": 21,
  "friend": [
    {
      "name": "Jiro",
      "age": 20
    }
  ]
}

まとめ

それっぽく出力するのは難しくはないです。
ただしインデントや出力順番が異なりますので注意してください。

【PostgreSQL】テーブルデータに合わせてシーケンスをリセットする

PostgreSQLにてシーケンス番号をリセットするSQLです。

テストデータのINSERTやDELETEを何度も繰り返しているうちに
シーケンス番号とテーブルのレコード件数が合わなくなってしまった時などに使用します。
シーケンス番号とレコード件数は別に合わせなければいけないわけではないですが
「最初の1件目をINSERTしたのにシーケンス番号が3から始まるのは気持ち悪い」とか
「今テーブルに2000件入っているので次にINSERTされるシーケンス番号は2001にしたい」
といった要望が世の中はあるかと思いますので、メモとして残します。

次にINSERTするシーケンス番号が今のレコード件数+1から始まるように設定

SELECT pg_catalog.setval(
  '<対象のシーケンス名>',
  (SELECT COUNT(1) + 1 FROM <対象のテーブル名>),
  false);

次にINSERTするシーケンス番号が今のレコードの最大値+1から始まるように設定

※レコード件数が0件の場合は1から始まるよう設定

SELECT pg_catalog.setval(
  '<対象のシーケンス名>',
  COALESCE((SELECT MAX(<対象の列名>) + 1 FROM <対象のテーブル名>) , 1),
  false);

補足

上記のSQLはSELECT文ですが、実行するとシーケンス番号がUPDATEされます。
また、pg_catalog.setvalはトランザクションの適応外です。
ROLLBACKしてもシーケンス番号は変わってしまいますのでご注意ください。

【Excel】個人的によく使うショートカット

私がExcelでよく使うショートカットです。
コピペ等のショートカットは除いています。

[Ctrl] + [;](セミコロン) … 現在の日付を入力(YYYY/M/D形式)
[Ctrl] + [:](コロン) … 現在の時刻を入力(H:MM形式)
[Ctrl] + [D] … 1つ上のセルをコピー
[Ctrl] + [HOME] … A1セルに移動*1
[Ctrl] + [PAGE UP] … 1つ左のシートに移動
[Ctrl] + [PAGE DOWN] … 1つ右のシートに移動
[Ctrl] + [Shift] + [@] … 計算式表示の切り替え
[Alt] + [F11] … VBAエディタの起動*2

個人的には [Ctrl] + [D] が地味に便利で一番好きです。
他にはファイルを閉じる前に全シートをA1セルに移動するようにしていますがその際は
[Ctrl] を押しながら [PAGE UP(または DOWN)] と [HOME] を交互に連打しています。
他にもあったら教えていただけると嬉しいです。

補足

[Ctrl] + [Shift] + [@]を押すとこんな感じで計算式自体がそのまま表示されます。
もう一度[Ctrl] + [Shift] + [@]を押すと元に戻ります。
f:id:nanashipgmer:20161009204524p:plain

*1:ウィンドウ枠の固定をしている場合はA1セルではなく固定しているセルに移動します。

*2:これはExcelのみではなくMicrosoft Office全般のショートカットです。

【ひとりごと】「!」がしんどい

プログラムにおいて条件式等でよく見かける「if(!条件式)」が個人的にちょっとしんどいと思った記事です。
何がしんどいかっていうと「!」自体が個人的に見づらいです。
この「!」を見落としたするということはすなわち

・本来 True であるべきところが False と解釈する
・本来 False であるべきところが True と解釈する

のどちらかになります。
リーダブルの問題ですむならまだしも最悪なのはコードレビューをした際に記述漏れ等で間違っていたりします。
このバグはどの言語でもクリティカルで、特に「本来 False になるべき条件式が True」になっていたりした場合には言語やシステムの仕様問わずほぼ致命傷になるかと思います。
こういったリーダブルやバグを少しでも抑えるため、私は「if(条件式 == false)」と書きたいのですがあまりこれに賛同してくれる人が周りにいません…

TwitterのTLとか見ていて思ったので何となく思ったので記事にしました。

【セットアップ】Ver 2.0 以降の Android Studio を日本語化する

Android Stuido の日本語化対応時に以下のようなエラーが表示される時があります。*1


Internal Error. Please report to https://https://code.google.com/p/android/issues

com.intellij.ide,plugins.PluginManager$StartupAbortedException: java.lang.reflect.InvocationTargetException
at com.intellij.ide,plugins.PluginManager$2run(PluginManager.java.94)
at java.lang.Thread.run(Thread.java.745)

f:id:nanashipgmer:20160703032657p:plain

これはAndroid Studio 2.0以降で出現するエラーです。
ネット上で多くヒットするyuuna様のGitHubにあるIDEA_resources_jpのjarファイルを使用して
日本語化する方法ですが、残念ながらこのjarファイルはAndroid Studio 2.0以降には対応されて
いないため、上記のようなエラーが出てしまいます。

諦めておりましたが、寿司すき焼き相撲様が2.0以降対応のjarファイルを作成してくださいました。
ありがとうございました。
対応方法自体は2.0より前と同じですが、念のため記載しておきます。

【1】文字化け対応する

Android Studioを起動して[File]→[Setting]で[Override default fonts by (not recommended)]にチェックを入れます。
これは日本語化対応自体ではなく日本語にした際の文字化けの対応です。
非推奨(not recommended)になっておりますがチェックして問題ありません。

f:id:nanashipgmer:20160703035626p:plain

【2】Android Studioを閉じる

【3】resources_jp.jarファイルを置く

寿司すき焼き相撲様のサイトからダウンロードしたresources_jp.jarファイルをAndroid Studioをインストールしたlibフォルダに移動(もしくはコピペ)します。
libのフォルダはAndroid Studioインストール時に指定したフォルダですが、特にデフォルトから変更していない場合は「C:\Program Files\Android\Android Studio\lib」になっているかと思います。

f:id:nanashipgmer:20160703041045p:plain

【4】Android Studioを起動する

日本語になっていますので、これで作業完了です。

f:id:nanashipgmer:20160703041133p:plain


一部日本語化されていない部分(特にビルド時のデバイスの設定画面やエラーダイアログ等)はありますのでご注意ください。

*1:バグ報告のURLがhttps://https://になってるけど気にしない。