バッチからSQLを実行し、かつSQLにパラメータも渡す方法

データベース

Windowsのバッチファイルを作って、そのバッチファイルからSQLを呼び出すことは多々あるかと思います。
今回は、バッチファイルからSQLを呼び出す方法と、SQLにパラメータを渡す方法について紹介していきたいと思います。


環境


  • DB:PostgreSQL9.6

バッチファイル


まずはバッチファイルです。
WindowsのBATファイルとして準備します。


SET HOST=localhost
SET USERID=postgres
SET PORT=17102
SET DBNAME=SAMPLE_DB
SET PARAM=100

psql.exe -h %HOST% -U %USERID% -p %PORT% -d %DBNAME% -f sample.sql PARAM=%PARAM%

SQLファイルに渡すパラメータは、DBのホスト名やユーザIDと同じように「PARAM」というパラメータを準備しています。


SQLファイル


次にSQLファイルです。
テーブル名はすべてサンプルになります。


BATファイルに定義した「PARAM」を使用するSQLを実行することができます。


SELECT
    id,
FROM
    SAMPLE_TABLE
WHERE
    id = :PARAM

SQLに記載するパラメータは、コロン(:)をキーワード前方に付加することで、SQL内で認識することができます。



mapBoxでポリゴン表示。geoJsonの表示内容について変更する。

MapBox

mapBoxでは、地図上に様々なメッシュの表示をおこなうことができます。
今回は、mapBoxを使ってgeoJsonの表示をおこない、かつ、表示内容をパラメータで変更する方法について紹介していきます。


環境情報


  • ブラウザ:Chrome
  • Mapbox:v2.3.1

ポリゴンの表示


以下のHTMLを記載するだけで地図上にポリゴンが表示されます。
※とはいっても、mapインスタンスの作成は省略しています。


map.on('load', function () {
    map.addSource('POLYGON_TEST', {
        'type': 'geojson',
        'data': {
            'type': 'FeatureCollection',
            'features': [
                {"type": "Feature", "geometry": {"type": "Polygon","coordinates": [[[139.74609375, 35.70556640625],[139.757080078125, 35.70556640625],[139.757080078125, 35.716552734375],[139.74609375, 35.716552734375],[139.74609375, 35.70556640625]]]}},
            ]
        }
    });
    
    map.addLayer({
        'id': 'PLOLYGON_TEST_fill',
        'type': 'fill',
        'source': 'POLYGON_TEST',
        'paint': {
            'fill-color': '#FFFF00',
            'fill-opacity': 0.1
        },
        'minzoom': 1,
        'maxzoom': 22
    });

    map.addLayer({
        'id': 'PLOLYGON_TEST_LINE',
        'type': 'line',
        'source': 'POLYGON_TEST',
        'paint': {
            'line-color': '#0000FF',
            'line-width': 2
        },
        'minzoom': 1,
        'maxzoom': 22
    });
});

「map.addSource」クラスで、geoJsonで表示する座標情報を定義しています。


ポリゴンの表示内容


ポリゴンの表示内容は、fill(塗りつぶし)とline(線)の定義でおこなうことができます。
パラメータとして準備されているのは、以下となります。


■fill(塗りつぶし)

fill-color

塗りつぶしの色

fill-opacity

不透明度(1が最も透明度が高い)

minzoon

表示する最小のズームレベル

maxzoon

表示する最大のズームレベル


■line(線)

line-color

線の色

line-width

線の太さ

minzoon

表示する最小のズームレベル

maxzoon

表示する最大のズームレベル



Amazonアソシエイトの申請から承認まで。却下時の対処方法も。

amazonアソシエイト

Amazonアソシエイトは、ブログ運営者であれば必須で導入しておきたいアフィリエイトプログラムです。
自分のブログにAmazonへのリンクを設置するだけで、ブログの収益化をおこなうことができます。


しかし、Amazonアソシエイトにはブログの審査があり、その審査を通過する必要があります。

今回は、筆者自身が体験したAmazonアソシエイトの申請方法と、却下時の対処、および、承認までの道のりについて紹介していきます


Amazonアソシエイトとは


Amazonアソシエイトとは、成果報酬型のアフィリエイトプログラムになります。


仕組みとしては、以下になります。


  1. Amazonアソシエイトから、紹介したい商品のURLを発行する。
  2. 商品のURLをブログに設置する。
  3. ブログ訪問者がブログに設置したURLを経由して、Amazonで承認を購入する。
  4. 商品の何%の売り上げが、ブログ運営者に支払われる。

つまり、ブログを経由してAmazonで商品が売れれば、ブログ運営者に売り上げをキックバック、という事ですね。


Amazonアソシエイトが良いのは、”紹介した商品以外が売れても売り上げが入る”という点です。
この部分が、Amazonアソシエイトが人気の点でしょう。


申請方法


Amazonアソシエイトへの申請は、とても簡単です。
Amazonアソシエイト・セントラルホーム、という画面から、Webサイトの情報を入力して申請をおこなうのみです。



Webサイトの情報はとても重要です。
自分のブログの情報を正確に入力して申請しましょう。


Amazonアソシエイトの申請

申請が無事に完了すると、以下のようなメールが登録したメールアドレスに送信されてきます。
メールが受信できたら申請完了です。


Amazonアソシエイトの申請完了

申請が完了すると、アソシエイトIDというIDが発行されます。
このIDは後々必要となるので、大事に保存しておきましょう。


却下時の対処方法


Amazonアソシエイトに合格するためには、2つの関門があります。


・1.180日間に3つの売り上げを発生させる。

・2.ブログのAmazon審査を通過する。


1が理由で却下となった場合は、Amazonから以下のようなメールが届きます。

Amazonアソシエイト・プログラムへお申し込みをいただき、ありがとうございます。

に、お客様よりいただいていたアソシエイト・プログラムへのお申し込みが却下されたことを通知するメールを送信しました。

