T-SQL - lag関数/lead関数でn個前/n個後の行を取得する

lag関数とlead関数を知ったのでメモしておきます。

それぞれ一言で説明すると、

  • lag関数 ... n個前の行を取得する
  • lead関数 ... n個後の行を取得する

といった感じです。この2つもウィンドウ関数と言っていいのかな。

構文の簡単なパターンだと、関数の1つ目の引数には「取得するカラム名」を、2つ目の引数には「n個前/n個後」を指定します。

lag(カラム名, n個前) over(...)
lead(カラム名, n個後) over(...)

年ごとや四半期ごとの比較といった実用的なサンプルは上記ドキュメントを参照してもらうとして、 ここでは超簡単なサンプルクエリを書いて実行結果を残しておきます。

with Seq(Value)  -- CTE(共通テーブル式)
as(
    -- 1から10まで連番を作る
    select 1
    union all
    select Seq.Value + 1
    from Seq
    where Seq.Value + 1 <= 10
)
select
    Value,
    -- Valueを昇順に並べ替えて3つ前の行を取得
    lag(Value, 3) over(order by Value) as Lag,
    -- Valueを昇順に並べ替えて3つ後の行を取得
    lead(Value, 3) over(order by Value) as Lead
from Seq
order by Value;

/*
Value       Lag         Lead
----------- ----------- -----------
1           NULL        4
2           NULL        5
3           NULL        6
4           1           7
5           2           8
6           3           9
7           4           10
8           5           NULL
9           6           NULL
10          7           NULL
*/

結果セットを見ると例えばValueが4の場合に、lag関数は1、lead関数は7を取得できていることがわかります。

また、lag関数/lead関数を使わないで上記と同じ結果を取得するには、 次のような自己結合を使ったクエリになると思いますが、 lag/lead関数を使ったほうがわかりやすいかなと。

-- lag関数/lead関数の代わりに自己結合を使って、3つ前、3つ後の行を取得する
with Seq(Value)
as(
    select 1
    union all
    select Seq.Value + 1
    from Seq
    where Seq.Value + 1 <= 10
)
select
    Src.Value,
    Dst1.Value as Lag,
    Dst2.Value as Lead
from Seq as Src
    left outer join Seq as Dst1
        on Src.Value = Dst1.Value + 3
    left outer join Seq as Dst2
        on Src.Value = Dst2.Value - 3
order by Src.Value;

T-SQL - count(*)とcount(カラム名)の違い

知らなかったのでメモ。

count(*)は全行数を取得するのに対して、count(カラム名)はnullを除く行数を取得します。

サンプルクエリを書いて確認しておきましょう。

select
    -- nullに関係なく全行数を取得
    count(*) as [count(*)],
    -- nullを除く行数を取得
    count(Value) as [count(Value)]
from (values
    (1, 100),
    (2, 200),
    (3, 200),
    (4, null),
    (5, 300),
    (6, 200)) as Test(Id, Value);

-- 行は全部で6、nullを除くと5
/*
count(*)    count(Value)
----------- ------------
6           5
*/

おまけ

countだけでなくsumやavgなどの関数もnullを無視して集計します。

select
    count(Value) as [count(Value)],
    sum(Value) as [sum(Value)],
    avg(Value) as [avg(Value)]
from (values
    (1, 100),
    (2, 200),
    (3, 200),
    (4, null),
    (5, 300),
    (6, 200)) as Test(Id, Value);

/*
count(Value) sum(Value)  avg(Value)
------------ ----------- -----------
5            1000        200
*/

おまけ2

nullの行しか存在しない場合はどうなるんだろうと思って、countやsumなどの関数を試しました。次のような結果になります。

select
    count(Value) as [count(Value)],
    sum(Value) as [sum(Value)],
    avg(Value) as [avg(Value)],
    min(Value) as [min(Value)],
    max(Value) as [max(Value)]
from (values
    -- nullの行が1つ
    (cast(null as int))) as Test(Value);

/*
count(Value) sum(Value)  avg(Value)  min(Value)  max(Value)
------------ ----------- ----------- ----------- -----------
0            NULL        NULL        NULL        NULL
警告: NULL 値は集計またはその他の SET 演算で削除されました。
*/

ん?警告?

参考

COUNT (Transact-SQL) | Microsoft Docs

ASP.NET Core - ミドルウェアパイプラインを作ってみる

ミドルウェアとかミドルウェアパイプラインとかリクエストデリゲートなどを理解するために、次のドキュメントを読みながら記事内にあるミドルウェアパイプラインの画像の動きを作ってみました。

ASP.NET Core のミドルウェア | Microsoft Docs

この画像のことです。

https://docs.microsoft.com/ja-jp/aspnet/core/fundamentals/middleware/index/_static/request-delegate-pipeline.png

