ラブびあ

ビール。ときどきラブ

GASでユーザー定義関数を使う

Google Apps Script
https://developers.google.com/apps-script/overview

■Custom Functions in Google Sheets
https://developers.google.com/apps-script/guides/sheets/functions

■functionに複数の範囲を渡して処理する例

=myfunction(A1:B5,C1:E5)
function myFunction() {
  var arr =[];
  for (var i=0 ; i<arguments.length ; i++) {
    for (var row=0 ; row<arguments[i].length ; row++) {
      for (var col=0 ; col<arguments[i][row].length ; col++) {
        arr.push(arguments[i][row][col]);
      }
    }
  }
  return arr.join();
}

自戒

東京メトロ オープンデータ活用コンテストに参加すべく夜なべしましたが、
結局応募まで至りませんでした。自戒を込めてやったところまで記しておく。
目標は「androidアプリでリアルタイム時刻表を作ること」でした。

Java
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
開発に必要なもの一式が入っている。これが無いと始まらない。

IDE(いずれか選択)
Eclipse + ADT
https://developer.android.com/sdk/index.html

Android Studio以前から存在していて比較的安定している
Eclipseユーザーは、こちらから入った方がとっつき易そう

Android Studio
https://developer.android.com/sdk/installing/studio.html

まだベータ版。毎日アップデートする勢いで開発中。
Eclipse版で足枷になっている旧バージョンのサポートを、
ある程度見限ってガシガシ更新しているように見えた。
機能的にEclipseに劣る点は無さそうで、
何よりサクサク動くので早くver1になって欲しい。

東京メトロAPIの呼び出しとjsonのパース
ポイントは
東京メトロAPIの開発者登録をしたキーが必要
jsonのパースにはgsonが良さそう?
・gson.jarをダウンロードしてlibsフォルダへ配置する
gradleにダウンロードさせても、アプリケーションからjarを探せないらしい
プロジェクトのlibsに配置して直接参照する
・gson的に、json-ldはjsonと同じ扱い
・インターネットへ接続する許可を設定する
AndroidManifest.xml に Manifest.permission.INTERNET を定義する
これがないと実行時にURLを開こうとすると原因がよーわからんexceptionになる。
http://developer.android.com/reference/android/Manifest.permission.html#INTERNET

ここに来るまで30時間ほど。

収穫はandroid開発のスタートラインまで来れた?こと。ふぅ。

東京メトロ オープンデータ活用コンテスト

東京メトロ オープンデータ活用コンテスト

「エンドユーザーのアプリから直接APIを呼んではいけない」

まじか。開発者登録した後に読める規約に書いてあるらしい。
これってつまり、ドメイン取って自前WEBサーバー立ててデータストア設計してWEBアプリ作って、
そこまで揃えてやっとスマホアプリ作りに入れるってことか。

スマホアプリ作るの初めてなんですぅ〜
という軟弱者の私が軽々しく参加できるコンテストではなさそう・・・

東京メトロAPIで取得できるデータを応答するWEBアプリ、で応募するかw


※2014/9/27追記

利用規約が改定され、上の条項は削除されました。
これで、どんなアプリでも自由に作ってくれたまえ、ということになったみたいです。

一緒に「トークンの扱いは自己責任で」的な規約が追加されましたが、
トークンなんて、最初からいらなかったんじゃ・・・

まぁ何か作ってみましょうかね。

ShiftJISの固定長ファイルを読み込む

VBScriptでShiftJISの固定長ファイルを読み込むテンプレ。
例によって cmd.vbs と wow64.vbs を同じフォルダへ入れておく。
サンプルでは text.txt を fixedlength.vbs にドロップすると、1レコードずつmsgboxします。

test.txt

123ほげ1  ふが1  
456ほげ2  ふが2  
789ほげ3  ふが3  

fixedlength.vbs

option explicit
dim wsh,fso
const ForReading   = 1
const ForWriting   = 2
const ForAppending = 8
set wsh = CreateObject("WScript.Shell")
set fso = CreateObject("Scripting.FileSystemObject")

include "cmd.vbs"
include "wow64.vbs"

call init
call main
call fini

set wsh = nothing
set fso = nothing

WScript.Quit '-----------------------------------------------------------------

sub init()
end sub

