【Slack】導入検討

f:id:albahoudori:20190707161702j:plain

検討項目                   

1.目的の明確化
2.導入準備
3.環境構築
4.運用

対応概要                    

1.目的の明確化

 →slack を導入した結果どうなりたいのか?
  ①コミュニケーション手段の改善
   ・チャットベースでのコミュニケーション
   ・プライベートアカウント利用の衰退(会社配布の個人アカウント利用)
   ・会社 → 社員 に対する情報伝達土台の確立
  ②情報共有の土台を構築
   ・IT知見や記事等の共有
   ・案件情報の共有
   ・社内業務の透明化
   ・同僚の業務内容の透明化

2.導入観点

 →小さな会社なので現在の情報伝達手段(メール)に並行してslackも稼働させていく
 ●第1フェーズ
  →使用感の確認、ルールの制定やコミュニケーション土台の構築。
   ・上司 : 会社側への伝達。
   ・部下(役職有) : 自分一人が動いても仕方ないので。
   ・開発メンバ : slack を土台とした開発を歓迎するため
 ●第2フェーズ
  →試運転開始。比較的コミュニケーションに明るいメンバの慣れる期間。
   ルール/運用等の見直しを並行する
   ・各現場に配属されている社員
   ・コミュニケーションに明るい社員
 ●第3フェーズ
  →本格運用。
  ・全社員
 ※注意
  ・第2フェーズ以降は、参加希望者がいれば参加を推奨する。

3.環境構築

 ●タスク
  ・各種設定項目の確認
  ・権限種別の確認(管理者・オーナー)
  ・有用そうなアプリケーションの確認
  ・招待の仕方/招待された側の登録方法
 ●ルールの制定
  →自由度を高く設定したいため基本的な内容のみ制定
   ・アカウントルール(表示名・画像設定)
   ・利用ルール
    ・失礼のない発言を。
    ・宗教等の勧誘は禁止。
    ・機密情報/個人情報を載せない。
    ・DM/プライベートチャンネルは基本的に使わない
    ・@chanelは使わない。@hereを使う。
    ・センテンスは分割せずに投稿する。
    ・資料の添付などは行わず社内共有フォルダを利用する。
   ・チャンネルルール
    ・誰でも作成可能。プライベートチャンネルは使わない。
    ・チャンネル作成には接頭文字をつけて作成すること
     ・corp_:自社情報
     ・news_:RSS等記事投稿チャンネル
     ・team_:所属や現場ごとのチャンネル
     ・chat_:雑談系
     ・tech_:技術系
 ●チャンネル
  →下記のチャンネルは事前に作成しておく。
  〇全員参加チャンネル
   ・#corp_会社通知-readonly :会社通知を行うために利用(元general)
   ・#corp_勤怠連絡 :メールで送っていた勤怠連絡をこちらに移譲
   ・#corp_帰社連絡 :帰社日に関する連絡。飲み会の参加等もここで実施
   ・#corp_質問箱 :会社通知等での質問が
   ・#chat_飯テロ : "どうでもよいことでチャンネルを作ってよい" という手本に
   ・#news_技術全般 :技術記事掲示チャンネル
  〇参加自由チャンネル
   ・#corp_XXXXXX :会社で行っている研修等

4.運用

 ●恒常運用
  ・退職者・新入社員
  ・デバイス紛失フローの確立
 ●チャンネル運用(雑記)
  →社員から情報発信が行われることを目的としたチャンネル。
   運用で生きている状態にすることが目的。
  ・#news_技術全般 チャンネル運用
  ・#chat_飯テロ チャンネル運用
 ●チャンネル運用(会社)
  →会社から情報発信を行うことを目的としたチャンネル。
  ・#corp_会社通知-readonly :会社側から伝えたい情報があり次第投稿
  ・#corp_質問箱 :社員からの質問をだれがいつまでに答えるかを考慮しておく

 

5.参考

Slackは文化だと思う。割といろいろな表現で周囲に伝えたつもりだったけど、どうやら担当者はそう思ってくれなかったようです。伝え方が悪かったのかもしれません。

どうやら Slack導入して『皆さん招待したので使ってください~!入れたので使ってくださーい!ルールはこれです!』の流れになりそうです。

みんな既に Slack でどこかに属したことがあるのであれば、それでもうまくいくかもしれませんね。

