なかなかどうして、この有様よ。

へたれ系PG/SEの技術系メモ+育児日記。

入力必須の「*」マークをCSSで適用する

概要

入力必須のマークを直接HTMLに記載するのではなく、CSSのclassとして各項目への設定を楽にする。

ソースと注釈

CSSを利用せずにHTMLに直接Styleを設定すると、以下のようなタグを毎回記載する必要があり、デザインの修正などに手間がかかる。

<th>
   注文日nbsp;<font color="red">*</font>
</th>

そのため、CSSに以下のようなclassを用意し、:afterという疑似要素に必須マークで設定する内容を記載すると、classが設定された要素の後ろに記載された内容を自動的に付与してくれる。

.required:after { 
   content: " *";
   color: red;
}

htmlでは、class名を記載するだけで表示される。

<th class="required">
   注文日
</th>
<th>
   <p class="required">
       品番
   </p>
</th>

参考URL

git logを使ってコミット履歴をcsv出力する

概要

Gitのコミットログから、修正内容などをExcelなどで一覧化したい場合に使うと便利なコマンド。

ソースと注釈

git-bash.exeを利用して、以下の内容をコマンドラインに入力すると、指定した場所にcsvファイルを出力できる。

以下コマンド引数と注釈。

  • --no-pager …ログをページャーを利用せずに表示する。
  • --since=2019-11-01 …指定した期間からログを取得する。
  • --until=2019-12-25 …指定した期間までのログを取得する。
  • --pretty=format: …ログのフォーマットを指定する。

    • %ad--date=オプションに従った形式のAuthorの日付
    • %h …コミットのハッシュ(短縮版)
    • %an … Authorの名前
    • %s …件名
    • %b …本文
  • --date=short …ログに表示する日付の表示フォーマットを指定する。

  • --no-merges …マージコミットを除く。
  • --date-order …コミット順で表示する。
  • commits.csv …指定したファイル名で出力する。
cd C:\\\\workspace\\xxx
git --no-pager log --since=2019-11-01--until=2019-12-25 \--pretty=format:"\"%ad\",\"%h\",\"%an\",\"%s\",\"%b\"" \--date=short --no-merges --date-order  commits.csv

ちなみに、GitがUTF-8の場合csvをダブルクリックしてExcelで開くと文字化けるので、一旦UTF-8で開けるテキストエディタでShift-JISに変換して保存し、それからExcelで開く必要があるので注意が必要。

参考URL

S2Daoのバインド変数コメントについて

概要

S2Daoで利用するバインド変数コメントについて、たまに触ると毎回忘れるのでメモ。
記載時に変数名とコメントの*との間に半角スペースが入ると、バインド変数コメントと判断せずに通常のSQLコメントとして判断され、引数が設定されない。

ソースと注釈

下記の場合は、empNoの値を引数として設定してSQLを発行してくれる。

SELECT * FROM emp WHERE empno = /*empNo*/7788

しかし、下記のように、変数名とコメントの*との間に半角スペースを入れてしまうと通常のSQLコメントとして判断され、empNoの値を引数として設定せずにSQLを発行する。

SELECT * FROM emp WHERE empno = /* empNo */7788

参考URL

Calendar型からDate形に変換する方法3選

概要

年月日がそれぞれ別の項目(セレクトボックスとか)になっているformの値を、Calendar型経由でDate型に変換する際、設定内容によっては時分秒が想定外の値になる。 なので、その現象と対処方法を記載する。

ソースと注釈

単純にCalendar型からgetTimeをすると、インスタンスを生成したタイミングの時分秒ミリ秒が設定されてしまう。
そのため、時分秒ミリ秒を「00:00:00.000」にする方法を3つ記載する。

  1. Calendar型からgetTime()して取得した日付(時分秒0指定)
     これは、Calendarのインスタンスにsetで年月日を設定する際に時分秒ミリ秒を0で設定する方法となる。
     単純に値を設定する項目が増えるだけなので、一番手軽にできる方法だと思われる。
  2. Calendar型から時分秒ミリ秒をクリアしてgetTime()して取得した日付
     これは、Calendarのインスタンスを一旦clearでクリアし、再度setで年月日を設定する方法となる。
     ただ、clearでは時間のクリアが行えないため、時間はsetで0を設定しなければならない。
     現在日付を利用する際に時分秒ミリ秒のクリアが必要な場合は有効であるが、そうでない場合は手間がかかる方法だと思われる。
  3. Calendar型からSimpleDateFormatで「yyyy/MM/dd」にしてparse()して取得した日付
     これは、SimpleDateFormatでフォーマットを年月日のみにし、その後CalendarのインスタンスからgetTimeした値をSimpleDateFormatで設定したフォーマットに変換し、それを再度SimpleDateFormatでDate形にparseする方法となる。
     parseを行うためTry/Catchかエラーのthrowを記載する必要が発生し、手間がかかるのでどうしてもSimpleDateFormatを利用しないといけない場合以外はあまり利用しない方が無難だと思われる。