アソシエイト・プログラムではお申し込みが送信された後、Amazon.co.jp における、3つの適格な売上を発生させるため、180 日の期間を設けております。3つの適格な売上とは、3 つの個別の注文のことであり、複数の商品が含まれている 1 つの注文は資格対象ではありません。

残念ながら、お客様のアソシエイトのリンクから、180日以内に十分な数の適格な売上が発生していなかったため、お客様のアソシエイト・プログラム専用アカウントは閉鎖されました。(注文アカウントは閉鎖されていないため、これまで通り、注文いただけます)

アソシエイト・プログラムの目標は、オリジナル性の高いコンテンツを通じて、Amazonサイトへのアクセス流入を増やす準備ができているサイトを運営するお客様との関係を構築することです。これは、Amazon とアソシエイト・プログラムへ参加をしているお客様の間で、相互に有益な関係を作り出します。

今後についても、お客様はいつでも Amazon アソシエイト・プログラムに再申請できます。しかしながら、前述の通り、期間内に適格な売上がない場合は、申し込みは却下となります。

よって、今後申し込みをされる場合は、お客様のウェブサイトが一貫したアクセスを獲得している場合にのみ再申請することをお勧めします。

再申し込みをされる際は、下記のURLの「無料アカウント作成」ボタンよりご申請ください。

https://affiliate.amazon.co.jp/

なお、これまでご利用をいただいていた元のアソシエイト ID は有効ではありません。ウェブサイトにその ID を含むリンクがある場合は、再申請時に割り当てられる新しいアソシエイト ID が含まれるようにリンクを更新する必要があります。

今後ともよろしくお願いいたします。

Amazon.co.jpアソシエイトチームより

この件については、却下時の対処は明確です。
こればかりは、180日間に3つの売り上げを上げるしか対処方法はありません。


ちなみに、筆者は「10,000PV/月」で、この条件を突破しました。

人気のある記事に、記事と関連するAmazon商品の紹介バナーを付ければ、突破できる条件だとは思います。


2が理由で却下となった場合は、Amazonから以下のようなメールが届きます。


お世話になっております。●●●, Amazonアソシエイト・プログラムへの参加申請の審査が終了しましたが、誠に残念ながら、プログラム要件を満たしておりませんでした。
そのため、一時的に承認されていたアカウントを閉鎖いたしました。 なぜですか? 違反内容は次のとおりです。

お客様がウェブサイト上で作成されたAmazon特別リンクのいずれにも、ストアに関連付けられたタグが使用されていないことが判明しました。
そのため、トラフィックのソースを特定できませんでした。これはプログラムポリシーへの違反行為となります。 
こちらから例を探すことができます。 http://sakusaku-techs.com/

どうすればいいですか? 本通知以前に発生した未払い紹介料はすべて支払われます。プログラム要件が満たされましたら、またいつでも再申請をお待ちしております。
その際は、こちらから別途申請を行ってください。 Amazonアソシエイト・プログラムより 詳細については、こちらの参考資料をご参照ください。 
Amazonアソシエイト・プログラム運営規約、アソシエイトヘルプ ご関心をお寄せいただきありがとうございます。 Amazon.jpより

申請理由によってメールの本文は異なると思います。
筆者の場合は、以下の文書に注目して対策を検討しました。


“お客様がウェブサイト上で作成されたAmazon特別リンクのいずれにも、ストアに関連付けられたタグが使用されていないことが判明しました。”


当然、Amazonリンクは設置しています。
それにも関わらず、タブが使用されていない(Amzonリンクが使用されていない)と言われています。


そこで、Amazonリンクは記事についてのみ設置していたのですが、トップ画面にも設置するようにしました。
同様の指摘をもらっているブログ運営者は、記事にだけではなくトップ画面にも設置するようにした方がいいかもしれません。


筆者の場合は、トップページ右下にリンクを設置しました。



再申請から承認まで


ブログの修正が完了したら、再度申請をおこないます。
再度申請をおこない、最初から申請をおこなわなければいけません。


再度、以下2つの条件をクリアする必要があります。


・1.180日間に3つの売り上げを発生させる。

・2.ブログのAmazon審査を通過する。


無事に申請が完了した承認されると、以下のメールが届きます。


Amazonアソシエイト・プログラムにお申し込みいただき、誠にありがとうございます。当プログラムへの参加のお申し込みを正式に確認させていただきましたので、お知らせいたします。今すぐAmazon.co.jpへのリンク作成を開始いただけます。

あなたのアソシエイトIDは●●●です。このIDは各アソシエイトメンバーを識別するために重要なものですので、必ず保管をお願いします。アソシエイト・プログラムへお問い合わせされる際にも、このIDを添えてご連絡ください。

【アソシエイト・セントラルについて】
アソシエイトメンバーは、アソシエイト・プログラム専用の管理画面「アソシエイト・セントラル」にいつでもアクセスできます。アソシエイト・セントラルではあなたのアフィリエイト活動をサポートする様々なツールやレポート、役立つ情報を24時間提供しています。早速ログインしてみましょう。
アソシエイト・プログラムへログイン!  => http://associates.amazon.co.jp

ログイン後は、アソシエイト・プログラムの便利ツールをお試しください。

1)Amazon.co.jpのブログパーツや、リンク作成ツールを利用する
=> http://widgets.amazon.co.jp
2)各種レポートで紹介料の確認をする 
=> https://affiliate.amazon.co.jp/gp/associates/network/reports/main.html
3)アカウントサービスで登録情報を変更する
=> https://affiliate.amazon.co.jp/gp/associates/network/your-account/main.html

このメールを受信すると、めでたくAmazonアソシエイトに合格です。

正式に承認されたので、Amazonアソシエイトの様々な機能を利用することができます。


筆者の場合、Amazonリンクをトップページ右下に設置して再度申請することで、承認を得ることができました。

これが理由だったかは正確にはわかりませんが、事実、却下になった時と合格した時のブログのリンク設置状況は、トップページにAmazonリンクが存在したかどうか?違いです。
同じような理由でAmazonアソシエイトに合格できない人は、参考にしてみてください。