【Python】TwitterAPI でデータ収集(リプライツイート内容と画像)

f:id:albahoudori:20190826232258j:plain

1.目的
 とあるツイートのリプライ内容を収集する

2.収集対象
 ・ツイート者
 ・ツイート内容
 ・添付画像
 ・ツイート日時

3.参考
 ・Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ ※2019年8月時点の情報 - Qiita
 ・Python+Twitter API で佐久間まゆの画像を集める - Qiita
 ・Twitter APIでリプライを集めたい! - Qiita

4.実行結果例
f:id:albahoudori:20190826233717p:plain

5.設定ファイル
 4行は Twitter Developer Tool に登録した際の値になります
 ファイル名:setting.py

CONSUMER_KEY = "XXXXXXXXXXXXXXXXX"
CONSUMER_SECRET = "XXXXXXXXXXXXXXXXX"
ACCESS_TOKEN = "XXXXXXXXXXXXXXXXX"
ACCESS_TOKEN_SECRET = "XXXXXXXXXXXXXXXXX"
USERID = '@@(対象アカウント名)'
TWEETID = '(対象ツイートのID/WEB でツイートにアクセスした際のURLに値が確認できる)'
SEARCHRANGE_START = "YYYY-MM-DD(検索期間スタート)"
SEARCHRANGE_END = "YYYY-MM-DD(検索期間エンド)"

6.Python実行プログラム
 必要なライブラリをインストールしておくこと
 ファイル名:(get_replay.py)

from requests_oauthlib import OAuth1Session, OAuth1
import json
import os
import re
import urllib
import setting
import io
import requests
import sys

###変数
#ローカル用
parentDlDir = "download"
dlDir = "tmp"
dlDirPath = "C:\\"
#接続用
searchCount = 100 # 一回あたりの検索数(最大100/デフォルトは15)
maxCount = 100 # 検索回数の上限値(最大180/15分でリセット)
userId = setting.USERID
tweetId = setting.TWEETID
searchRange_start = setting.SEARCHRANGE_START
searchRange_end   = setting.SEARCHRANGE_END

### inner Class
# 集めた画像を格納するディレクトリの作成を行う
def createDlDir():
    global dlDir
    global dlDirPath
    if not os.path.isdir(parentDlDir):
        os.mkdir(parentDlDir)
    check_count = 0
    while True:
        dlDir = "/dir" + str(check_count)
        dlDirPath = parentDlDir + dlDir
        if not os.path.isdir(dlDirPath):
            os.mkdir(dlDirPath)
            return
        check_count += 1

#リプライツイートの検索
def getReplayTweet(auth, targetUserId):
    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
    url = "https://api.twitter.com/1.1/search/tweets.json?tweet_mode=extended&lang=ja&q="+targetUserId + "&since=" + searchRange_start + "&until=" + searchRange_end +"&count="+str(searchCount) 
    response = requests.get(url, auth=auth)
    data = response.json()['statuses']
    maxid = 0
    limit = 1
    replyCnt = 0
    check_image = []
    while True:
        # 制御
        if len(data) == 0:
            break
        if limit == maxCount:
            return 
        # リプライツイート取得
        for tweet in data:
             # 制御
             if not tweet['in_reply_to_status_id_str'] == tweetId:
                 continue
             # 基本情報取得
             replyTweetId  = str(tweet["id"])
             replyText = ''.join(tweet["full_text"].splitlines())
             replyUserName = tweet['user']['screen_name'] 

             replyAtTime = tweet["created_at"]
             replyCnt += 1
             replyCntPadded = '%04d' % replyCnt
             imageStatus = "NULL"
             
             #画像収集
             imageList = []
             imageNumber = 0
             try:
                 image_number = 1
                 media_list = tweet['extended_entities']['media']
                 for media in media_list:
                     image = media['media_url']
                     if image in check_image:
                         continue
                     with open(dlDirPath +"/"+ replyCntPadded + '_' + replyUserName + "_" + str(imageNumber) +"_"+ os.path.basename(image), 'wb') as f:
                         img = urllib.request.urlopen(image).read()
                         f.write(img)
                     imageList.append(str(image))
                     imageNumber += 1
                 imageStatus = "OK"
             except KeyError:
                 imageStatus = "--"
             except:
                 imageStatus = "NO"
             
             print(replyCntPadded + "\t" + imageStatus + "\t" + replyTweetId  + "\t" +  replyUserName   + "\t" +  replyText + "\t" + replyAtTime)
        
        # 次のXXX件を再度検索
        maxid = int(tweet["id"]) - 1
        nextUrl = url + "&max_id=" + str(maxid)
        response = requests.get(nextUrl, auth = auth)
        data = response.json()['statuses']
        limit += 1

