MoyaSystem

もやしです。

SQL Server における Clustered Index と Non-Clustered Index の使われ方

Clustered Index (CI) と Non-Clustered Index (NCI) とはそもそも何か

SQL ServerのインデックスはCIとNCIに分けられます。CIはデータベースの行そのものをCIキーに沿って並べ替えます。NCIはCIをバリューとする参照テーブルを作成します。CIは一般的にテーブルの主キーと同一であり、ひとつのテーブルに一つしか設定できません。NCIはひとつのテーブルに複数設定することができます。
一例として、StudentID, SchoolID, Name の3カラムを持つStudentテーブルを考えます。これに対し、StudentID を CIキー、SchoolID をNCIキーとして設定すると、データベース構造は以下のようになります。

Student テーブル

StudentID SchoolID Name
001 103 鈴木一郎
002 101 田中二郎
003 102 佐藤三郎
004 101 高橋四郎

StudentテーブルはCIキーでソートされます。

SchoolID インデックス

SchoolID StudentID
101 002
101 004
102 003
103 001

NCIをキー、CIをバリューとしたテーブルが作られます。
これにより、WHERE句でインデックスキーが指定された場合、すべての行を参照する必要なく、求めたいデータに効率的にアクセスすることができます。

NCI が使われるとき

NCIによる検索が行われるのは、以下の2条件を満たす場合です。

  1. SELECT 句にCIキーまたはNCIキーのみが含まれている
  2. WHERE 句がない、またはNCIキーのみが含まれている

この場合、CIを使うよりNCIを使うほうが、必要な情報に速くアクセスできます。以下のSQLを考えてみましょう。

SELECT SchoolID, StudentID
FROM Student
WHERE SchoolID = '102'

SchoolIDはNCIキー、StudentIDはCIキーです。既に示したSchoolIDインデックスを見ると、なんとこれらのカラムが含まれています。つまりStudentテーブルそのものを参照する必要はないのです。

CIが使われるとき

NCIが使われない時なのですが、具体例を見るために以下のSQLを考えてみます。

SELECT SchoolID, StudentID
FROM Student
WHERE StudentID = '002'

SELECT句に含まれるカラムはNCIからでも取得できますが、WHERE句にCIキーが指定されています。この場合はStudentテーブルを参照し、CI Seekを行ったほうが高速です。CIを利用すればStudentIDが002の生徒はひとりだけだと即座にわかりますが、NCIを使うとすべての行を参照しなければならないからです。

SELECT SchoolID, StudentID, Name
FROM Student
WHERE SchoolID = '102'

この場合NCI SeekではなくCI Scan が行われます。理由はSELECT句にNameが含まれているので直接Studentテーブルが参照されるからです。NCI Seekしたほうがいいような気もしますが、NCIからテーブルを参照しに行く処理がオーバーヘッドになるのでしょう。

覚え方

実テーブルに含まれる情報を見に行かないといけないときはCI、そうでなければNCIが使われるとおぼえましょう :)

Cakephp 3.0のMVC構造についてのざっくりしたまとめ

はじめに

最近CakePHPでシステムの開発を行っているのですが、この3月にリリースされたばかりのバージョン3.0で始めてしまったがために、いろいろ2.0と異なる部分があって戸惑ってます。
http://book.cakephp.org/3.0/en/index.html

Model-View-Controllerの概要についてはこちらをご参照くださいませ。
http://ja.wikipedia.org/wiki/Model_View_Controller

フォルダ構成

「Posts」という名称のテーブルを作成し、

cake bake all Posts

コマンドを実行した状態を仮定します。

  • Controller
    • Component
    • PostsController.php ファイル
  • Model
    • Behavior
    • Entity
      • Post.php ファイル
    • Table
      • PostsTable.php ファイル
  • Template
    • Post
      • add.ctp ファイル
      • edit.ctp ファイル
      • index.ctp ファイル
      • view.ctp ファイル
  • View
    • Helper

2.0とは次のような点が異なっています。

Modelファイルの格納先が Model/Entity/(エンティティ名) フォルダ下になった

2.0では Modelフォルダ直下でした。

テンプレートファイル(.ctp)の格納先が Template/(エンティティ名) フォルダ下になった

2.0では View/(エンティティ名)フォルダ直下でした。3.0ではViewフォルダの直下にbakeで生成されるファイルはありません。

まとめ

MVCといいながらViewに相当する部分がTemplateというフォルダの下に含まれているのでやや違和感があります。とはいえ2.0とそこまでかわりがあるわけではありません。
問題は各クラスの役割がかなり大きく再編されているので、2.0とおなじ感覚で使えないメソッドが数多くあります。ここはマニュアルやマイグレーションガイドを見ながら格闘していくしかないと思われます。