ブログをはじめるならレンタルサーバ。ロリポップを2年使ってきた感想

CSS

このブログはロリポップレンタルサーバを使用しています。
筆者は、このブログが初めてのブログなのですが、レンタルサーバを借りたのも初めてです。


これからブログを始めようと思っている人向けに、レンタルサーバの必要性や、ロリポップレンタルサーバを使ってきた感想について書いていきます。


筆者はロリポップレンタルサーバしか使ったことがないのですが、できるだけ他レンタルサーバとの差についても説明します。
結論としては、ロリポップお勧めですよ!っていう内容になっています。


ブログをはじめるならレンタルサーバ


ブログサービスとレンタルサーバ


ブログをはじめる際、”サーバをどうするか?”について、まず決めなければいけないです。
大きく分けて、ブログサービスを使うか?自分でレンタルサーバを借りるか?の2通りあります。


ブログサービス


まず、ブログサービスについて説明していきます。

ブログサービスを使う場合、以下のようなサービスがあります。


  • Amebaブログ
  • FC2ブログ
  • 楽天ブログ
  • Qiitaブログ

各ブログサービスの特色は以下になります。


Amebaブログ


一度は名前はきいた事がある人も多いかと思います。

日本で最もメジャーなブログサービスかと思います。

有名人も多く登録していますね。

メジャー度からするとナンバーワンですね。


FC2ブログ


運営元はアメリカの会社になります。

主な特徴は、他ブログサービスと比べてデザインの変更自由度が高いところですね。

また、ブログサーブスには珍しく、営利目的(アフィリエイトなど)でのブログ運営が認めらている点についても特色の一つとなります。


楽天ブログ


楽天ブログは、その名のとおり楽天が運営するブログです。

楽天市場との親和性が高いので、楽天一番の商品の紹介によってアフィリエイトをおこない、楽天スーパーポイントを貯めたりすることができます。


ここまで紹介したブログ以外でも同じですが、各ブログサービスでアカウントを作成さえすれば、すぐにブログを始めることができます。
手軽さでいけば、圧倒的にブログサービスを使った方がブログをはじめるスピードは速いです。


レンタルサーバ


ブログを始めるためのもう一つの方法が、自分でレンタルサーバを借りて、そのレンタルサーバ上にブログを構築する方法です。


レンタルサーバはいろいろありますが、代表的なものは以下になります。

  • ロリポップ
  • エックスサーバ
  • さくらのレンタルサーバ

レンタルサーバを借りてブログを始める場合、”借りたらすぐにブログを開始”という訳にはいかないです。
自分で、ブログ環境を構築する必要があります。
ブログ環境を構築するのに有名なソフトウェアがWordPressです。


よく、“WordPressブログでブログを始める”といった表現をする人がいますが、正式にはニュアンスが違います。
正式には、”レンタルサーバを自分で借りて、自分でWordPressをインストールしてブログを始める。”が正解です。


WordPressをインストールというと、何か難しいような印象がありますが、これは極めて簡単です。
インストールウィザードに沿って進めれば、インストールに失敗することはないでしょう。


なぜ、WordPressがこんなに普及しているかと言いますと、“一番有名だから。”という理由だけです。
日本でもメジャーなので、困った時のトラブルシューティングについても、ネット上に豊富に公開されています。
世界のWebサイトの約40%がWordPressで構築されているみたいですね。


WordPressではなくても、ブログサイトは構築することができます。
WordPressは、ジャンルでいくとCMS(Contents Management System)になります。
CMSでいくと、WordPress以外い以下のようなものがあります。

  • Drupal
  • Shopify

筆者は、Drupalは使ったことがあります。
まあ、WordPressと似たような構成ではありますが。。。


WordPress以外にもCMSは世の中にあふれていますので、何が何でもWordPressを使わなければいけないという訳ではありません。
しかし、やっぱりメジャーなものを使っておいた方が無難です。
何か問題が発生した場合に、解決策がみつかるスピードが段違いに違うので。


レンタルサーバを使った方がいい理由


ブログを始める理由によりますが、圧倒的にレンタルサーバを自分で借りてブログを始めたほうがいいです。
一番の理由は、“自由度の違い”です。


レンタルサーバは、その名の通り、世界のどこかに(クラウドに)存在しているサーバ領域を借りているという事です。
自分のスペースを確保している訳ですね。
なので、どういった目的でそのサーバを使っても自由ですし、アドセンスやアフィリエイトを使用してお金を稼ぐことも自由です。


しかし、ブログサービスの場合は、そうはいきません。
ブログサービスは、いわば、ブログ運営会社(AmebaやFC2)にブログ記事を載せるスペースを提供してもらっているだけです。
ブログ運営会社のルールに沿ってブログ記事を書かなればいけません。


ブログサービスによっては、アドセンスやアフィリエイトを使ったマネタイズ行為は禁止しているサービスもありますし、いきなり運営会社からアカウントを凍結させられる場合もあります。


また、ブログデザインの点でもレンタルサーバを借りてWordPressで構築した方がよいです。
WordPressでブログを構築する際、テーマというものを選択します。
このテーマは、ブログの基本デザインとなり、無料で様々なテーマが公開されています。
自分で気に入ったテーマを選択することで、デザインを自由に設定することができます。


ブログサービスの場合は、そのブログサービスの基本デザインに沿ってブログ記事を作ることになります。
自由にデザインを変更できないという点でも、ブログサービスをお勧めしない理由です。


結局は、ブログを始める目的によって変わりますね。
一秒でも早くブログを開始したくて、アドセンスやアフィリエイトといったマネタイズは今後もおこなわないということであれば、ブログサービスを使った方がよいでしょう。

しかし、デザインも自分で自由に変更したいし、ゆくゆくはマネタイズしていきたいのであれば、圧倒的にレンタルサーバを借りてブログを始めることをお勧めします。


ロリポップを2年使ってきた感想


短刀直入に言うと、ロリポップは初めてブログを使う人にとっては最適、です。


とにかく安い、です。