sub main()
	dim i
	for i = 0 to WScript.Arguments.Count - 1
		call func(WScript.Arguments(i))
	next
end sub

sub func(arg)
	dim ifile, defs, record
	defs = array(3,10,10,2) 'レコード定義

	set ifile = fso.OpenTextFile(arg, ForReading, False)
	do until ifile.AtEndOfStream
		record = readRecord(ifile, defs)
		if UBound(record) > 0 then
			msgbox join(record, vbCrLf)
		end if
	loop
	ifile.Close
	set ifile = nothing
end sub

sub fini()
end sub

sub include(filename)
	filename = fso.BuildPath(fso.GetParentFolderName(WScript.ScriptFullName), filename)
	ExecuteGlobal fso.OpenTextFile(filename, ForReading, False).ReadAll()
end sub

' 固定長ファイルから1レコード分読み込み、配列に分割して返す
' ifile   固定長ファイル
' defs    カラム長配列
' @return 1レコード分の配列
function readRecord(ifile, defs)
	dim arr,i,s,l,c
	redim arr(UBound(defs))
	for i = 0 to UBound(defs)
		s = ""
		l = 0
		do while l < defs(i)
			if ifile.AtEndOfStream then
				exit function
			end if
			c = ifile.Read(1)
			l = l + lenj(c)
			s = s & c
		loop
		arr(i) = s
	next

	readRecord = arr
end function

' ShiftJIS相当関数群 ---
' 文字列の切り出し
function midj(s, position, length)
	dim i,c,pos
	pos = 1
	for i = 1 to len(s)
		c = mid(s, i, 1)
		if pos >= position then
			midj = midj + c
		end if

		pos = pos + lenj(c)
		if pos >= position + length then
			exit function
		end if
	next
end function

' 1文字の長さ
function lenj(c)
	if(asc(c) >= 1 and asc(c) <= 255) then
		lenj = 1
	else
		lenj = 2
	end if
end function

' 右桁埋め(文字列の空白埋めなど)
function rpadj(s, length, padding)
	rpadj = midj(s & string(length, padding), 1, length)
end function

' 左桁埋め(数値のゼロ埋めなど)
function lpadj(s, length, padding)
	lpadj = strreverse(rpadj(strreverse(s), length, padding))
end function

bashでメニュー

bashでこんな感じのメニューを簡単に作る方法です。

■■■メインメニュー■■■
 0: submenu001.sh メニュー1
 1: submenu002.sh メニュー2
 q: 終了
コマンドを選択してEnterキーで決定します>

■作り方

  • メインメニューとサブメニューは同じフォルダに作成します
  • メインメニューはサブメニューを呼び出します
  • サブメニューに記述したコマンドを適宜実行する構成です
  • メインメニューにはサブメニュー内の「#@menu:サブメニュー」が表示されます
  • メインメニューはとりあえずコピペで動きます
  • サブメニューは適宜作成してください
  • 小さいシステムならサブメニューだけで、サブメニューを直接起動でも良いかもしれません

■メインメニュー
mainmenu.sh

#!/bin/bash

setMenu()
{
	IFS=$'\n'
	menu=(`grep "^#@menu:" *.sh`)
}