### Main Class
if __name__ == '__main__':
    #イニシャライズ
    CK  = setting.CONSUMER_KEY
    CKS = setting.CONSUMER_SECRET
    AT  = setting.ACCESS_TOKEN
    ATS = setting.ACCESS_TOKEN_SECRET

    #フォルダ作成
    createDlDir()
    
    #接続開始
    auth = OAuth1(CK, CKS, AT, ATS)
    targetUserId = userId + ' exclude:retweets' # RTは除く
    targetUserId = urllib.parse.quote_plus(targetUserId)
    getReplayTweet(auth, targetUserId)

7.問題点
 たぶん以下のサイトにある理由だとは思いますが、一部ツイートが取得できませんでした。。。
 解決策がみつからなかった・・・
 【Twitter】取得できないリプライツイート問題 - Qiita

【初心者・教育用】基本情報技術者試験_勉強方針

f:id:alba_ahoudori:20150207225943j:plain

今回は基本情報技術者試験を受けるための勉強方針を記載したいと思います。会社の立場上、教育周りの記事が多い・・・
本ページはどちらかというと ”入社前に基本情報技術者の資格を取得しないといけないけど、何から手をつけていいかわからない” というような人向けになりますのであしからず。

記載内容は以下の通りになります

・1.基本情報技術者資格とは(概要)
・2.基本情報技術者資格の取得の意味(体験談)
・3.勉強方針(方針・対策や参考書紹介等)

1.基本情報技術者資格とは

基本情報技術者資格は情報処理技術者試験のひとつでIT技術・知識力が認定される国家資格です。IPA (情報処理推進機構)が発行しています。
技術・知識レベルが認定されますので、多くの会社で取得が奨励されています。

IPA 独立行政法人 情報処理推進機構:情報処理技術者試験


簡単な概要としましては
・情報術者試験の難易度はレベル2にあたる試験
・4肢択1式の試験問題(センター試験的な)
・午前問題・午後問題とわかれ、試験時間はそれぞれ150分。
・午後問題は一部選択式となっている。
・年二回春期・秋期おこなわれる
・午前問題・午後問題それぞれで60%以上の点数で合格となる

2.基本情報技術者資格の取得の意味

基本情報技術者資格の周りの認識を先に話しておきたいと思います。
基本情報技術者の資格は、IT技術者の入門資格といえるため、IT系で仕事をしている人たちは
「あぁ、この人(有資格者)はITに関する最低限の浅い知識を持ってはいるんだな」
という認識・印象がとなります。

その印象をもたれるのはしょうがないことで、基本情報技術者試験を取得した新入社員がきても、PCの初期セットアップができることの証明にはなるわけではありません。

「じゃぁ資格とる意味ないじゃん?」

いえ、全然意味あるのです。基本情報技術者資格は先に書いた通り入門資格であり、ITに関する幅広い知識を証明するものであります。IT関連の幅広い知識を浅いながらも持っている人と持っていない人では大きな差があります。

例えば先輩社員が教育する新入社員が以下の二人いたとします。
・Aさん:「IPアドレス? あ~、なんとなく知ってるような、知ってないような・・・」という新入社員
・Bさん:基本情報技術者有資格者の新入社員
この二人の教育内容は区別しないといけませんね。
Bさんには業務内容やPCのセットアップの話等ができ始めます。Aさんへの教育は当たり前だ! という人もいるかもしれませんが、もし当たり前だとしてもBさんの方が業務について先んじて慣れることができますよね。

契約等のお客さんとの会話等で、業務とは全く違う分野に携わる必要があることもあるでしょう。そんな場合には基本的な知識は押さえておきたいところです。
また、知識をもっていることを証明してくれるものになりますので勉強していることの証明は、周りの印象は多少なりとも良いでしょう。


3.勉強方針

前節は長々話してしまいましたが、それでは勉強方針について記載していきます。

3.1.午前問題