ロリポップは、用途に合わせて以下の5つのプランがあります。
以下、安い順です。

  • エコノミー
  • ライト
  • スタンダード
  • ハイスピード
  • エンタープライス

筆者はスタンダードを使っていますが、性能・容量ともに十分です。
何も不都合ありません。
それなのに月額550円ですからね。


正直なところ、始めてブログを作る人は、最初からアクセスが頻繁に発生する訳ではないので、ライトから始めてアクセスが増えてきたらプランをあげていく方式でもいいかと思います
筆者はいきなりスタンダードから契約したのですが、その理由は、電話サポートの有無です。
電話サポートは、スタンダードプランからしかないので。


でも実際は、2年ロリポップを使っていますが、電話サポートが必要な場面はありませんでした。
なので、今となってはライトから始めてもよかったかなと思っているくらいです。

※エコノミーはWordPressが使えないので、エコノミーから始めるのはやめておいた方がいいですね。


また、ファイルのアップロードやダウンロードをおこなう場合、SSHでの接続が必須だと思いますが、SSHもデフォルトで使えます
SSHでの接続設定をおこなう必要があるのですが、SSH接続をおこなっても別途領域が発生することはありません。


ドメインを取得する


ブログを始めようとした場合は、同時に独自ドメインを取得した方がよいです。
ドメインとは、URLの「http://xyz.com」の「xyz.com」の部分です。


独自ドメインを取得しなかった場合でもブログの公開はおこなうことができますが、ロリポップが提供するデフォルトドメインとなります。
このままでもいいですが、SEO等の観点から、独自ドメインを取得することをお勧めします。


このドメインは、インターネットの世界でユニークなものでなければいけないので、ドメイン提供サービスから使用されていないドメインを検索して取得する必要があります。
お勧めとしてムームドメインです。
ロリポップと同じGMOパペポが運営しているので、ロリポップとの連携がとてもスムーズです。


また、12ヵ月以上契約する場合の限定ですが、ロリポップでレンタルサーバを契約している場合、ムームードメインのドメイン料金は無料です。



ロリポップの料金形態


最後にロリポップの料金形態です。
前述させてもらいましたが、はじめてブログを始める人はスタンダードプランがおすすめです。


ハイスピードは、最初からは不要だと思います。
心配しなくても、そんなにアクセスが集中することはありません。
ブログが育ってきて、契約プランの見直しをおこなう必要があった場合にプランの変更をおこなえばいい、くらいのスタンスでよいかと思います。
個人でブログをおこなう分には、まずエンタープライズは不要です。


エコノミー

110円

ライト

220円~

スタンダード

550円~

ハイスピード

550円~

※2021/9/11現在 キャンペーン中のようです。

エンタープライズ

2,200円~


「~」というのは理由があります。
様々なオプションを付けることができるという感じなんですね。


筆者はバックアップオプションのみ付けています。
月額330円になります。
こういったオプションをつけていくと、上記プランに料金が上乗せされていきます。


筆者は、スタンダードプラン+バックアップオプション付きなので、880円/月になります。
始めてブログを始めるには、お手軽な値段かと思います。




Mapboxのスタイルを変更する。日本語の地図表示に変更。

MapBox

WEBアプリケーションを使用しての地図表示で、一番使い勝手がよい(メジャーな)のはGoogleMapかと思います。
しかし、Mapboxも非常に使い勝手がよく、地図表示においてGoogleMapと遜色がない機能を備えてあります。


今回は、Mapboxを使って地図表示を行う上での重要なポイントである、地図スタイルの変更について方法を紹介していきます。


環境情報


  • ブラウザ:Google Chrome
  • Mapbox:2.3.1

スタイルの取得


Mapboxのダッシュボード画面から、「mapBoxStudio」に移動します。

Mapboxに移動する

スタイルの検索で、”Japan”を入力して検索します。
“Mapbox Japan”がみつかりますので、クリックしてスタイル編集画面に移動します。

Mapboxのスタイル検索

スタイル編集画面の右上の「共有」ボタンを押下して、共通ウィンドウを表示します。

Mapboxのスタイル共有

共有ウィンドウで「スタイル URL」をコピーします。
このURLをMapboxのAPIに組み込みをおこないます。

MapboxのURL取得

APIへの組み込み


「スタイル URL」を、MapboxのAPIに設定します。


JavaScriptの「mapboxgl.Map」インスタンス作成時のパラメータに『style』がありますが、このパラメータを変更します。


var map = new mapboxgl.Map({
    container: 'map',
    center: [139.7671563757408, 35.69760688745724],
    zoom: 13,
    style: '※コピーしたURL'
});

では地図表示をおこなってみましょう。
地図表示が日本語表示となっています。

Mapboxでの地図表示


SQLの結合3パターン。クロスジョインはカラム列からレコード行への変換で使おう。

データベース

SQLのテーブル結合は、以下の3種類の方法があります。


  • 内部結合
  • 外部結合
  • クロス結合

取得したいデータの内容によって、結合の方法を使い分ける必要があります。


今回は、3種類の結合方法についてPostgreSQLを使って解説していきます。


環境情報


  • DB:PostgreSQL9.6

テーブル定義と格納データ


解説で使用するテーブル定義と格納データは以下になります。

『役職テーブル』と『社員テーブル』の、2つのテーブルを想定します。


create table yakusyoku (
  yakusyoku_id serial not null
  , yakusyoku_name varchar(10) not null
  , constraint yakusyoku_pkey primary key (yakusyoku_id)
) ;

create table syain (
  syain_id serial not null
  ,yakusyoku_id integer
  , syain_name varchar(10) not null
  , constraint syain_pkey primary key (syain_id)
) ;
"yakusyoku_id"	"yakusyoku_name"
-----------------------------------
1				"部長"
2				"課長"
3				"係長"
4				"主任"
"syain_id"	"yakusyoku_id"	"syain_name"
----------------------------------------------
1			1				"山田部長"
2			2				"鈴木課長"
3			3				"佐藤係長"
4			3				"佐藤係長"

内部結合/依存関係ありのデータを取得する