Use/Run拡張メソッドを使ってパイプラインを作る

Startup.Configureメソッド内でミドルウェアパイプラインを構築します。Use拡張メソッドやRun拡張メソッドを使うと、ミドルウェアをインライン(ラムダ式)で記述できます。Use拡張メソッドは次のミドルウェアコンポーネントとも言ったりしてる)を呼び出しますが、Run拡張メソッドは次のミドルウェアを呼び出さずパイプラインの終端になります。

public class Startup {
    public void ConfigureServices(IServiceCollection services) {
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
        // 最初に呼び出されるミドルウェア
        app.Use(async (context, next) => {
            context.Response.ContentType = "text/plain";

            // 次を呼び出す前の処理
            await context.Response.WriteAsync("Middleware 1 before\n");

            // 次のミドルウェアを呼び出す
            await next.Invoke();

            // 次を呼び出した後の処理
            await context.Response.WriteAsync("Middleware 1 after\n");
        });

        // 2つ目のミドルウェア
        app.Use(async (context, next) => {
            await context.Response.WriteAsync("\tMiddleware 2 before\n");

            await next.Invoke();

            await context.Response.WriteAsync("\tMiddleware 2 after\n");
        });

        // 最後に呼び出されるミドルウェア
        app.Run(async context => {
            await context.Response.WriteAsync("\t\tMiddleware 3\n");
        });
    }
}

実行すると次のレスポンスが返ります。

Middleware 1 before
    Middleware 2 before
        Middleware 3
    Middleware 2 after
Middleware 1 after

ミドルウェアクラスとUseMiddleware拡張メソッドを使ってパイプラインを作る

上記と同じことをクラスを使って再現したいと思います。インラインでの処理をミドルウェアクラスにカプセル化します。

ミドルウェアクラスはHttpContextを引数に持つInvokeAsyncメソッドを持っていればいいみたいです。

// 最初に呼び出されるミドルウェア
public class Middleware1 {
    private readonly RequestDelegate _next;

    public Middleware1(RequestDelegate next) {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context) {
        context.Response.ContentType = "text/plain";

        // 次を呼び出す前の処理
        await context.Response.WriteAsync("Middleware 1 before\n");

        // 次のミドルウェアを呼び出す
        await _next(context);

        // 次を呼び出した後の処理
        await context.Response.WriteAsync("Middleware 1 after\n");
    }
}

// 2つ目のミドルウェア
public class Middleware2 {
    private readonly RequestDelegate _next;

    public Middleware2(RequestDelegate next) {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context) {
        await context.Response.WriteAsync("\tMiddleware 2 before\n");

        await _next(context);

        await context.Response.WriteAsync("\tMiddleware 2 after\n");
    }
}

// 最後に呼び出されるミドルウェア
public class Middleware3 {
    // 使わないけど
    private readonly RequestDelegate _next;

    public Middleware3(RequestDelegate next) {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context) {
        await context.Response.WriteAsync("\t\tMiddleware 3\n");
    }
}

public class Startup {
    public void ConfigureServices(IServiceCollection services) {
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
        // 最初に呼び出されるミドルウェア
        app.UseMiddleware<Middleware1>();

        // 2つ目のミドルウェア
        app.UseMiddleware<Middleware2>();

        // 最後に呼び出されるミドルウェア
        app.UseMiddleware<Middleware3>();
    }
}

実行すると上記と同じレスポンスが返るようになります。

普段UseStaticFilesメソッドやUseMvcメソッドを使っていると思いますが、その中で実際にはミドルウェアはクラスとして用意されていて、UseMiddleware拡張メソッドを使っているようです。

勉強になりました。

AzureのComputer Vision APIのOCR機能を試してみた

Azure Cognitive ServicesにあるComputer Vision APIを使ってみました。

Computer Vision — 画像処理および画像分析 | Microsoft Azure

Computer Vision APIといってもいくつか機能があって、今回はそのうちOCR機能をJavaScript(というかTypeScript)で試してみました。

OCRを試す

APIの仕様はここにあります。

使い方は簡単でAPIのエンドポイントURLに、画像のURLをJSON形式でPOSTするか、画像のバイナリデータをPOSTするだけです。

TypeScriptでバイナリデータをPOSTする場合は次のような感じになるのかなと思います。

// 画像をBlobとして取得するとして
const blob: Blob = await getBlob();

// APIのURL(今回は東アジア)
const apiUrl = "https://eastasia.api.cognitive.microsoft.com/vision/v1.0/ocr";

// APIのキー
const apiKey = "内緒のキー";