幅広くIT知識が求められる問題が出題されます。あまり深い内容は突っ込んでこないので、一問一問はたいして難しくないのですが範囲の広さが少しつらいです。
IT系の仕事に従事している人でも、全く関係ない分野についての得点は厳しいかもしれません。

①勉強方針 対策

一冊の参考書を、数回通ることが重要。何冊もの参考書を買って対応するほど深い内容はでません。
近年、世情に合わせた問題が出ているようですので、直近の過去問を確認することで通過することができるでしょう。

②初受験の方へ

勉強量が多く大変かと思われますので計画的に対応するといいでしょう。
例:1ヶ月間で参考書を数回通る。その後、半月程度過去問模擬問題を実施する。

基本情報技術者試験ドットコム

③おすすめ参考書

イメージ&クレバー方式でよくわかる 栢木先生の基本情報技術者教室 (情報処理技術者試験)

→栢木 先生 シリーズ。勉強が苦手な方でもとてもわかりやすく解説してあります。これ一冊網羅できれば午前試験には問題なく対応できるかと思います

3.2.午後問題

ここが鬼門。開発に従事している人は、こっちの方が点数とりやすい(アルゴリズム・言語系の経験があることから)かもしれません。

①勉強方針 対策

アルゴリズム・言語系の得点を狙っておく方針を記載します。
・問8/疑似言語(必修)
疑似言語問題で、全ての言語に通ずる基礎の問題となります。これは問9以降の言語問題に通ずるため(一部例外有)とっかかりとしてはここから推奨します。

※近年、疑似言語を複雑化している印象があります。とっかかりとしては少し前の過去問を解く方がよいのではと感じています。

・問9~問13/言語(選択・1つ)
Java・C・COBOLアセンブラ表計算の選択式。ここで選ぶのは各個人の興味関心次第となりますが、自分がしたいこと・することを念頭において選択しましょう。
基本、コードを読めればOKです。書ける必要はないですね。その為、基本的な参考書を読んだのちは、過去問のコード翻訳をいくつかするだけでもかなり効果的ではないかと。

・問2~問7/その他ストラテジ・マネジメント(選択・4つ)・問1は必須
ここは決め打ちをおこないましょう。①・②の勉強を行い言語系で点数を稼いでおき、問1~問7の選択に余裕を持たせたいところです。

②初受験の方へ

疑似言語・言語の習得もそれなりに勉強量は必要かと思われます。また、過去問を解いとくことは午後問題にとってとても有効ですので必ず行うようにしましょう。

③おすすめ参考書

Javaの絵本シリーズ
 →言語勉強用の参考書。とても優しく書かれているので、まったく言語が分からない人にもおすすめ。過去問を解く前に読んでおきましょう。

※気持ちとしてはJavaがいいと思っています

Javaの絵本 増補改訂版

Javaの絵本 増補改訂版

 

【読了中】AmazonWebServices 定番業務システム14パターン 設計ガイド

最近 初めてKindleで買った参考書。

読み終わったら感想かこう。

Amazon Web Services 定番業務システム14パターン 設計ガイド

Amazon Web Services 定番業務システム14パターン 設計ガイド

 

 

【初心者・教育用】開発業務の前提になる知識をまとめてみた

f:id:albahoudori:20190707161702j:plain

 

初めて開発の業務に携わろう・プログラミングしようとしている方向けの教育用記事です(だいたいはITパスポート・基本情報では出てくるものになっちゃった気がします)

現場によっては使わない単語・知識もあるかもしれませんがプログラムを書く人は99%知っている(説明できる)内容です。

前もって基礎的な知識を習得しておくことで、スムーズな業務担当に移れればと思います。

1.基本知識

# 項目 課題
1-1 開発体制 ウォータフォールモデル、プロトタイプモデル、スパイラルモデル とは何かまとめる
1-2 開発フェーズ ウォータフォールの場合、どのような開発フェーズがあるかをまとめる
1-3 仕様書 仕様書はどんなものがあり、どんな内容を記述すべきかをまとめる
1-4 ソース管理 SVN・Git について概要を調べる
1-5 正規表現 正規表現とはなにか、またサクラエディタを利用し正規表現を利用した単語検索(Grep)を実施する
1-6 コーディング規約 コーディング規約の具体例を収集する
1-7 開発者ツール 開発者ツールはどのような種類があり、どのような言語に対応しているかを把握する。必要なツールがわかってるならインストール方法迄把握する