metaタグのname="viewport"についてまとめてみた

はじめに

レスポンシブデザインで作成したサイトをスマホで見てもうまくレイアウトが反映されておらず、その原因がmetaタグでviewportを指定していなかったから、というのを突き止めるのに一日かかってしまった。超悔しいのでまとめる。

参照元はこちら。
CSS Device Adaptation

そもそも

metaタグには name="viewport" としてこんな感じで値を設定できるが

<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />

これはcssのviewportに相当する。

@viewport {
  width: device-width;
}

width 属性、height 属性

  • 1〜10000 までの数値(px) 、または device-width, device-height を指定

initial-scale、maximum-scale、minimum-scale 属性

  • css の zoom プロパティに相当する
  • 0.1〜10までの数値、または device-width, device-height を指定
  • device-width, device-height は10と同じ
  • 1がズームなし。10に近づくほどズームイン(画面いっぱい)、1より小さい値はズームアウト

user-scalable 属性

  • css の user-zoom プロパティに相当する
  • yes か no が設定できる。yes は zoom、no は fixed に相当する
  • yes ならズーム許可、no なら不許可

BuddyPressのプライベートメッセージで使えるHTMLタグを増やしたい

はじめに

あけましておめでとうございます。

やりたいこと

BuddyPressのプライベートメッセージ機能でdivタグを使えるようにしたい。標準ではdivタグが使えないので、Googleマップを表示するなどの便利な機能が使えないので不便。ただしBuddyPressのソースは触らずに自前プラグイン側で実現させたい。

書いた

define( 'BP_CUSTOM_TAGS', true); // カスタムタグの仕様を宣言
global $allowedposttags, $allowedtags, $allowedentitynames;

$allowedposttags = array(
    // BuddyPressの定義をコピー
);

$allowedtags = array(
	'a' => array(
		'href' => array (),
		'title' => array ()),
	'abbr' => array(
		'title' => array ()),
        // 略...

        // divタグの使用を許可
	'div' => array(
		'id' => array(),
		'name' => array()
		),
        // 略...
);

$allowedentitynames = array(
    // BuddyPressの定義をコピー
)

1行目でカスタムタグの使用を宣言する。この定数がfalseだとBuddyPressの標準カスタムタグが適用される。2行目で宣言されている3つのグローバル変数はタグのチェックに使われるもので、すべて必要。カスタマイズしないものであっても、それぞれBuddyPress内にあるfunctions.kses.phpの内容をコピーして自前プラグインに貼り付けて編集したほうがよい。そうしないとある変数は自前プラグイン、ほかの変数はWordPress標準の定義を使うことになりBuddyPressの設定が無視されてしまう。具体的な問題はわからないが避けたほうが無難、たぶん。
プライベートメッセージのタグチェックに使われるのは$allowedtagsなので、ここにdivタグの使用を許可する旨を追記すれば完了。

TitaniumのaddHTTPCookieがAndroidで動かない件とその対処法

このシリーズ毎度タイトルがひどい。

 

はじめに

JavaScriptの共通コードでiPhoneAndroidのアプリを同時に開発できるTitaniumだが、まったく同じコードで書けば良いかというとそうではない。その一例がcookie
iPhoneアプリではアプリ内で使われるcookieはすべて共通で、HTTP通信すべてに対して同じcookieが使用される。ところがAndroidアプリではcookieがHTTPClientで使われるHTTP cookieとWebViewで使われるSystem cookieの2種類に分かれており、両方で同じ値を使いたい場合は明示的に指定する必要がある。*1

System cookieをHTTP cookieにセットしたい...だができない...

Androidでのcookie操作を可能にするためTitaniumにはいくつかのメソッドが用意されている。たとえばSystem cookieをHTTP cookieにセットしたいときはこうすればよい……と思ったのですが……

var myClient = Ti.Network.createHTTPClient();
var systemCookies = Ti.Network.getSystemCookies("my.domain", "my/path", null);
if(systemCookies){
	systemCookies.forEach(function(cookie){
        myClient.addHTTPCookie(cookie);
        })
}

このコード動かない。getSystemCookieでcookieはちゃんと取ってこれているのだが、どうもaddHTTPCookieが思った通りに動いてくれない。ちなみにAndroidのバージョンは4.0.4。ほかのOSでは試していないので、別のOSではうまくいくかもしれませんが。

こうすれば動く

かなり無理矢理感のあるコードですが、これで動きます。