// APIを呼び出す
const response = await fetch(apiUrl, {
    method: "POST",
    // リクエストのヘッダにメディアタイプとAPIのキーを指定する
    headers: {
        "Content-Type": "application/octet-stream", // ボディのメディアタイプ
        "Ocp-Apim-Subscription-Key": apiKey  // APIのキー
    },
    // リクエストのボディにBlobを設定する
    body: blob
});

// レスポンスはJSON
// OCRの結果を取得できる
const result: IResult = await response.json();

/*
// 適当に行(文字列)を抽出してみるならこんな感じ
result.regions
   .map(region => region.lines)
   .reduce((previous, current) => previous.concat(current))
   .map(line => line.words.map(word => word.text).join(" "))
   .forEach(line => console.log(line));
*/

レスポンスにはOCRの結果が入ります。フォーマットはJSONで、次のようなインターフェイスを持ったオブジェクトです。単にテキストだけでなく、その座標も取得できるようです。

// OCRの結果
interface IResult {
    language: string;
    textAngle: number;
    orientation: string,
    // 複数のリージョン
    regions: IRegion[];
}

// リージョン
interface IRegion {
    boundingBox: string;
    // 複数行を持つ
    lines: ILine[];
}

// 行
interface ILine {
    boundingBox: string;
    words: IWord[];
}

// 単語
interface IWord {
    boundingBox: string;
    text: string;
}

参考

JavaScriptチュートリアルとかはこのあたり。

ASP.NET Core MVC - ビューコンポーネントの呼び出し方色々

ASP.NET Core MVCのビューコンポーネントを試してみました。

ビュー コンポーネント | Microsoft Docs

ビューコンポーネントは従来のASP.NET MVCにあったチャイルドアクション(子アクション、ビューからRenderActionで呼び出していたあれ)の代わりになる機能ですね。呼び出し方が色々あるようなので、ちょっと書き残しておきます。

ビューコンポーネント名を指定する

上記ドキュメントにありますが、ビューコンポーネント名を指定して呼び出す方法があります。試してみましょう。

次のようなビューコンポーネントクラスを用意します。csファイルはプロジェクト直下に「ViewComponents」フォルダを作ってそこに入れるといいのかな?

// 適当なビューコンポーネント
public class Sample1ViewComponent : ViewComponent {
    public async Task<IViewComponentResult> InvokeAsync(int no, string name) {
        // 何か重たい処理があるとして
        await Task.Delay(0);

        // ViewBagも使える(それかViewModelを作るか)
        ViewBag.No = no;
        ViewBag.Name = name;

        return View();
    }
}

続いてビューコンポーネントの呼び出し側。Component.InvokeAsyncメソッドの1つ目の引数にビューコンポーネント名を、2つ目の引数にはSample1ViewComponent.InvokeAsyncメソッドの引数を匿名オブジェクトで指定します。

@await Component.InvokeAsync("Sample1", new { no = 1, name = "Sample1 string" })

型を指定する

typeof演算子で型を指定して呼び出す方法もあります。

@await Component.InvokeAsync(typeof(Sample1ViewComponent), new { no = 2, name = "Sample1 typeof" })

ジェネリック メソッドを使う

ジェネリックジェネリクス?)メソッドが用意されているので、次のような呼び出し方もできます。ただ丸括弧"()"を使う必要があってちょっとだけ残念。ちょいちょい括弧を忘れてコンパイラさんに怒られそう。

@(await Component.InvokeAsync<Sample1ViewComponent>(new { no = 3, name = "Sample1 generics" }))

引数にクラスを使う

ビューコンポーネントを呼び出す引数は、匿名オブジェクトではなく具体的なクラスでも問題なさそうです。

引数が多いという体で引数をクラスにまとめます。

// 適当なビューコンポーネントその2
public class Sample2ViewComponent : ViewComponent {
    // ビューコンポーネントの呼び出しパラメータクラス
    // インナークラスにする必要はないけど
    public class Param {
        public int No { get; set; }
        public string Name { get; set; }
    }

    // 引数にParamクラスを受け取る
    public async Task<IViewComponentResult> InvokeAsync(Param param) {
        // 何か重たい処理があるとして
        await Task.Delay(0);

        ViewBag.No = param.No;
        ViewBag.Name = param.Name;

        return View();
    }
}

呼び出し側もわかりやすいのでこの方法もありかなと思います。

@await Component.InvokeAsync(typeof(Sample2ViewComponent), new Sample2ViewComponent.Param { No = 4, Name = "Sample2" })

コントローラのアクションから呼び出す

ちょっと話は変わりますが、ビューコンポーネントはビューだけでなくコントローラのアクションメソッドでも呼び出せます。Ajaxでも使いたいときですかね。

// ビューコンポーネントを返すアクション
public IActionResult Sample1() {
    return ViewComponent(typeof(Sample1ViewComponent), new { no = 5, name = "Sample1 controller" });
}

こんなところかなと。