2.ファイル種別 

# 項目 課題
2-1 INIファイル どのような情報が記載されるかを把握する
2-2 CSVファイル どのような情報が記載されるかを把握する
2-3 TSVファイル どのような情報が記載されるかを把握する
2-4 XMLファイル どのような情報が記載されるかを把握する
2-5 HTTPファイル どのような情報が記載されるかを把握する
2-6 CSSファイル どのような情報が記載されるかを把握する
2-7 BATファイル どのような情報が記載されるかを把握する
2-8 CSファイル どのような情報が記載されるかを把握する
2-9 PYファイル どのような情報が記載されるかを把握する
2-10 JSファイル どのような情報が記載されるかを把握する
2-11 PHPファイル どのような情報が記載されるかを把握する
2-12 sqlite3ファイル どのような情報が記載されるかを把握する

3.単語

# 項目 課題
3-1 ビルド 意味を把握する
3-2 NULL 意味を把握する
3-3 オブジェクト指向 意味を把握する
3-4 カプセル化 意味を把握する
3-5 名前空間 意味を把握する
3-6 インスタンス 意味を把握する
3-7 クラス 意味を把握する(継承,抽象.interface,コンストラクタ も併せて把握する)
3-8 メソッド 意味を把握する(パブリック,プライベート,引数 も併せて把握する)
3-9 トランザクション処理 意味を把握する
3-10 バッチ処理 意味を把握する
3-11 デバック 意味を把握する

4.基本文法 (言語毎)

# 項目 課題
4-1 型の種類がどんなものがあるのか把握する、型を変更をするにはどうすればよいのか把握する
4-2 if文 記述方法を把握する
4-3 ループ文 記述方法を把握する
4-4 例外文 記述方法を把握する
4-5 配列 記述方法を把握する
4-6 基本演算 +, -,×, ÷, 余り 等基本的な算術演算子の記述方法を把握する
4-7 コメント 書き方及び書くべき内容を把握する

5.DB基本知識

# 項目 課題
5-1 DB テーブルRDBとは何か、DBの種類はどんなものがあるか、テーブルとは何かを把握する
5-2 ビュー ビューとは何かを把握する
5-3 主キー 主キーとは何かを把握する
5-4 外部キー 外部キーとは何かを把握する
5-5 正規化 正規化とは何か、どんな種類があるか、非正規化する場合は何かを把握する
5-6 カラム カラムとは何か、属性(型,NULL制約)とは何か
5-7 インデックス インデックスとは何か、またその重要性を把握する

6.基本文法 (クエリ文/DB毎)