内部結合は、依存関係(レコード間のリレーション)があるデータのみを取得します。
今回の例では、役職が付いている社員のみが取得できることになります。


SELECT
    A.yakusyoku_id	
    A.yakusyoku_name	
    B.syain_id	
    B.syain_name
FROM
    yakusyoku as A INNER JOIN syain as B 
ON 
    A.yakusyoku_id = B.yakusyoku_id;
"yakusyoku_id"	"yakusyoku_name"	"syain_id"	"syain_name"
-----------------------------------------------------------
1				"部長"				1			"山田部長"
2				"課長"				2			"鈴木課長"
3				"係長"				3			"佐藤係長"
3				"係長"				4			"佐藤係長"

外部結合/依存関係なしのデータも取得する


内部結合は、依存関係がないデータのみも取得します。
今回の例では、主任の社員はいないのですが、役職テーブルのメインテーブルとして外部結合をおこなっているので、主任レコードも取得できます。


SELECT
    A.yakusyoku_id	
    A.yakusyoku_name	
    B.syain_id	
    B.syain_name
FROM
    yakusyoku as A LEFT OUTER JOIN syain as B 
ON 
    A.yakusyoku_id = B.yakusyoku_id;
"yakusyoku_id"	"yakusyoku_name"	"syain_id"	"syain_name"
-----------------------------------------------------------------
1				"部長"				1			"山田部長"
2				"課長"				2			"鈴木課長"
3				"係長"				3			"佐藤係長"
3				"係長"				4			"佐藤係長"
4				"主任"				""			""

クロス結合/組み合わせを取得する


クロス結合の基本文法


クロス結合は、レコード同士の組み合わせを取得します。
今回の例では、全ての役職と全ての社員の組み合わせを取得できます。


結合条件も指定しないので、役職テーブルの主任レコードに対する組み合わせについても取得できます。


SELECT
    A.yakusyoku_id	
    A.yakusyoku_name	
    B.syain_id	
    B.syain_name
FROM
    yakusyoku as A CROSS JOIN syain as B;
"yakusyoku_id"	"yakusyoku_name"	"syain_id"	"syain_name"
--------------------------------------------------------------
1				"部長"				1			"山田部長"
1				"部長"				2			"鈴木課長"
1				"部長"				3			"佐藤係長"
1				"部長"				4			"佐藤係長"
2				"課長"				1			"山田部長"
2				"課長"				2			"鈴木課長"
2				"課長"				3			"佐藤係長"
2				"課長"				4			"佐藤係長"
3				"係長"				1			"山田部長"
3				"係長"				2			"鈴木課長"
3				"係長"				3			"佐藤係長"
3				"係長"				4			"佐藤係長"
4				"主任"				1			"山田部長"
4				"主任"				2			"鈴木課長"
4				"主任"				3			"佐藤係長"
4				"主任"				4			"佐藤係長"

クロス結合の使いどころ。横→縦変換


クロスジョインはあまり用途がないように思いますが、よく使う用途としては横→縦変換になります。


横→縦変換とは、カラム毎に格納されている値をレコード事に格納することです。
以下のようなイメージですね。



以下に具体的を使って説明しています。

テーブルの横→縦変換をおこなってみます。


create table col_test (
  col_test_id serial not null
  , name1 varchar(10) not null
  , name2 varchar(10) not null
  , name3 varchar(10) not null
  , name4 varchar(10) not null
  , name5 varchar(10) not null
  , constraint col_test_id_pkey primary key (col_test_id)
) ;
	"name1",	"name2",	"name3",	"name4",	"name5"
--------------------------------------------------------------
1,	"名称1_A",	"名称2_B",	"名称3_C",	"★名称4_D","名称5_E"
2,	"★名称1_B","名称2_B",	"名称3_B",	"名称4_B",	"名称5_B"
3,	"名称1_C",	"★名称2_C","★名称3_C","名称4_C",	"名称5_C"

このテーブルから、名称の先頭に「★」が付いている値を行データとして取得するとします。 この場合、縦→横変換をおこなうと便利です。


縦→横変換をおこなうために、テンポラリテーブル(TEMPORARY TABLE)
を使っています。
テンポラリテーブルで、1~5の値が入っているのみのテーブルを作成しています。


このテンポラリテーブルと対象テーブル(ここではpivot)をCROSS JOINを使って組み合わせを作成し、その組み合わせで名称の先頭に「★」ついているレコードのみを取得しています。


-- テンポラリテーブルの作成
CREATE TEMPORARY TABLE pivot (
 val integer PRIMARY KEY
);

-- テンポラリテーブルに1~5の値が入った状態にする
INSERT INTO pivot with recursive rec(val) as (
 values(1)
 union all
 select 
    Val+1
 from 
    rec
 where 
    Val+1 <= 5
) select Val from rec;

-- 名称の先頭に「★」がついたレコードのみを抽出
SELECT * FROM (
    SELECT
        case P.val
            when 1 then A.name1
            when 2 then A.name2
            when 3 then A.name3
            when 4 then A.name4
            when 5 then A.name5
        end as cross
    FROM
        col_test as A CROSS JOIN pivot P) AS X
WHERE
    X.cross like '★%';
"cross"
-------------------
★名称1_B
★名称2_C
★名称3_C
★名称4_D


PythonをApache上でCGIとして動作させる方法

apache

Pythonを使ってWEBアプリケーションを構築する場合、Apacheを使ってCGIとしてPythonを動作させるのが一般的です。
PythonをApache上で動作させるには、PythonとApacheをインストールして、Apacheの設定ファイルを環境にあわせて修正するだけでよいです。


今回は、PythonをApache上で動作させる方法について紹介していきます。


環境情報


  • OS:Windows10
  • Apache:Apache 2.4.41
  • Python:Phton3.9

Pythonスクリプトの格納


まず、使用するPythonスクリプトファイルをApacheの所定の場所に格納します。
ApacheのCGIとしてPythonを動かすので、Apacheのルートディレクトリに存在する「cgi-bin」に格納します。


