ListObjectの作成と概要
ListObjectについて、下記のようなシリーズで説明していきます。・ListObjectの作成と概要 ←今回
・ListObjectの並べ替え
・ListObjectの絞り込み
・ListObjectの絞り込みの解除
・ListObjectの絞り込みデータの配列化
・ListObjectのデータ追加
・ListObjectのデータ変更
・ListObjectのデータ削除
今回は、Excelワークシート上の「データベーステーブル」である「ListObjectオブジェクト」の作成方法と概要について説明します。
1.テーブルの作成と手動操作
まず既存の表(例:図1のA1~G8セルの範囲)を手動でテーブルにするには、データ範囲の中の1セル(図1ではA1セル)を選択①し、リボンの「挿入」タブ→「テーブル」グループ→「テーブル」ボタンをクリック②します。すると「テーブルの作成」ダイアログが表示されますので、テーブルの範囲と先頭行を見出しとするか否かのチェックを確認し、OKボタン③をクリックします。
図1
①~③の操作で、表はテーブル(ListObjectオブジェクト)として扱うことができるようになります。
また既定のテーブル名は、そのシートで最初に作ったテーブルであれば「テーブル1」となっていますが、テーブルの一部または全部を選択④し、リボンの「テーブルデザイン」タブ→「プロパティ」グループ→「テーブル名」の欄を書き換えることで「テーブル名を変更⑤」することが可能です。
図2
テーブルの外見上(既定状態)は、見出し行(図3の1行目)が濃い青の背景色+白色文字で、データ行はまだらの背景色が設定されています。
図3
また各見出しにはフィルター(下向き三角形の印)が自動的に設定され、クリックで表示されるダイアログ(図3の右側)を使って「並べ替え」や「絞り込み」が出来ます。
テーブル範囲の右下角のセルには「テーブルの角」であるマークがあり、テーブル範囲に接触する行・列のセルに値を入力すると、テーブルの範囲は自動的に広がります。
逆に、テーブル範囲のある行の「全てのデータをクリア」しても自動的にテーブル範囲が狭まることはありません。データ行を削除し、正しいテーブル範囲を確保するには、図4のように「削除したいデータ行のセルで右クリックし、メニューの中から「削除」→「テーブルの行」を選択する事によりデータ行が削除され、テーブルの範囲も行方向に縮小します。ちょうど「削除+上方向にシフト」を選んだような形になります。
図4
一方「行単位で削除(マクロ操作でのEntireRow.Delete)」を使ってもテーブル範囲は縮小できますが、1つのシートに複数のテーブルを置いてある場合には、他のテーブルに影響を与えてしまうため注意が必要です。
2.テーブルスタイルのオプション
テーブルのスタイルとして選択できる項目には、図5のように7項目あります。テーブル内のどこかを選択した状態で、上部リボンの「テーブルデザイン」タブ→「テーブルスタイルのオプション」から操作します。図5
7項目の内容は図6のようになっています。テーブルを作成した直後は、「見出し行」「縞模様(行)」「フィルターボタン」のみがON(チェックボックスにレ点あり)の状態です。
項目 | 既定 | 内容 | プロパティ |
---|---|---|---|
見出し行 | ON | タイトル行の表示 | Show |
集計行 | OFF | 集計行の表示 | Show |
縞模様(行) | ON | 背景色を行方向に縞模様にする | ShowTableStyle |
最初の列 | OFF | 1列目を太字にする | ShowTableStyle |
最後の列 | OFF | 最終列を太字にする | ShowTableStyle |
縞模様(列) | OFF | 背景色を列方向に縞模様にする | ShowTableStyle |
フィルターボタン | ON | フィルターボタンを表示 | ShowAutoFilter |
この中で「見出し行」「集計行」は、テーブルの構造に影響する項目です。見出し行・集計行をON-OFFした時のテーブルの状態を図7に示します。
図7
「集計行」をONにするとデータ部(DataBodyRange)の下に集計行が追加されます(図7の左側)。また「見出し行」をON-OFFすると、タイトル行が表示ー非表示(図7の右側)します。
また「フィルターボタン」をOFFにすると、タイトル行からフィルターボタンが消えるため、ユーザーからは並べ替え・絞り込みが出来なくなる形になります。但し、実際には「データ」タブ→「並べ替えとフィルター」→「フィルター」ボタンをON-OFFすることで、再表示が可能です。
これは、テーブルデザイン上の「フィルターボタン」チェックボックスの元スイッチが、ツール上の「フィルター」になっている為のようです。ツール側のフィルターをOFFにしておく(=ボタンがハイライトしていない状態)とテーブルデザイン側のフィルターボタンが「Enabled = False状態(=薄い灰色で操作不能)」となることからも頷けます。
その他の縞模様等の設定は、表示形式の変更のみですので機能的には影響が無いと思われます。
3.テーブルの見出し部・データ部の指定法
テーブルをVBAから操作するには、テーブル各部の指定方法を知っておく必要があります。まず、テーブルの「見出し+データ」の部分を指定するには「Range」を使用します。従ってテーブル全体の範囲を示すには、図8のように「ListObjects(テーブル名).Range」となります。(以降の説明ではテーブル名をDVDmgtとしています)
図8
一方、テーブルの見出し部を除いた「データのみ」の部分を指定するには、Rangeの代わりに「DataBodyRange」を使用します。テーブルの全データ範囲を示すには、図9のように「ListObjects(テーブル名).DataBodyRange」となります。
図9
テーブルの「見出し」の部分を指定するには「HeaderRowRange」を使用します。テーブルの見出し範囲全てを示すには、図10のように「ListObjects(テーブル名).HeaderRowRange」となります。
図10
4.テーブルの列・行の指定法
テーブル本体やテーブルの列・行を特定する方法について、ワークシートの場合と対比して図11に整理します。まず、通常のExcelワークシートを特定する手段には、「Sheets("Sheet1")」などと「シートの名前」を指定する方法と、「Sheets(1)」などと「並んでいるシートの位置」を指定する方法があります。
これと同様に、テーブルは「テーブル名」・「作った順番」のどちらからでもアクセスできます。またテーブルの列も「見出しの文字列」または「テーブルの左側からの列位置」で特定できます。
なお、テーブルのデータ行に対しては、上から何行目という「行位置」で特定します。
コレクション | オブジェクト | ||
---|---|---|---|
名前を使用 | 位置を使用 | ||
シート | Sheets | Sheets("Sheet1") | Sheets(1) |
テーブル | ListObjects | ListObjects("DVDmgt") | ListObjects(1) |
テーブルの列 | ListColumns | ListColumns("DvdNo") | ListColumns(1) |
テーブルのデータ行 | ListRows | ― | ListRows(3) |
テーブルの指定方法については、上述した様に図2の⑤で設定取得可能な「テーブル名」、またはワークシート上でテーブルを「作った順番」で表します。複数のテーブルを作った後、先に作成したテーブルを削除した際には順番が繰り上がりますので「その時点での作られた順番」という事になります。
次に列の指定方法についてです。
図12のようにDvdNo列(=テーブルとしては1列目)を指定するには、「ListObjects(テーブル名).ListColumns("DvdNo").Range」または「ListObjects(テーブル名).ListColumns(1).Range」とします。
ここで、範囲名として「Range」を使用していますので、図8と同様に「見出し+データ」が範囲になります。
次に列の指定方法についてです。
図12のようにDvdNo列(=テーブルとしては1列目)を指定するには、「ListObjects(テーブル名)
ここで、範囲名として「Range」を使用していますので、図8と同様に「見出し+データ」が範囲になります。
図12
これを「データ部分のみ」の列範囲にするには、「Range」を図9で使用した「DataBodyRange」に置き換え、図13の様に「ListObjects(テーブル名)
図13
また「ListColumns(列位置)」のまとまり(コレクション)は、図14のように「ListColumns」となりますので、列数を得るには「ListColumns.Count」となります。なお、図10のタイトル行(HeaderRowRange)のセル数も列数と同じ値になりますので、「HeaderRowRange.Count」としても列数が得られます。
図14
一方、行を指定する場合は「行には行見出しが無い」ために「データの先頭からの行位置」を使って、図15のように「ListObjects(テーブル名)
この際、ListRowオブジェクトに対してのプロパティの中に「DataBodyRange」は存在しません。「Range」でセル範囲を指定します。
図15
また「ListRows(行位置)」のまとまり(コレクション)は、図16のように「ListRows」となりますので、行数を得るには「ListRows.Count」となります。
図16
タイトルだけでデータが無いテーブルもあります。その場合図17のように、タイトル行の下に空行が見えるので「これはDataBodyRangeでは?」と思われるかもしれませんが、これは「InsertRowRange」という「次に新たにデータを入れる行」です。
InsertRowRangeは、データの無いテーブルにのみ存在するもので、データが入っているテーブルでは「InsertRowRange Is Nothing」となります。
図17
また、テーブルデザインのオプションで「集計行」をONにすると、図18のようにデータ領域の下に集計行が表示されます。その集計行は「TotalsRowRange」となります。なお、表示されていない時には「TotalsRowRange Is Nothing」となります。
図18
5.テーブルデザインのオプションによる指定範囲の違い
テーブルデザインのオプションを使用すると「集計行」や「見出し行」をON-OFFすることが出来ます。既定の場合は、「Range」「DataBodyRange」「HeaderRowRange」は図19のような範囲となります。この場合、データが存在するテーブルですので「InsertRowRange」はありませんし、また集計行もONになっていないため「TotalsRowRange」もありません。
図19
しかし「集計行」や「見出し行」をON-OFFした場合は、それに応じて、見出し行(HeaderRowRange)及び集計行(TotalsRowRange)の有無が図20のように変わります。
特に「Range」の範囲は影響を受けてしまうため、例えば「Rangeの一番上の行は必ずHeaderRowRange」や「Rangeの最終行の1つ下は必ず空白」となる訳では無いので注意が必要です。
図20
またデータが1つも無いテーブルの場合、既定では「Range」「HeaderRowRange」「InsertRowRange」は図21のようになります。
図21
この場合も、「集計行」「見出し行」のON-OFFにより、図22のように変わります。
図22
6.テーブルの削除(解除)
テーブルを削除するには、以下の手順で行えます。・テーブル範囲全体をセル選択→「右クリックメニュー」→「削除」→「テーブルの列」または「テーブルの行」
・テーブル範囲全体をセル選択→「Delキー」
但し両方とも「データが消えてしまう」のと、Delキーの方は「背景色+タイトル白文字」の書式は残ってしまいます。
データを残したまま「テーブルを削除」するには、図23のようにテーブルの一部または全部をセル選択⑥し、リボンの「テーブルデザイン」タブ→「ツール」グループ→「範囲に変換」をクリック⑦します。すると確認のダイアログ⑧が表示されるのでOKボタンをクリックするとテーブルが削除(=解除)されます。
図23
但し「背景色+タイトル白文字」や「まだらの背景色」の書式は残ったままとなるので、手動での変更作業が必要となります。
また、シート上にテーブルが複数あり、先に作ったテーブルを削除した場合は、シート内でのListObjectsの順番が繰り上がりますので、「作った順番」でテーブルを指定している場合は注意が必要です。
7.マクロによるテーブルの作成
マクロ側からテーブル(ListObject)を作成するには、Addメソッドを使用します。例えばSheet1のA1セルを含んだ範囲に、テーブルの元データ(タイトル行もあり)があり、そのデータを「myTable」という名前のテーブルにするのが図24です。- '========== ⇩(1) テーブルの作成 ============
- Sub makeTable_1()
- With Sheets("sheet1")
- .ListObjects.Add( _
- SourceType:=xlSrcRange, _
- Source:=.Range("A1").CurrentRegion, _
- XlListObjectHasHeaders:=xlYes, _
- TableStyleName:="TableStyleMedium2" _
- ).Name = "myTable"
- End With
- End Sub
04行目「Sheets("sheet1").ListObjects.Add」がテーブル(ListObject)を作っているコードです。そのAddメソッドには図25のようなパラメータがあります。
パラメータ | データ型 | 内容 |
---|---|---|
SourceType | XlListObject | データソースの種類 |
Source | Variant | データソースを表すデータ範囲 |
LinkSource | Boolean | 外部データソースをリンクするか否か SourceType が xlSrcRange の場合は省略 |
XlListObject | XlYesNoGuess | タイトル行の有無 |
Destination | Variant | 移動先のセル位置 |
TableStyleName | String | TableStyle の名前 |
「SourceType」パラメータは、データソースの種類を指定するもので、図26の中から選びます。今回はワークシート上のデータ範囲をテーブルにしますので、05行目「SourceType:=xlSrcRange, _」と設定します。なお、xlSrcRangeは既定値ですので省略してもOKです。
定数 | 値 | 内容 |
---|---|---|
xlSrcExternal | 0 | 部データソース |
xlSrcRange | 1 | セル範囲(既定値) |
xlSrcXml | 2 | XML |
xlSrcQuery | 3 | クエリ |
xlSrcModel | 4 | PowerPivotモデル |
「Source」パラメータには、データ範囲をRangeで指定します。06行目「Source:= Sheets("sheet1").Range("A1").CurrentRegion, _」では、図27のように「連続している範囲」をデータ範囲とするために、A1セルを起点にして「空白行・空白列」で囲まれた範囲をCurrentRegionプロパティを使って取得し、Sourceパラメータに指定しています。
図27
「XlListObjectHasHeaders」パラメータは、指定したデータ範囲に「タイトル行があるか否か」を指定します。指定できる値は、図28の3種です。07行目「XlListObjectHasHeaders:=xlYes _」では、「タイトル行あり」を指定しています。
テーブルにしようとしているデータにタイトル行がある場合は「xlYes」を、無い場合は「xlNo」を指定します。xlNoを選んだ場合は、「列1」「列2」・・・と自動的に列名が振られます。
なお「xlGuess」を指定すると、作成時にExcel側で判断をしてくれます。
定数 | 値 | 内容 |
---|---|---|
xlGuess | 0 | タイトルの有無をExcelが自動判断(既定値) |
xlYes | 1 | タイトル行あり |
xlNo | 2 | タイトル行なし |
「TableStyleName」パラメータは、作成したテーブルのスタイルを指定します。テーブルのスタイルは、リボンの「テーブルデザイン」タブ→「テーブルスタイル」グループ→「クイックスタイル」で表示されるダイアログ内から選び、そのスタイル形式の「文字列」を指定することになります。形式は図29のように「(淡色)TableStyleLight」「(中間)TableStyleMedium」「(濃色)TableStyleDark」の後に数字をつけた文字列です。なお自作もできるようです。
図29
08行目「TableStyleName:="TableStyleMedium2" _」では、「"TableStyleMedium2"」というスタイルを設定しています。設定を省略すると、これと思われるスタイルになります。
なお「書式なし」である「""(長さゼロの文字列)」は、08行目のようにAddメソッドのパラメータとして設定してしまうとエラーが発生します。もし書式なしを設定したいのであれば、図30のように一旦既定スタイルでテーブルを作成し、その後30行目「.ListObjects("myTable").TableStyle = ""」で書式を変更して下さい。
- '========== ⇩(2) 書式なしのテーブルの作成 ============
- Sub makeTable_2()
- With Sheets("sheet1")
- .ListObjects.Add( _
- SourceType:=xlSrcRange, _
- Source:=.Range("A1").CurrentRegion, _
- XlListObjectHasHeaders:=xlYes _
- ).Name = "myTable"
- .ListObjects("myTable").TableStyle = ""
- End With
- End Sub
パラメータとして他に「LinkSource」「Destination」がありますが、ワークシートをテーブル(SourceTypeパラメータに xlSrcRangeを設定)にする際には使用しません。
最後に09行目「).Name = "myTable"」で、Addメソッドでテーブルを作成すると同時にテーブルの名前(ここでは"myTable")を設定しています。
なお「テーブル名はExcelブック内で重複できない」という制約があります。そのため手動で設定する場合は、図2の⑤でテーブル名を設定する際に「既存の名前を入れるとエラー」が発生します。
しかしマクロ側からテーブル名を設定する際は、ちょっと様子が違います。
まず、テーブル(ListObject)の名前には2種あります。「Nameプロパティ」と「DisplayNameプロパティ」です。
「Name」の方は「ListObjects(テーブル名)」のように「オブジェクト名」として使用されます。一方「DisplayName」は、Excelのリボン上に表示される「ユーザー側から目に見えるテーブル名」となります。2つの名前の特徴を図31に整理します。
用途 | 制限 | |||
Name | ListObjects(〇〇)と オブジェクトの名前に使用 | シート内で 重複禁止 | ||
---|---|---|---|---|
DisplayName |
| ブック内で 重複禁止 |
「テーブル名はExcelブック内で重複できない」と前述しましたが、調べてみると「ブック内で重複不可なのはDisplayName」であって、Nameプロパティの方はシート内で重複していなければ設定は可能なようです。
但し、手動でワークシート上部のテーブル名の枠内にテーブル名を入れる場合は、ブック内で重複していないテーブル名しか設定できませんので、「Name」と「DisplayName」には同じ値が入ることになります。
またマクロ側から設定する場合も、通常は他のテーブル名を調べた(または、各テーブルの名前を走査するコードを組み込んだ)上で、「ブック内で重複しない名前」をNameプロパティに設定するはずなので、「ブック内で重複していない値」が「Name」「DisplayName」の両方に同じ値として入っているだけなのです。
そのような重複しない努力をせずに、例えば「Sheet2に"myTable"という名前のテーブル」がある状態で、図30のマクロ(Sheet1にテーブルを作り、Nameプロパティに"myTable"を設定する)を動かすと、作られたテーブルは
・Nameプロパティ="myTable"
・DisplayNameプロパティ="myTable_1"
という設定値になります。(但し、Sheet1に"myTable"が存在せず、ブック内に"myTable_1"が無い時)
つまり「Nameプロパティ」に対してテーブル名を設定する際に「シート内で名前が重複していない」限りは、「Name」には指定した名前が設定されます。そして、その名前を「DisplayName」にもコピーする際には「ブック内の全てのテーブル名を調べ」て、同じ名前が存在するようであれば「名前に追番を振る」ような動きをしているようです。なお、シート内で重複している時には実行時エラーが出ます。
また「DisplayNameプロパティ」に対してテーブル名を設定する際には、まず「ブック内の全てのテーブル名を調べ」るので、同じ名前が存在する時はエラーが出ます。この場合、自動的に名前を変更してくれる事はありません。
但しテーブルの作成は完了しており、名前が「テーブル1」のような既定名となります。
以上「テーブル名が重複していた時に、Name値設定・DisplayName値設定」をした時の結果を図32にまとめます。
図32
テーブルを作るときにエラーが出てくれさえすれば「あっ、テーブル名が重複していた」と気づくのですが、エラーが出ずに「Name と DisplayName が異なる値」になってしまうと、制御するときのテーブル名と見た目のテーブル名が異なることになってしまいます。
それでも「シート名+テーブル名」のセットで扱っていれば制御は可能ですが、間違いの元だとおもいますので、図33のような「設定したいテーブル名の存在有無の確認関数」を使った後にテーブル作成段階に移るべきと思います。
- '========== ⇩(3) テーブル名の存在有無を確認する関数 ============
- Function IsTableName(CheckName As String) As Boolean
- '戻り値:True=存在する False=存在しない
- Dim Sh As Worksheet '←調べるワークシート
- Dim i As Integer '←シート内のテーブルの数
- CheckName = UCase(CheckName)
- For Each Sh In ThisWorkbook.Worksheets
- For i = 1 To Sh.ListObjects.Count
- If UCase(Sh.ListObjects(i).DisplayName) = CheckName Then
- IsTableName = True
- Exit Function
- End If
- Next i
- Next Sh
- End Function
図33は、引数に「設定したいテーブル名」を渡して実行することで、ブック内に存在する場合はTrueが返る関数です。
テーブル名を「ListObjects(テーブル名)」の様に使う場合は、大文字小文字を無視して「myTable」も「MyTABLE」も同じテーブル名として扱ってくれます。しかし「ListObjects(1).Name = テーブル名」のような使い方をした時は、大文字小文字で異なった文字列として判断されてしまいます。
ですので46行目「CheckName = UCase(CheckName)」で、テーブル名を大文字揃え(UCase関数)にして比較をするようにしています。
48行目「For Each Sh In ThisWorkbook.Worksheets」では、ワークシート1つ1つについて調べていきます。
49行目「For i = 1 To Sh.ListObjects.Count」は、調べているワークシート上にあるテーブルを1つ1つ調べます。もしテーブルが存在しない場合は「Sh.ListObjects.Count = 0 」ですので、49~54行目のFor~Nextが終了してしまい、次のワークシートへ調査が移動することになります。
50行目「If UCase(Sh.ListObjects(i).DisplayName) = CheckName Then」では「DisplayNameの値」と、引数で得た「調べるテーブル名」を比較しています。比較する際には46行目で「大文字揃え」にしていますので、DisplayName側もUCaseで大文字に変換します。
ここでDisplayNameの代わりにNameを使ったり、「ListObjects(CheckName) Is Nothing」のようなオブジェクト名での存在確認をしてしまうと、正しい確認ができません。
例えばSheet1→Sheet2の順に、テーブル名を同じ「myTable」にしてマクロ作成したとすると、以下のような名前になります。
・Sheet1には、テーブル.Name = "myTable"、テーブル.DisplayName = "myTable"
・Sheet2には、テーブル.Name = "myTable"、テーブル.DisplayName = "myTable_1"
この後にテーブル名として「myTable_1」の存在を確認したとすると、Nameプロパティを使った条件式では引っ掛かりませんので「DisplayNameプロパティ」での確認が必須です。
もし50行目が成立(=調べているテーブル名が存在)したら、51行目「IsTableName = True」で関数の戻り値にFalseを設定し、52行目「Exit Function」で抜け出します。
逆に最後のシート・最後のテーブルまで調べても50行目が成立しなかったら、関数戻り値に何も設定しないまま終了しますので、関数のデータ型(Boolean型)の初期値であるFalseが戻ることになります。
8.マクロによるテーブルの削除・解除
テーブルを削除・解除する方法は2種類ありそうです。1つ目はListObjectの「Deleteメソッド」を利用する図34です。Deleteメソッドを使用すると、文字通り「テーブルのデータも書式も削除」されます。と言っても「セル範囲の削除」のように下方や右側のセルが上方向や左方向にシフトする訳でも無く、周囲に影響を与えずに綺麗に消えてくれます。
- '========== ⇩(4) テーブルの削除 ============
- Sub TableDel_1()
- Sheets("Sheet1").ListObjects("myTable").Delete
- End Sub
2つ目は「Unlistメソッド」を利用する図35です。こちらは「データも書式も残り、テーブルだけが解除」されます。
- '========== ⇩(5) テーブルの解除 ============
- Sub TableDel_2()
- Sheets("Sheet1").ListObjects("myTable").Unlist
- End Sub
9.テーブルスタイルのオプション設定を吸収するコード
テーブルのスタイルは、ユーザーの操作で「見出し行」「集計行」のON-OFFができてしまいます。そのため「テーブルデザインのオプションによる指定範囲の違い」で説明したように、「Range」の範囲が変わってしまったり「HeaderRowRange」が有ったり無かったりする可能性のある上で、テーブルに対するプログラムを動かす必要があります。ON-OFFの状態を取得して、その状態に合わせた処理をする方法もありますが、分岐が多くなるデメリットがあります。ですので「既定の状態(見出し行=ON、集計行=OFF)に戻してから処理」をするのも1つの方法だと思い、そのコードを図36で紹介します。
- '========== ⇩(6) テーブルスタイルの設定を吸収 ============
- Sub TableStyle_Default()
- Dim isHeader As Boolean '←見出し行の設定値
- Dim isTotal As Boolean '←集計行の設定値
- isHeader = T.ShowHeaders '←見出し行の設定値を取得
- isTotal = T.ShowTotals '←集計行の設定値を取得
- T.ShowHeaders = True '←見出し行をONにする
- T.ShowTotals = False '←集計行をOFFにする
- ' === 処理内容 ====
- T.ShowHeaders = isHeader '←見出し行を復元する
- T.ShowTotals = isTotal '←集計行を復元する
- End Sub
内容は単純で、85行目「isHeader = T.ShowHeaders」で「見出し行」の状態を、86行目「isTotal = T.ShowTotals」で「集計行」の状態を取得し仮置きしておきます。
そして88行目「T.ShowHeaders = True」で見出し行をONに、89行目「T.ShowTotals = False」で集計行をOFFにして「既定の状態」にします。
既定の状態にした上で必要な処理をした後、93行目「T.ShowHeaders = isHeader」で見出し行を元の状態に、94行目「T.ShowTotals = isTotal」で集計行を元の状態に戻します。
なおユーザーが設定したスタイルは無視し、容赦なく「既定状態」に戻す方法も有りだと思います。その場合は89~90行目だけを実行すれば良いことになります。
アプリ実例
「DVD等の内容・保管場所等管理システム」「先行予約可能な備品予約・貸出システム」
「ToDoリストで個人タスク管理」
「会社番号検索システム」