getMenu()
{
	IFS=$':'
	arr=($1)

	if [[ ${#arr[@]} -ge $2 ]]; then
		echo ${arr[$2]}
	else
		echo
	fi
}

printMenu()
{
	for (( i = 0; i < ${#menu[@]}; i++ ))
	do
		shell=`getMenu "${menu[$i]}" 0`
		title=`getMenu "${menu[$i]}" 2`
		printf "%2d: %s %s \n" $i ${shell} ${title}
	done
}

executeMenu()
{
	IFS=$':'
	item=(${menu[$1]})
	./${item[0]}
}

showMenu()
{
	while true; do
		clear
		echo ■■■メインメニュー■■■
		printMenu
		echo " q: 終了"
		read -p "コマンドを選択してEnterキーで決定します> " input

		if [[ ${input} =~ "[eEqQ]" ]]; then
			exit
		fi

		if [ ${input} -ge 0 ] && [ ${input} -lt ${#menu[@]} ]; then
			executeMenu ${input}
		fi
	done
}

setMenu
showMenu
exit 0

■サブメニュー
submenu001.sh

#!/bin/bash
#@menu:メニュー1
while true; do
	clear
	cat <<EOF
==================================================
メニュー1
==================================================
 1: サブメニュー1
 2: サブメニュー2
 3: サブメニュー3
 q: 終了
==================================================
EOF
	case "$input" in
	1)
		sudo ssh -t -t {remotehost} {command1}
		;;
	2)
		sudo ssh -t -t {remotehost} {command2}
		;;
	3)
		sudo ssh -t -t {remotehost} {command3}
		;;
	q|Q|e|E)
		exit 0
		;;
	*)
		;;
	esac
	read -p "メニューを選択してください(1-3:q)>" input
done

exit 0

重いSQLを抽出する

SQLDeveloperの(たしか)CPU上位SQLを少し改造したもの。
実行1回あたり5秒以上かかっているSQLを洗い出します。

SELECT s.INST_ID,
    (s.cpu_time/1000000) "CPU_Seconds",
    s.disk_reads "Disk_Reads",
    s.buffer_gets "Buffer_Gets",
    s.executions "Executions",
    CASE
        WHEN s.rows_processed = 0
        THEN NULL
        ELSE ROUND((s.buffer_gets/NVL(REPLACE(s.rows_processed,0,1),1)))
    END "Buffer_gets/rows_proc",
    ROUND((s.buffer_gets /NVL(REPLACE(s.executions,0,1),1))) "Buffer_gets/executions",
    (s.elapsed_time      /1000000) "Elapsed_Seconds",
    ROUND((s.elapsed_time/1000000)/NVL(REPLACE(s.executions,0,1),1)) "Elapsed/Execution",
    ga.sql_fulltext "SQL",
    s.module "Module",
    s.SQL_ID
FROM gv$sql s
LEFT JOIN gv$sqlarea ga ON s.inst_id = ga.inst_id and s.sql_id = ga.sql_id
WHERE s.sql_id IN
    ( SELECT DISTINCT sql_id
    FROM
        ( WITH sql_class AS
        (SELECT sql_id,
            state,
            COUNT(*) occur
        FROM
            (SELECT sql_id ,
                CASE
                    WHEN session_state = 'ON CPU'
                    THEN 'CPU'
                    WHEN session_state = 'WAITING'
                    AND wait_class    IN ('User I/O')
                    THEN 'IO'
                    ELSE 'WAIT'
                END state
            FROM gv$active_session_history
            WHERE session_type                         IN ( 'FOREGROUND')
            AND sample_time BETWEEN TRUNC(sysdate,'MI') - :minutes/24/60 AND TRUNC(sysdate,'MI')
            )
        GROUP BY sql_id,
            state
        ),
        ranked_sqls AS
        (SELECT sql_id,
            SUM(occur) sql_occur ,
            rank () over (order by SUM(occur)DESC) xrank
        FROM sql_class
        GROUP BY sql_id
        )
    SELECT sc.sql_id,
        state,
        occur
    FROM sql_class sc,
        ranked_sqls rs
    WHERE rs.sql_id = sc.sql_id
        --and rs.xrank <= :top_n
    ORDER BY xrank,
        sql_id,
        state
        )
    )
AND ROUND((s.elapsed_time/1000000)/NVL(REPLACE(s.executions,0,1),1)) > 5 -- SQL1回あたり5秒を超えるもの
ORDER BY "Elapsed/Execution" DESC NULLS LAST

SQL整形マクロ

SQL整形マクロ V1.04
http://hide.maruo.co.jp/lib/macro/sqlclean104.html

希望通りの動作のものを見つけられなかったので、
自分でも改造できそうだった↑に、ちょっとだけ好みを追加してみました。

■「シングルクォートで括られた」を文字列定数とする機能
スキップ用の変数をループの前に追加して、

	##Skip = -1;

シングルクォートが出てくる度にフラグを反転させます。

		// 文字列定数の時の処理キャンセル
		if(code == 39)
		{
			##Skip = ##Skip * -1;
			right;
			continue;
		}
		if(##Skip == 1)
		{
			right;
			continue;
		}

■改行を追加する条件のjoinをleftとinnerに変更。
この配列を変更

$EntElm[14]	="join"

やや実行速度が遅い気もしますが、今のところまずまず使わせて頂いています。