PythonをCGIとして動作させる場所

今回は、以下の”Hello,World!”を表示するだけのスクリプトで動作確認をおこないます。


#! C:\Python34\python

print('Content-Type: text/html')
print('')
print('Hello,World!')

1行目は、Phthonのインストールパスを記載する必要があります。
筆者のマシンでは、「C:\Python34\python」にPythonをインストールしています。


Apacheのインストールについては省略しましたが、以前書いた記事があるので、そちらを参照してください。


Apache設定ファイルの修正


次に、PythonをApacheのCGIとして動かすためにApacheの設定ファイルを修正します。
修正するのは、Apacheの設定ファイルである「httpd.conf」になります。


筆者のマシンのApacheは「C:\Apache24」にインストールしているのですが、「httpd.conf」の格納先は以下になります。


・C:\Apache24\conf\httpd.conf

「httpd.conf」の修正箇所は大きくわけて2カ所になります。 まず、Scriptエイリアスの設定で、「cg-bin」フォルダをcgi実行スクリプトフォルダとして有効にします。


<IfModule alias_module>
    #
    # Redirect: Allows you to tell clients about documents 
    # exist in your server's namespace, but do not anymore.  
    # will make a new request for the document at its location.
    # Example:
    # Redirect permanent /foo http://www.example.com/bar

    #
    # Alias: Maps web paths into filesystem paths and is used to
    # access content that does not live under the DocumentRoot.
    # Example:
    # Alias /webpath /full/filesystem/path/
    #
    # If you include a trailing / on /webpath then the server will
    # require it to be present in the URL.  You will also likely
    # need to provide a <Directory> section to allow access to
    # the filesystem path.

    #
    # ScriptAlias: This controls which directoriesscripts. 
    # ScriptAliases are essentially the same as
    # documents in the target directory are treated 
    # run by the server when requested rather than as 
    # client.  The same rules about trailing "/" apply
    # directives as to Alias.
    #
    ScriptAlias /cgi-bin/ "${SRVROOT}/cgi-bin/"
</IfModule>

通常、上記の設定はデフォルトで有効になっているので、特に設定変更する必要はないかもしれません。


もう1か所の変更箇所が、「cgi-bin」フォルダのディレクトリ設定です。
拡張子「.py」をスクリプトファイルとして認識します。

以下の例では、「.py」ファイルだけではなく「.cgi」「.pl」もスクリプトファイルとして設定しています。


<Directory "${SRVROOT}/cgi-bin">
   AllowOverride none
   AddHandler cgi-script .cgi .pl .py
   Options +ExecCGI
   Require all granted
</Directory>

上記の設定をおこなった後にApacheを再起動し、ブラウザでアクセスして格納したPythonスクリプトが動作するかを確認します。


以下の画面が表示されれば設定成功です。



PythonスクリプトとHTMLを共存させる場合


「cgi-bin」下にはスクリプトファイル(上記例では「.cgi」「.pl」「.py」)だけではなく、通常のHTMLファイルも格納したいことはあると思います。
その場合は、「cgi-bin」のディレクトリ設定で「AddHandler text/html」の設定をおこない、.htmlも有効にする必要があります。


以下の例では、「cgi-bin」下に「.html」「.htm」「.txt」「.css」「.js」の配置を許可しています。


<Directory "${SRVROOT}/cgi-bin">
   AllowOverride none
   AddHandler cgi-script .cgi .pl .py
   AddHandler text/html .html .htm .txt .css .js
   Options +ExecCGI
   Require all granted
</Directory>

この設定をいれることで、「cgi-bin」下にスクリプトファイル以外の静的HTMLについても格納可能になります。




画像からExif情報を抜き出すJavaプログラム

Java

スマートフォンやデジタルカメルで撮影した画像のメタ情報として、Exifという情報があります。
このExif情報には、画像を取った位置情報や画像の確度といった、様々なメタ情報が格納されています。


今回は、このExif情報をJavaで取得する方法を紹介していきます。


環境情報


Java:Java1.8.0_281


Exif情報の取得プログラム


Exif情報の取得サンプルプログラムは以下になります。
出力結果が解り易いように、出力結果を「Exif IFD0」「Exif SubIFD」「GPS」の3つのカテゴリ毎に出力をおこなっています。


サンプルプログラムではExif情報を「Metadata」に格納し、「getDirectories()」でメタ情報を取得していきます。
Exif情報の代表的な以下3つの情報を、標準出力しています。


項目

Tagクラスのメソッド

出力内容

タグタイプ

getTagType()

Exif情報のID

タグ名

getTagName()

タグのタイプ(名称)

説明

getDescription()

設定内容


import java.io.File;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.Tag;

public class Exif {

    /** Exif IFD0 */
    private static final String STR_IFD = "Exif IFD0";

    /** Exif SubIFD */
    private static final String STR_SUBIFD = "Exif SubIFD";

    /** GPS */
    private static final String STR_GPS = "GPS";

    // メイン処理
    public static void main(String[] args){

        System.out.print("start: Exif\r\n");

        try {

            /** 対象ファイルを読み込み */
            File file = new File("./sample.jpg");
            Metadata metadata = 
                ImageMetadataReader.readMetadata(file);

            /** ディレクトリ情報でループ */
            for (Directory directory : metadata.getDirectories()) {
                String dirName = directory.getName();

                /** Exif IFD0 */
                if (STR_IFD.equals(dirName)) {
                    System.out.print(
                        "------directoryName=" + dirName + 
                        "------\r\n");
                    for (Tag tag : directory.getTags()) {
                        String tagName = tag.getTagName();
                        int tagType = tag.getTagType();
                        String desc = tag.getDescription();
                        System.out.print(
                            Integer.toString(tagType) + ":" + 
                            tagName + "=" + desc + "\r\n");
                    }
                }

                /** Exif SubIFD */
                if (STR_SUBIFD.equals(dirName)) {
                    System.out.print(
                        "------directoryName=" + dirName + 
                        "------\r\n");
                    for (Tag tag : directory.getTags()) {
                        String tagName = tag.getTagName();
                        int tagType = tag.getTagType();
                        String desc = tag.getDescription();
                        System.out.print(
                            Integer.toString(tagType) + ":" + 
                            tagName + "=" + desc + "\r\n");
                    }
                }

                /** GPS */
                if (STR_GPS.equals(dirName)) {
                    System.out.print(
                        "------directoryName=" + dirName + 
                        "------\r\n");
                    for (Tag tag : directory.getTags()) {
                        String tagName = tag.getTagName();
                        int tagType = tag.getTagType();
                        String desc = tag.getDescription();
                        System.out.print(
                            Integer.toString(tagType) + ":" + 
                            tagName + "=" + desc + "\r\n");
                    }
                }
            }
        } catch (Exception e) {
            System.out.print("occured exception \r\n");
        }

        System.out.print("end: Exif\r\n");
    }
}