以下、サンプルソース
(ブログへのコピーがミスっていなければ、そのままで多分動くはず。)

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;


public class TestCalenderToDate {


    public static void main(String[] args) {


         // 値を設定する
        Integer year = 2020;
        Integer month = 1;
        Integer day = 15;


        System.out.println("設定値");
        System.out.println("年:"+ year.toString());
        System.out.println("月:"+ month.toString());
        System.out.println("日:"+ day.toString());
        System.out.println("-----");
        System.out.println("結果を表示");
         getDate001(year, month, day);
        System.out.println("-----");
         getDate004(year, month, day);
        System.out.println("-----");
         getDate002(year, month, day);
        System.out.println("-----");
         getDate003(year, month, day);
        System.out.println("-----");


    }


    /**
    * Calender型からgetTime()して取得した日付.
    *
    * @param year
    * @param month
    * @param day
    */
    private static void getDate001(Integer year, Integer month, Integer day) {


        // 年月日を生成
         Calendar cal = Calendar.getInstance();
        cal.set(year, month - 1, day);
        Date date = cal.getTime();


        System.out.println("●Calender型からgetTime()して取得した日付");


        System.out.println(date.toString());
    }


    /**
    * Calender型からgetTime()して取得した日付(時分秒0指定).
    *
    * @param year
    * @param month
    * @param day
    */
    private static void getDate004(Integer year, Integer month, Integer day) {


        // 年月日を生成
         Calendar cal = Calendar.getInstance();
        cal.set(year, month - 1, day, 0, 0, 0);
        Date date = cal.getTime();


        System.out.println("●Calender型からgetTime()して取得した日付(時分秒0指定)");


        System.out.println(date.toString());
    }




    /**
    * Calender型から時分秒ミリ秒をクリアしてgetTime()して取得した日付.
    *
    * @param year
    * @param month
    * @param day
    */
    private static void getDate002(Integer year, Integer month, Integer day) {


        // 年月日を生成
         Calendar cal = Calendar.getInstance();


        // 時分秒ミリ秒をクリアする
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.clear(Calendar.MINUTE);
        cal.clear(Calendar.SECOND);
        cal.clear(Calendar.MILLISECOND);


        cal.set(year, month - 1, day);
        Date date = cal.getTime();


        System.out.println("●Calender型から時分秒ミリ秒をクリアしてgetTime()して取得した日付");


        System.out.println(date.toString());
    }


    /**
    * Calender型からSimpleDateFormatで「yyyy/MM/dd」にしてparse()して取得した日付.
    *
    * @param year
    * @param month
    * @param day
    */
    private static void getDate003(Integer year, Integer month, Integer day) {


        // 年月日を生成
         Calendar cal = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        cal.set(year, month - 1, day);


        String str = "●Calender型からSimpleDateFormatで「yyyy/MM/dd」にしてparse()して取得した日付";


        try {
            Date date = sdf.parse(sdf.format(cal.getTime()));


            System.out.println(str);
            System.out.println(date.toString());
        } catch (ParseException e) {
            System.out.println(str);
            System.out.println("日付生成エラー");
        }
    }

}

出力結果は以下の様になる。

設定値
年:2020
月:1
日:15
-----
結果を表示
●Calender型からgetTime()して取得した日付
Wed Jan 15 09:14:50 JST 2020
-----
●Calender型からgetTime()して取得した日付(時分秒0指定)
Wed Jan 15 00:00:00 JST 2020
-----
●Calender型から時分秒ミリ秒をクリアしてgetTime()して取得した日付
Wed Jan 15 00:00:00 JST 2020
-----
●Calender型からSimpleDateFormatで「yyyy/MM/dd」にしてparse()して取得した日付
Wed Jan 15 00:00:00 JST 2020
-----

pomeraDM200を買いました。

概要

どうしても欲しくて買ってしまったpomera DM200を、先行モデルのpomera DM100と比較も含め記載していきたいと思います。

pomera DM200とは