→練習できるサイト(https://paiza.io/ja)を活用するとよい

# 項目 課題
6-1 参照文 SELECT-FROM, WHERE データ参照を行う
6-2 並び替え ORDER BY文で並び替えを行う
6-3 結合 INNER JOIN文, LEFT JOIN文でテーブル結合を行う
6-4 集計 GROUP BY文で集計を行い SUM関数で集計を行う
6-5 データ操作 INSERT文, DELETE文, UPDATE文, MERGE文でデータ更新を行う(更新可能なテーブルを参照すること)
6-6 ケース文 CASE-WHEN-THEN-ELSE 文を実行する
6-7 基本関数 基本的な関数は何があるかを把握する
6-8 入れ子 入れ子(インクエリ・サブクエリ)のクエリ文を作成する・解読する

【プログラム】クエリフォーマット(javascript)+サクラエディタ

f:id:albahoudori:20190826232258j:plain
毎回勉強がてらという理由でプログラミングしていますが、だいたいのきっかけが「あ、これやってみよう!」という興味だけです。
今回は サクラエディタで利用する クエリ成形用マクロ(javascript) を作ってみました。 が、あまりいい出来ではないというか、美しさが足りないというか・・・
またテストも十分じゃないので、今後問題が見つかり次第直します。

■機能
インラインに対応したインデント整形を行います。条件文のクエリ(サブクエリ)はインデント一個足りなくなるぅぅぅぅ・・・
■使い方
●設定
①下部の プログラムソース の内容をコピーしてファイルを作成する。ファイル名は "formatSQL_v1.0.js" とする。
サクラエディタ を開き [設定] → [共通設定] → [マクロ] をひらく。
③[参照]ボタンから ①のファイルを格納したフォルダを参照し、画像通りの以下の設定を行い[設定]を押下する。
④種別:外部マクロ を選択し、対象の機能(queryFormat)を選択する。以下の画像通りに設定し(Shift + Ctrl + ALt は好きに選択を) 割付 を押下する。
⑤[OK]を押下する

●使い方
サクラエディタを開き、対象のクエリを張り付ける。張り付けたクエリをすべて選択し、設定④ で設定した"キー"を押下する

●プログラムソース

/**
 * Version:1.0
 */

/**
 * メンテナンス
 */
var DEBUG = 0;
var debugTextArray = [];

/**
 * イニシャライズメソッド.
 * データインプット情報を一元化する.
 */
function initialize(text){
    
    //文字統一
    var keywordArray = [" FROM\\(" , " JOIN\\(" , " GROUP BY\\(" , "HAVING\\(" , " WHERE\\(" , " AND\\(", "ORDER BY\\(" ];
    text = text.toUpperCase();
    text = replaceAll(text,"\r\n"," ");
    text = replaceAll(text,"\r"," ");
    text = replaceAll(text,"\n"," ");
    text = replaceAll(text,"\t"," ");
    text = replaceAll(text,"★","");
    text = replaceAll(text,"☆","");
    for(i=1; i<keywordArray.length-1; i++)
    {
        var beforeText = keywordArray[i];
        var afterText = beforeText.replace("("," (");
        var text = replaceAll(text,beforeText,afterText);
    }

    text = text.replace(/\s+/g, " ")// 2つ以上の空白を排除
    
    return text;
}
/**
 * メイン処理を行うメソッド
 * インデント調整等を行う.
 */
function formatSql(allText){

    //1.初期化
    allText = initialize(allText);
    var returnValue = "";
    
    var searchText901 = " INNER JOIN \\(";
    var searchText902 = " FROM \\(";
    var searchText903 = " LEFT JOIN \\(";
    var searchText904 = " LEFT OUTER JOIN \\(";
    var searchText905 = " IN \\(";
    
    allText = replaceAll(allText, searchText901," INNER JOIN ★(");
    allText = replaceAll(allText, searchText902," FROM ★(");
    allText = replaceAll(allText, searchText903," LEFT JOIN ★(");
    allText = replaceAll(allText, searchText904," LEFT OUTER JOIN ★(");
    allText = replaceAll(allText, searchText905," IN ★(");
    
    if (DEBUG == 1){ debugTextArray.push("[DEBUG]#1:" + allText + "\r\n")}

    //2.一文字ずつチェックし、内部クエリのSTARTとENDの"かっこ"を紐づける
    charList = allText.split("");
    var q = 0;
    var s = 0;
    var allKakkoCountArray = [];
    var inQueryKakkoCountArray = []; //インクエリ判別用の配列
    var flag = "0";
    
    // すべての"(" ")"にIDを割り当てを行い、紐づける
    // "(" : "★", ")" : "☆" が後の処理の指標となる
    for(k=0; k<charList.length; k++){
        var charValue = charList[k];
        
        if(charValue == "★"){
            flag = "1";
        }
        
        if(charValue == "("){
            s = s + 1 ;
            allKakkoCountArray.push(s);
            if (flag == "1")
            {
                inQueryKakkoCountArray.push(s);
                flag = "0";
            }
            
        }
        
        if(charValue == ")"){
            tmpValue = allKakkoCountArray.pop();
            for(n=0; n<inQueryKakkoCountArray.length; n++){
              if(tmpValue == inQueryKakkoCountArray[n])
              {
                  charList[k] = "☆)";
              }
            }
        }
    }
    
    allText = charList.join("");// すべての文字を結合する
    if (DEBUG == 1){ debugTextArray.push("[DEBUG]#2:" + allText + "\r\n")}

    //3.基本的なキーワードで改行する
    var array =[];
    var selectBlockArray = allText.split("SELECT ")
    var tmpBasicQuery = "";
    for(i=1; i<selectBlockArray.length; i++){
        text = "\r\n" + "SELECT\r\n\t"+ selectBlockArray[i];
        text = replaceKeyword(text);
        tmpBasicQuery = tmpBasicQuery + text;
    }
    if (DEBUG == 1){ debugTextArray.push("[DEBUG]#3:" + tmpBasicQuery + "\r\n")}

    //4.インクエリの整形を行う
    baseTabCount = 0;
    baseTabText = "";
    var flag = 0; //インクエリ整形実行用のフラグ
    var rnBlockTextArray = tmpBasicQuery.split("\r\n");
    for(i=1; i<rnBlockTextArray.length; i++)
    {
        //flagが0以外の時→インクエリによるインデント調整
        if (flag != 0)
        {
            baseTabText = "";
            for(n=0; n<baseTabCount; n++)
            {
                baseTabText = baseTabText + "\t";
            }
            flag = 0;
        }

        tmpText =  rnBlockTextArray[i];
        if (tmpText.indexOf("★") != -1)
        {
            returnValue = returnValue + baseTabText + tmpText.replace("★","") + "\r\n";
            baseTabCount = baseTabCount + 1;
            flag = 1
            continue;
        }
        
        if (tmpText.indexOf("☆") != -1)
        {
            tmpText = replaceAll(tmpText,"☆\\)","\r\n" + baseTabText + "☆)" );
            returnValue = returnValue + baseTabText + tmpText.replace("\t☆","") + "\r\n";
            baseTabCount = baseTabCount - 1;
            flag = 2
            continue;
        }
        
        if (tmpText.indexOf("★") == -1 && tmpText.indexOf("☆") == -1)
        {
            returnValue =  returnValue + baseTabText + tmpText + "\r\n";
        }
    }
    if (DEBUG == 1){ debugTextArray.push("[DEBUG]#3:" + returnValue + "\r\n")}
 
    if (DEBUG == 1){ return debugTextArray.join("");}
    return returnValue;
}
/**
 * 置換メソッド.
 * 全件置換を行う際に利用する.
 */
function replaceAll(str, beforeStr, afterStr){
    var reg = new RegExp(beforeStr, "g");
    return str.replace(reg, afterStr);
}


/**
 * 基本的な改行判定してよいクエリ文字を規定し、置換するメソッド
 */
function replaceKeyword(text){
    searchText001 = " FROM ★\\(";
    searchText002 = " FROM ";
    searchText003 = " INNER JOIN ★\\(";
    searchText004 = " INNER JOIN ";
    searchText005 = " LEFT JOIN ★\\(";
    searchText006 = " LEFT JOIN ";
    searchText007 = " LEFT OUTER JOIN ★\\(";
    searchText008 = " LEFT OUTER JOIN ";
    searchText099 = ","
    
    searchText101 = " WHERE ";
    searchText102 = " GROUP BY ";
    searchText103 = " HAVING "
    searchText104 = " AND "
    searchText105 = " ORDER BY "
    searchText106 = "☆"
    
    //FROM・JOIN
    text = replaceAll(text, searchText001, "\r\n" + "FROM\r\n"       + "★(");
    text = replaceAll(text, searchText002, "\r\n" + "FROM\r\n"       + "\t");
    text = replaceAll(text, searchText003, "\r\n" + "INNER JOIN\r\n" +"★(");
    text = replaceAll(text, searchText004, "\r\n" + "INNER JOIN\r\n");
    text = replaceAll(text, searchText005, "\r\n" + "LEFT JOIN\r\n"  +"★(");
    text = replaceAll(text, searchText006, "\r\n" + "LEFT JOIN\r\n");
    text = replaceAll(text, searchText007, "\r\n" + "LEFT OUTER JOIN\r\n"  +"★(");
    text = replaceAll(text, searchText008, "\r\n" + "LEFT OUTER JOIN\r\n");
    
    //OTHER
    text = replaceAll(text, searchText101,  "\r\n" + "WHERE\r\n"    +  "\t" );
    text = replaceAll(text, searchText102,  "\r\n" + "GROUP BY\r\n" +  "\t" );
    text = replaceAll(text, searchText103,  "\r\n" + "HAVING\r\n"   +  "\t" );
    text = replaceAll(text, searchText104,  "\r\n" + "\t" + "AND ");
    text = replaceAll(text, searchText099,"\r\n" + "\t" + ",");
    text = replaceAll(text, searchText105,  "\r\n" + "ORDER BY\r\n" +  "\t" );
    text = replaceAll(text, searchText106,  "\r\n☆");
    return text;
}

/**
 * 実行
 */
var text = Editor.GetSelectedString(0);
if ( text !== "" ) Editor.InsText(formatSql(text));