Exif情報の取得例


Exif情報の取得例を以下に説明します。
今回は、ネットで公開しているExif情報が格納されている画像に対してサンプルプログラムを実行しています。


Exif情報を確認すると、画像を撮影した位置情報について格納されていることが確認できます。
一番下に出力されている「0:GPS」~「27:GPS」の情報になります。



start: Exif
------directoryName=Exif IFD0------
270:Image Description=SA390025
271:Make=KDDI-SA
272:Model=W51SA
274:Orientation=Top, left side (Horizontal / normal)
282:X Resolution=72 dots per inch
283:Y Resolution=72 dots per inch
296:Resolution Unit=Inch
306:Date/Time=2008:07:08 15:32:08
531:YCbCr Positioning=Center of pixel array
------directoryName=Exif SubIFD------
33437:F-Number=f/3.0
36864:Exif Version=2.20
36867:Date/Time Original=2008:07:08 15:32:08
36868:Date/Time Digitized=2008:07:08 15:32:08
37121:Components Configuration=YCbCr
37378:Aperture Value=f/2.8
37379:Brightness Value=1
37381:Max Aperture Value=f/2.8
37382:Subject Distance=0.0 metres
37383:Metering Mode=Average
37384:White Balance=Unknown
37385:Flash=Flash did not fire, auto
37386:Focal Length=4.8 mm
40960:FlashPix Version=1.00
40961:Color Space=sRGB
40962:Exif Image Width=1600 pixels
40963:Exif Image Height=1200 pixels
41729:Scene Type=Directly photographed image
41985:Custom Rendered=Normal process
41986:Exposure Mode=Auto exposure
41987:White Balance Mode=Auto white balance
41988:Digital Zoom Ratio=1
41989:Focal Length 35=28 mm
41990:Scene Capture Type=Standard
41992:Contrast=None
41993:Saturation=None
41994:Sharpness=None
41996:Subject Distance Range=Unknown
------directoryName=GPS------
0:GPS Version ID=2.200
1:GPS Latitude Ref=N
2:GPS Latitude=35° 42' 2.91"
3:GPS Longitude Ref=E
4:GPS Longitude=139° 45' 57.52"
18:GPS Map Datum=WGS-84
27:GPS Processing Method=65 83 67 73 73 0 0 0 71 80 83 45 70 73 88
end: Exif


バッチ処理の二重起動チェックをシェルだけで実現する方法

Linux

Linux系のОSでバッチを起動する場合は、シェルを作成して、シェルからJavaなどのプログラムを呼び出すケースが多いかと思います。


バッチで問題になるのが「バッチの重複起動」になります。
“バッチ突き抜け”とも言いますが、1つのバッチが終了する前に同じバッチが起動してしまうと二重起動の状態になってしまい、思わぬ不具合が発生してしまいます。
この場合、プログラム側で重複起動をおこなわないような制御をおこなう必要があります。


今回は、このバッチ重複起動チェックをシェルだけで実現する方法を紹介します。

シェルだけで実現する方法なので非常にシンプルですが、確実に二重起動チェックを防ぐことができます。


環境情報


  • OS:CentOS 7.6

pgrepコマンドとは


重複起動チェックで使用するコマンドは「pgrep」コマンドです。
「pgrep」コマンドは、プロセスが保持しているプロセス名属性からプロセスIDを取得するコマンド
になります。


要は「pgrep」コマンドは、「ps」コマンドと「grep」を合体したコマンドですね。
psコマンドの出力結果をパイプ(|)で繋いでgrepで抽出することが多いかと思いますが、この操作を同時に実行することが「pgrep」コマンドになります。


基本的な文法は以下になります。

pgrep [オプション] [プロセスの特定パターン]

二重起動チェックのシェルプログラム


二重起動チェックのシェルプログラムは以下になります。
「pgrep」コマンドで自身のプロセス名のプロセス番号を取得し、そのプロセス番号と自身のプロセス番号と比較しています。
プロセス番号の比較結果が異なる場合は、自身のプロセスと同名の他プロセスが動作していると判断する、ということですね。


#!/bin/sh

echo "start"
PGREP=`pgrep -f $0 -o`
PGREP_RSLT=$?

if [ $$ != $PGREP  ];
then
  echo "$0は起動中です。二重起動チェックです。"
  exit 1
fi

sleep 180
echo "end"

上記サンプルプログラムを同時に実行すると、一方のシェルはチェックに掛かって起動できません。
二重起動チェックにかかりやすいように、180秒のスリープをおこないバッチ実行中の時間を長くとっています。


pgrepコマンドのオプションと特定パターン


二重起動チェックで使用している「pgrep」コマンドのオプションや、使用している特殊変数について説明していきます。


まずは「pgrep」コマンドのオプションです。
二重起動チェックで使用しているのは「-f」オプションと「-o」オプションになります。


「-f」「-o」オプション含めて、よく使用するオプションを紹介しておきます。


オプション フルオプション 説明

-f

–full

コマンドライン全体をパターンマッチの対象とする。

-o

–oldest

対象のプロセスから最も昔に起動されたものだけを対象とする。

-n

–newest