var myClient = Ti.Network.createHTTPClient();
var systemCookies = Ti.Network.getSystemCookies("my.domain", "my/path", null);
var cookiestring = "";
if(systemCookies){
	systemCookies.forEach(function(cookie){
	cookiestring += '; ' + cookie.name + '=' + cookie.value;
        })
	exhibitClient.setRequestHeader('Cookie:', cookiestring);
}

そのほかにも……

TitaniumのコードはiPhoneだと概ね期待通りに動くのですが、Androidでは動かないケースが多々あるので注意が必要。Androidアプリは素直にJavaで書いたほうがいいかもしれません。うーん。

*1:ああめんどくさい。

Titanium製AndroidアプリのWebViewでCookieをHTTPClientと共有する方法

久々に問題解決でドはまりしたのでメモ。しかしひどいタイトルだ……。

はじめに

TitaniumでAndroidアプリを作成する際、Cookieの扱いには注意が必要です。

On Android, the HTTPClient uses its own cookie store which does not share cookies with the system cookie store used by Titanium.UI.WebView.

http://docs.appcelerator.com/titanium/latest/#!/api/Titanium.Network.HTTPClient-method-getResponseHeader

Androidでは、HTTPClientは自分自身のCookieを使用します。それはTitanium.UI.WebViewが使用するSystem Cookieとは内容を共有しません。

つまり、HTTPClientとWebViewでのCookieは自動的には共有されないということです。この現象はAndroidでのみ発生します。iOSアプリではこの違いを意識する必要はなく、HTTPClientのCookieはWebViewにも引き継がれます。

Cookieを共有する方法

上記の通り、WebViewはSystem Cookieを使用していますので、そちらにHTTPClientで取得したCookieを追加してやればOKです。
例えば次の処理では、特定のドメインに対するCookieがSystem Cookieに保存されます。

var cookies = Ti.Network.getHTTPCookiesForDomain('my.domain.com');
cookies.forEach(function(cookie){
	Ti.Network.addSystemCookie(cookie);
});

Cookieを取得するメソッドはいくつかありますので、目的に応じて使いわけてみてください。

JavaScript: ほかのオブジェクトのメソッドを拝借して新しいオブジェクトを作る

課題

新しいオブジェクトを作成する際に、既存のオブジェクトのメソッドをそのオブジェクトのメソッドとして呼び出せたらいいな、ということがあります。しかし、そのオブジェクトをまるごと継承するのは都合が悪いときもあります。そのような場合、一部のメソッドのみ拝借するという方法があります。

「格闘家」と「バスケ選手」から「サッカー選手」を作りたい例を考えてみましょう。サッカー選手は「kick」と「dribble」ができればよい、と仮定します。既にFighterオブジェクトとBasketballerクラスは用意されています。

    var Fighter = function Fighter(){};
    Fighter.prototype.panch = function(){
        console.log(this.name + " can panch!");
    };
    Fighter.prototype.kick = function(){
        console.log(this.name + " can kick!");
    };

    var Basketballer = function Basketballer(){};
    Basketballer.prototype.dribble = function(){
        console.log(this.name + " can dribble!");
    };
    Basketballer.prototype.dank = function(){
        console.log(this.name + " can dank!");
    };

格闘家はpanchとkickができ、バスケ選手はdribbleとdankができます。kickとdribbleのみサッカー選手に使わせたい場合、継承は適切な手段ではありません。

メソッド拝借

以下のようにすればメソッドを拝借することができます。

    var SoccerPlayer = function SoccerPlayer(){};
    SoccerPlayer.prototype.kick = function(){
        Fighter.prototype.kick.call(this);
    };
    SoccerPlayer.prototype.dribble = function(){
        Basketballer.prototype.dribble.call(this);
    }

    var honda = Object.create(SoccerPlayer.prototype,{
        name: {value: "Keisuke Honda"}
    });

    honda.kick(); // Keisuke Honda can kick!
    honda.dribble(); // Keisuke Honda can dribble!
    // honda.panch(); // error
    // honda.dank(); // error

SoccerPlayerオブジェクトを作成し、kick, dribleメソッドの中で、Fighter.prototype.kick, Basketballer.prototype.dribble の両関数が持っているcallメソッドを呼び出してやります。引数にSoccerPlayerオブジェクト自身(this)を渡すと、各メソッドはその呼び出し元であるthisにSoccerPlayerオブジェクトを束縛します。結果、SoccerPlayerオブジェクトのメソッドとしてkick, dribbleを呼び出すことができます。当然、panch, dankを呼び出すことはできません。

最後に

ブログ本文書いてからキックとドリブルができればサッカー選手ってそうとう無茶苦茶な説明だなと思ったんですが、あらためて新しい例を考えなおすのめんどくさかったのでそのままにしておきます。謹んでお詫びを申し上げます。