pomeraと言えば、最初は折りたたみキーボードを搭載したデジタルメモですが、先行モデルのpomera DM100は「ストレートタイプキーボード(折りたたまないキーボード)」のシリーズとして誕生しました。
そして、その後継モデルとして誕生したのがpomera DM200になります。
ちなみに、折りたたみキーボードタイプのシリーズも新商品が生産されており、大きく2種類のモデルタイプがあります。

pomera DM200でよかった点

ポメラSyncでスマホとの同期が楽に

pomera DM100でも、QRコードEvernote連携、Flash Airを利用してPCやスマホに連携することはできましたが、手順が多かったりするのでなかなか別の端末に移行するのは大変でした。
ですが、今回pomera DM200では連携機能として「ポメラSync」が増えました。
これは、Googleのアカウントを利用したもので、pomera DM200とスマホに同一のGoogleアカウントでログインすれば、pomera DM200とiPhoneなら「メモ」に連携することができます。

しかし、Googleアカウントの設定で少しややこしかったり、iPhoneでメモにGoogleのアカウントを利用する方法が一部わかりにくかったりする場合がありますが、そこさえ乗り越えれば快適に同期ができます。
また、ポメラSyncは自分で同期処理を行わなければ同期されませんので、pomera DM200を起動したらまずポメラSyncでスマホとの同期を取って、その後pomera DM200で変更を加え、pomera DM200をしまう前にポメラSyncでスマホへ同期を行うようにした方が良さそうです。

アウトライン機能が増えた

pomera DM100では、表(CSV)とテキストメモの2種類のファイルを扱えましたが、pomera DM200では表(CSV)機能はなくなりました。
しかし、pomera DM200ではテキストメモでのアウトライン機能が追加されました。
アウトライン機能はMicrosoft Wordなどにありますし、テキストメモでもMarkdown記法を用いて利用することができます。
そして、pomera DM200ではアウトライン表記の際のルールとしてMarkdown記法の見出し(H1)で利用する"#"を利用することができ、はてなブログなどMarkdown記法で記事を投稿できるブログサービスやスマホのメモアプリなどにそのまま利用できるのでとても便利です。
私はMarkdown記法を日常でよく利用しているので、これは便利と即利用しました。

pomera DM200に不満な点

バッテリー残量の表示

まず、pomera DM200は前回のpomera DM100と異なり内蔵のリチウムイオンバッテリー駆動となっています。
なので、バッテリーが充電中なのかどうか、残量が大体どれくらいなのかは下のバーにバッテリーアイコンで表示されていますが、実際に電池残量が何%なのかは、メニューから「設定」>「本体情報」を開いて確認する必要があります。
充電式になった上、下のバーにバッテリーアイコンが表示されているのであれば、そこに「xxx%」とバッテリー残量を表示してもらえたらいいのに、と思いました。

開いたときの起動の速度

pomera DM200ではOSがLinuxベースの独自OSへ新しくなったからか、それとも私の方で設定が悪いのかはきちんと調べていないので判りませんが、pomera DM100の時より画面を開いてからの起動が遅いように感じました。
pomera DM100では、画面を開けば即電源が入り、すぐに入力を開始できましたが、pomera DM200では画面を開いてしばらくしてEnterを押したら画面が表示されるような感じなので、ちょっと待ち時間が発生してしまいます。
私の設定が悪い可能性もあるので、もうしばらく確認してみようかと思いますが、今のところはこんなものなのかな、と思ったりもします。

最後に

pomera DM100に比べると、pomera DM200は格段に使いやすくなっていると思います。
まだ購入して2日目ですが、文章を書く気力がとても湧いてくるのでpomera DM200を使うのが楽しいです。
もし、また何か気になることや便利だったことがあれば追記や別記事で書いていければな、と思いました。

PostgreSQL シーケンス操作の小技

概要

PostgreSQLのシーケンスでよく使う採番方法と、シーケンス操作や同一のSQLで複数回シーケンスを利用する場合の方法などをメモしています。
シーケンスについては以下ページを参考にしました。

9.16. シーケンス操作関数 - PostgreSQL9.5

シーケンス操作

前提として、PostgreSQLのシーケンスは非トランザクション処理のため、トランザクションロールバックされても採番した値はロールバックされません。

シーケンスで次の値を取得する

シーケンスで次の値を取得したい場合、nextval()関数にシーケンス名を指定してselectを行います。

-- シーケンスの次の値の取得
SELECT nextval('seq_id');

シーケンスの現在値を取得する

シーケンスで現在の値を取得したい場合、currval()関数にシーケンス名を指定してselectを行います。