対象のプロセスから最も最近に起動されたものだけを対象とする。

-U ユーザ

–uid ユーザ

指定したユーザのプロセスを対象とする。「,」区切りで複数指定可能。

-v

–inverse

検索パターンの否定。


次に特殊変数についてです。


  • $0:実行中プロセスのシェルファイル名。
  • $?:直前に実行したコマンドの実行結果。成功時は「0」。失敗時は「0」以外。
  • $$:実行中プロセスのプロセスID。

この特殊変数は非常に便利です。
実際の動きを紹介していきます。


$0特殊変数。実行中プロセスのファイル名。


実行中プロセスのシェルファイル名は「test.sh」となります。
そのファイル名がそのまま特殊変数として格納されています。


#!/bin/sh

echo "start"
echo $0
echo "end"

test.sh

$?特殊変数。コマンド実行結果。


コマンドの実行結果が格納されています。
厳密にはコマンドの実行結果を参照して、二重起動チェックに成功したか?失敗したか?についても処理分岐が必要となります。


#!/bin/sh

echo "start"
PGREP=`pgrep -f $0 -o`
echo $?
echo "end"

0

$$特殊変数。実行中プロセスのプロセスID。


実行中プロセスのプロセスIDが格納されています。


#!/bin/sh

echo "start"
echo $$
echo "end"

4269


JavaでNULL判定・空文字判定をおこなう場合に注意するポイント

Java

Javaで文字列のNULL判定や空文字判定をおこなう場合、判定ロジックに気を付けないと思いもよらない例外が発生する場合があります。

今回は、NULL判定や空文字判定をおこなう場合の注意すべきポイントをサンプルプログラム付きで説明していきます。


環境情報


  • Java:Java1.8.0_281

NULL判定


文字列の比較はStringクラスのequalsを使用しますが、equalsメソッドを使用する変数を比較元にするか?もしくは、比較先にする?によって、例外の発生パターンが異なってきます。

public class NullSample {

    // NULLの定数
    private static String DEFINE_NULL = null;

    // 空文字の定数
    private static String DEFINE_KARA = "";

    public static void main(String[] args){
        System.out.print("start: main\r\n");


        /*------------------------------------------*/
        /* NULL判定                             */
        /*------------------------------------------*/

        // 1.NULL判定/定数が左側
        try {
            String val = "abc";
            if (DEFINE_NULL.equals(val)) {
                System.out.print("val is null \r\n");
            } else {
                System.out.print("val is not null \r\n");
            }
        } catch (Exception e) {
            System.out.print("NULL判定で例外!定数が左側 \r\n");
        }

        // 2.NULL判定/定数が右側
        try {
            String val = "abc";
            if (val.equals(DEFINE_NULL)) {
                System.out.print("val is null \r\n");
            } else {
                System.out.print("val is not null \r\n");
            }
        } catch (Exception e) {
            System.out.print("NULL判定で例外!定数が右側 \r\n");
        }
        System.out.print("end: main\r\n");
    }
}

プログラムの実行結果は以下になります。


start: main
NULL判定で例外が発生!定数が左側  ← 1の判定結果
val is not null                    ← 2の判定結果
end: main

1の判定について、例外が発生しています。
equalsメソッドを使用するオブジェクト自体がNULLの場合は例外が発生するということがわかります。


そのため、実際のプログラムでは、NULLが格納される可能性のある変数ではequalsメソッドは使用しないことが推奨されます。


空文字判定


次は空文字の判定についてです。
サンプルプログラムで動きを確認してみます。


public class KaraSample {

    // NULLの定数
    private static String DEFINE_NULL = null;

    // 空文字の定数
    private static String DEFINE_KARA = "";

    public static void main(String[] args){
        System.out.print("start: main\r\n");

        /*------------------------------------------*/
        /* 空判定                             */
        /*------------------------------------------*/

        // 1.空判定/定数が左側
        try {
            String val = "abc";
            if (DEFINE_KARA.equals(val)) {
                System.out.print("val is kara \r\n");
            } else {
                System.out.print("val is not kara \r\n");
            }
        } catch (Exception e) {
            System.out.print("空判定で例外が発生!定数が左側 \r\n");
        }

        // 2.空判定/定数が右側
        try {
            String val = "abc";
            if (val.equals(DEFINE_KARA)) {
                System.out.print("val is kara \r\n");
            } else {
                System.out.print("val is not kara \r\n");
            }
        } catch (Exception e) {
            System.out.print("空判定で例外が発生!定数が右側 \r\n");
        }
        System.out.print("end: main\r\n");
    }
}

val is not kara    ← 1の判定結果
val is not kara    ← 2の判定結果

1と2のどちらの方法であっても、例外が発生せずに空文字判定をおこなうことができています。


上記のサンプルプログラムは、判定結果が空文字ではないという結果になっていますが、試しに空文字での判定をおこなってみます。


public class KaraSample {

    // NULLの定数
    private static String DEFINE_NULL = null;

    // 空文字の定数
    private static String DEFINE_KARA = "";

    public static void main(String[] args){
        System.out.print("start: main\r\n");

        /*------------------------------------------*/
        /* 空判定                             */
        /*------------------------------------------*/

        // 1.空判定/定数が左側
        try {
            String val = "";
            if (DEFINE_KARA.equals(val)) {
                System.out.print("val is kara \r\n");
            } else {
                System.out.print("val is not kara \r\n");
            }
        } catch (Exception e) {
            System.out.print("空判定で例外が発生!定数が左側 \r\n");
        }

        // 2.空判定/定数が右側
        try {
            String val = "";
            if (val.equals(DEFINE_KARA)) {
                System.out.print("val is kara \r\n");
            } else {
                System.out.print("val is not kara \r\n");
            }
        } catch (Exception e) {
            System.out.print("空判定で例外が発生!定数が右側 \r\n");
        }
        System.out.print("end: main\r\n");
    }
}

val is kara    ← 1の判定結果
val is kara    ← 1の判定結果

正常に、空文字の判定がおこなわれて結果も正常であることがわかります。