-- シーケンスの現在値の取得
SELECT currval('seq_id');

2020/01/15 追記

currval()関数については以下のPostgreSQLドキュメントにあるように、セッション内でのnextval()関数で取得した現在値を表示するようです。

currval
現在のセッションにおいて、そのシーケンスから nextval によって取得された直近の値を返します。 (セッション内で、シーケンスに対し nextval が呼ばれていない場合には、エラーが報告されます。)

なので、nextval()関数が同一セッション内で実行されていない場合は表示できません。
(私も知らなかったので、currval()関数で今のシーケンスの値は何だろうと確認しようとしてエラーになって初めて知りました。)

シーケンスの初期化

シーケンスを初期化したい場合は、setval()関数にシーケンス名、シーケンス作成時に指定した範囲内の値を指定してselectを行います。

-- シーケンスの初期化
SELECT setval('seq_id', 1);

また、既にあるテーブルのIDの件数を設定したい場合は、setval()関数内にSQL文を記載して取得した結果を設定することが出来ます。

-- シーケンスの初期化(トランテーブルの件数を設定)
SELECT
    setval( 
    'seq_id'
    , ( 
      SELECT
          COUNT(T.ID) 
      FROM
        TRN_DATA T
    )
  );

2020/01/14 追記

count()関数ではなくmax()関数を使ってみたところエラーとなったので、count()関数のみであれば可能のようです。

同一のSQLで複数のシーケンスを利用する場合

また、同一のSQL文の中でシーケンスを取得してその値を別のカラムにも設定したい、という場合、以下のようなSQL文を記載することで同一の値を利用する事が出来ます。

-- シーケンスので採番+採番した値を別カラムにも利用
SELECT
    nextval('seq_id') as id
  , currval('seq_id') as create_no;

ちなみに、この時どちらもnextval()関数を使用すると、それぞれで採番されるので、同一の値になりません。
そのため、最初にnextval()関数で採番し、それ以降はcurrval()関数で採番後の現在値を利用するようにします。

そして、以下のようにシーケンスで採番した値を0埋めして取得するなども可能になります。

-- シーケンスので採番+採番した値のLpadの0埋め
-- ※ to_char時に先頭にスペースが入るので、一旦trimをかけてからlpadして0埋めする
SELECT
    nextval('seq_id') as id
  , lpad( 
    trim( 
      to_char(currval('seq_id'), '99999')
    ) 
    , 5
    , '0'
  ) as create_no;

SQLで対象のカラムのデータが複数のカラムのデータのいずれかに含まれている場合

ざっくり概要(ざっくり)

最近、SQLを書いていて「とあるマスタテーブルにあるカラムのデータが、とあるトランザクションテーブルにある複数のカラムのうちどこかに含まれている場合は対象にする」というSQLを書く機会があったので、メモとして残しておきます。

「めんどくさい」のは簡潔にしたい

この業界に入って久しいですが、SQLを極めるほどSQL文を書くということはほとんどなかったので、今回のSQLトランザクションテーブルの複数カラムを単純に「Where句にORで全てのカラムを書く」という方法を考えました。
しかし、複数カラムが実は10カラムあったので、それは大変「めんどくさい」ですし、「ORで全てのカラムをバカ正直に条件に書くのはなんかミスりそうでやだなー」という結論に至ったので、いろいろ調べてみたのでした。

そして、こちらの内容を見つけました。
MySQL - sqlで複数のカラムのデータにいずれかを含んでいる状態|teratail
上記の内容を確認すると、SQLでINで条件を設定する際に、対象の複数カラムを設定すればそれを対象として条件検索してくれるということでした。

SQL

以下の構造のテーブルがあったとします。
マスタテーブル(MST_TABLE)にはカラムはID、ITEM_CDとその他カラムがあり、トランザクションテーブル(TRN_TABLE)にはID、ITEM_CD1からITEM_CD10までの連番カラムとその他のカラムがある前提です。

SQL文としては以下になります。

Select 
    T.ID
From 
    MST_TABLE M
  , TRN_TABLE T
Where 
    M.ITEM_CD IN (
           T.ITEM_CD1
          , T.ITEM_CD2
          , T.ITEM_CD3
          , T.ITEM_CD4
          , T.ITEM_CD5
          , T.ITEM_CD6
          , T.ITEM_CD7
          , T.ITEM_CD8
          , T.ITEM_CD9
          , T.ITEM_CD10
        );

これで、SQLで対象のカラムのデータが複数のカラムのデータのいずれかに含まれている場合のデータを取得することができます。