ユーザー定義フィールドのデータベースを設計する方法は?


145

私の要件は:

  • 任意のデータ型のユーザー定義フィールドを動的に追加できる必要がある
  • UDFをすばやく照会できる必要がある
  • データ型に基づいてUDFの計算を実行できる必要がある
  • データ型に基づいてUDFをソートできる必要がある

その他の情報:

  • 主にパフォーマンスを探しています
  • UDFデータを添付できる数百万のマスターレコードがあります。
  • 私が最後に確認したとき、現在のデータベースには50mil以上のUDFレコードがありました
  • ほとんどの場合、UDFはすべてではなく数千のマスターレコードにのみ添付されます。
  • UDFは結合されないか、キーとして使用されません。これらはクエリやレポートに使用される単なるデータです

オプション:

  1. StringValue1、StringValue2 ... IntValue1、IntValue2、...などで大きなテーブルを作成します。私はこのアイデアは嫌いですが、他のアイデアよりも優れていると誰かが教えてくれればそれを考慮します。

  2. 必要に応じてオンデマンドで新しい列を追加する動的テーブルを作成します。また、すべての列にインデックスを付けないとパフォーマンスが低下するので、このアイデアは好きではありません。

  3. UDFName、UDFDataType、およびValueを含む単一のテーブルを作成します。新しいUDFが追加されたら、そのデータだけをプルして指定されたタイプに解析するビューを生成します。解析基準を満たさないアイテムはNULLを返します。

  4. データ型ごとに1つずつ、複数のUDFテーブルを作成します。したがって、UDFStrings、UDFDatesなどのテーブルがあります。おそらく、#2と同じように実行され、新しいフィールドが追加されるたびにビューを自動生成します。

  5. XMLデータ型?私はこれまでこれらを扱ったことがありませんが、言及されているのを見ました。特にパフォーマンスに関して、期待どおりの結果が得られるかどうかはわかりません。

  6. 他に何か?


7
Martin Fowlerは、2(ユーザーが更新可能なスキーマ)または5(インデックス付きXML LOB)を推奨してい
Neil McGuigan

動的データベーススキーマに関するStackOverflowの質問もご覧ください。
FloverOwe

回答:


49

パフォーマンスが主な関心事である場合は、#6 ... UDFごとのテーブルを使用します(実際には、これは#2のバリアントです)。この回答は、特にこの状況と、説明されているデータ分散およびアクセスパターンの説明に合わせて調整されています。

長所:

  1. 一部のUDFはデータセット全体のごく一部の値を持っていることを示しているため、そのテーブルはUDFをサポートするために必要なだけの大きさになるため、別のテーブルを使用すると最高のパフォーマンスが得られます。同じことが関連するインデックスにも当てはまります。

  2. また、集計やその他の変換のために処理する必要があるデータの量を制限することで、速度が向上します。データを複数のテーブルに分割すると、UDFデータに対していくつかの集計およびその他の統計分析を実行し、その結果を外部キーを介してマスターテーブルに結合して、非集計属性を取得できます。

  3. データが実際に何であるかを反映するテーブル/列名を使用できます。

  4. データ型を使用して、制約、デフォルト値などをチェックし、データドメインを定義できます。オンザフライのデータ型変換によるパフォーマンスへの影響を過小評価しないでください。このような制約は、RDBMSクエリオプティマイザーがより効果的なプランを開発するのにも役立ちます。

  5. 外部キーを使用する必要がある場合でも、組み込みの宣言的参照整合性がトリガーベースまたはアプリケーションレベルの制約の強制によって実行されることはほとんどありません。

短所:

  1. これにより、多くのテーブルが作成される可能性があります。スキーマの分離や命名規則を適用すると、これを軽減できます。

  2. UDFの定義と管理を操作するには、さらに多くのアプリケーションコードが必要です。これは、元のオプション1、3、および4よりも必要なコードがまだ少ないと思います。

その他の考慮事項:

  1. UDFをグループ化するのに意味のあるデータの性質について何かあれば、それを奨励する必要があります。このようにして、これらのデータ要素を1つのテーブルに組み合わせることができます。たとえば、色、サイズ、コストのUDFがあるとします。データの傾向は、このデータのほとんどのインスタンスが次のようになることです

     'red', 'large', 45.03 

    のではなく

     NULL, 'medium', NULL

    このような場合、NULLになる値はほとんどなく、3つの列すべてにアクセスする必要がある場合に必要な結合が2つ少ないため、2つのテーブルを作成する必要がないため、1つのテーブルに3つの列を結合することで速度が著しく低下することはありません。 。

  2. 使用頻度が高く頻繁に使用されるUDFからパフォーマンスの壁にぶつかった場合は、マスターテーブルに含めることを検討する必要があります。

  3. 論理テーブルの設計によって特定の時点まで進むことができますが、レコード数が非常に大きくなると、選択したRDBMSによって提供されるテーブルパーティションオプションについても検討を開始する必要があります。


1
チェックリスト!私とフィルの間のジョークの中で、それがルールに反しないことを願っています。
GunnerL3510 2011年

おかげで、私はこれのいくつかのバリエーションを行うと思います。私たちのUDFデータのほとんどは、参照のみを目的として保持する必要があるマップされていないインポートフィールドから取得されているので、それらを1つのテーブルに入れたいと思います。他のUDFは必要に応じて定義され(事前に特定できません。通常、プロセスを変更したり、数か月間何か特別なものを追跡したりするときに作成されます)、クエリで一般的に使用されます。これらの値の論理ユニットごとに個別のテーブルを作成すると思います。
レイチェル

UDFの日付/バージョンが設定されたテーブルを使用しています。このメソッドを使用して、stackoverflow.com / a / 123481/328968 で最新の値を取得します。
Peter

22

私はこの問題についてたくさん書いています。最も一般的な解決策は、Entity-Attribute-Valueアンチパターンです。これは、オプション#3で説明するものに似ています。 ペストのようなこのデザインは避けてください

本当に動的なカスタムフィールドが必要なときにこのソリューションで使用するのは、それらをXMLのblobに格納することです。これにより、いつでも新しいフィールドを追加できます。ただし、スピーディーにするために、検索またはソートする必要があるフィールドごとに追加のテーブルを作成します(フィールドごとにテーブルを作成するのではなく、検索可能なフィールドごとにテーブルを作成します)。これは、逆インデックス設計と呼ばれることもあります。

このソリューションに関する2009年の興味深い記事は、http//backchannel.org/blog/friendfeed-schemaless-mysqlで読むことができます。

または、ドキュメント指向のデータベースを使用できます。この場合、ドキュメントごとにカスタムフィールドがあることが期待されます。Solrを選択します。


1
オプション#3を避けるべき理由を説明できますか?私はあなたの例のいくつかを見ましたが、それらは私がやろうとしていることと実際には同じではありません。すべての属性を格納する場所ではなく、単に追加のデータを格納する場所が必要です。
レイチェル

2
まず、属性をNULLにしないのは誰ですか?すべての属性を一意にせずに、どのように属性を一意にしますか?そこから続きます。アプリケーションコードを作成すると、RDBMSがすでに提供している機能を提供することになります。論理エンティティレコードを挿入してフェッチするために、ある種のマッピングクラスを作成しなければならないところまでです。
ビルカーウィン、2011

2
簡単に言えば、「データとメタデータを混在させない」です。fieldnameまたはのvarchar列を作成tablenameすると、メタデータ識別子がデータ文字列として保存されます。これが多くの問題の始まりです。en.wikipedia.org/wiki/Inner-platform_effect
ビルカーウィン

2
@Thomas:逆索引の設計では、データ型の標準スキーマソリューション、およびUNIQUEやFOREIGN KEYなどの制約を使用できます。EAVを使用する場合、これらはまったく機能しません。行ごとに異なる属性をサポートしているという理由だけで非リレーショナルであるという特性を反転インデックスがEAVと共有することに同意しますが、それは妥協点です。
ビルカーウィン、2011年

2
@thitami、何年にもわたって私が学んだことは、どのソリューションアプリにとって適切なものである可能性があるということです。EAVでさえ、特定のアプリにとって最も悪いソリューションではないかもしれません。クエリを知らないと、最適化戦略を選択できません。あらゆる種類の最適化は、他のクエリを犠牲にして特定のクエリを改善します。
ビルカーウィン2018年

10

私はおそらく次の構造のテーブルを作成します:

  • varchar名
  • varcharタイプ
  • 10進数値
  • varchar StringValue
  • 日付値

コースの正確なタイプは、ニーズによって異なります(もちろん、使用しているdbmsによっても異なります)。NumberValue(10進数)フィールドをintおよびbooleansに使用することもできます。他のタイプも必要になる場合があります。

値を所有するマスターレコードへのリンクが必要です。マスターテーブルごとにユーザーフィールドテーブルを作成し、単純な外部キーを追加するのがおそらく最も簡単で最速です。このようにして、マスターレコードをユーザーフィールドで簡単かつ迅速にフィルタリングできます。

何らかのメタデータ情報が必要になる場合があります。したがって、次のようになります。

テーブルUdfMetaData

  • int id
  • varchar名
  • varcharタイプ

テーブルMasterUdfValues

  • int Master_FK
  • int MetaData_FK
  • 10進数値
  • varchar StringValue
  • 日付値

あなたが何をしても、私はテーブル構造を動的に変更しません。メンテナンスの悪夢です。また、XML構造使用しません。非常に低速です。


私はあなたの戦略が好きで、多分それを選ぶかもしれませんが、2017年に何か違うものを選ぶでしょうか?jsonのように
maztt 2017

私たちのプロジェクトでは、jsonのようなものにシリアル化する独自のデータ構造を実装しました。それはキャストせずに、そして素晴らしいプログラミング言語統合でデータを読み書きするtypesaveインターフェースを特徴とします。それは本当に素晴らしいです。データベース内のこの種の「ドキュメント」すべてと同じ問題があります。特定の値を照会するのは難しく、「ドキュメント」の外部のデータを簡単に参照することはできません。使用方法によっては、どちらも問題にはなりません。
Stefan Steinegger 2017年

その上、2011年に私が提案したのは、私見がまだ有効な解決策です。
Stefan Steinegger 2017年

10

これは、MongoDBやCouchDBなどの非リレーショナルソリューションで解決できる可能性のある問題のようです。

どちらも動的スキーマ拡張を可能にすると同時に、求めるタプルの整合性を維持できます。

私はビル・カーウィンに同意します、EAVモデルはあなたのための高性能なアプローチではありません。リレーショナルシステムで名前と値のペアを使用することは、本質的に悪いことではありませんが、名前と値のペアが情報の完全なタプルを作成する場合にのみうまく機能します。これを使用すると、実行時にテーブルを動的に再構築する必要が生じ、あらゆる種類のものが困難になり始めます。クエリはピボットメンテナンスの練習になるか、タプルの再構築をオブジェクトレイヤーにプッシュすることを強制します。

オブジェクトレイヤーにスキーマルールを埋め込まないと、null値または欠損値が有効なエントリであるかエントリの欠如であるかを判断できません。

スキーマを効率的に管理できなくなります。100文字のvarcharは「値」フィールドの正しいタイプですか?200文字?代わりにnvarcharにする必要がありますか?これは難しいトレードオフであり、結果として、セットの動的な性質に人為的な制限を課す必要があります。「xのユーザー定義フィールドのみを使用でき、それぞれの長さはy文字のみです。

MongoDBやCouchDBのようなドキュメント指向のソリューションでは、ユーザーに関連付けられたすべての属性を単一のタプル内に維持します。結合は問題ではないので、誇大宣伝にもかかわらず、これら2つは結合ではうまくいかないため、人生は幸せです。ユーザーは、4MBに達するまで管理が難しくならない長さで、必要なだけ(または許可する限り)属性を定義できます。

ACIDレベルの整合性を必要とするデータがある場合は、ソリューションを分割することを検討してください。高整合性データはリレーショナルデータベースにあり、動的データは非リレーショナルストアにあります。


6

カスタム列を追加するユーザーを提供したとしても、それらの列に対するクエリが必ずしもうまく機能するとは限りません。クエリの設計には多くの側面があり、それらを適切に実行できます。その中で最も重要なのは、何を最初に格納するかについての適切な仕様です。したがって、基本的には、ユーザーが仕様について考えることなくスキーマを作成し、そのスキーマから迅速に情報を取得できるようにしたいですか?もしそうなら、特にユーザーがデータの数値分析を行えるようにしたい場合、そのようなソリューションがうまく拡張することは不可能なことです。

オプション1

IMOこのアプローチでは、スキーマが何を意味するのかがわからなくても、スキーマが提供されます。これは、災害のレシピであり、レポート作成者にとって悪夢です。つまり、どの列にどのデータが格納されているかを知るには、メタデータが必要です。そのメタデータがめちゃくちゃになったら、データを破壊する可能性があります。さらに、間違ったデータを間違った列に入れやすくなります。(「何ですか?String1には修道院の名前が含まれています。私はそれがチャリーシーンのお気に入りの薬だと思いました。」)

オプション3、4、5

IMO、要件2、3、および4は、EAVの変動を排除します。このデータに対してクエリ、並べ替え、または計算を行う必要がある場合、EAVはクトゥルフの夢であり、開発チームとDBAの悪夢です。EAVはパフォーマンスの点でボトルネックを作成し、必要な情報にすばやくアクセスするために必要なデータの整合性を提供しません。クエリはすぐにクロス集計Gordianノットに変わります。

オプション2,6

それには本当に1つの選択肢があります。仕様を収集してからスキーマを構築します。

格納したいデータに対して最高のパフォーマンスが必要な場合は、開発者と協力してニーズを理解し、できるだけ効率的に格納されるようにする必要があります。それでも、テーブルのスキーマに基づいてフォームを動的に構築するコードを使用して、他のテーブルとは別のテーブルに格納できます。列の拡張プロパティを許可するデータベースがある場合は、それらを使用してフォームビルダーが適切なラベルやツールチップなどを使用できるようにすることもできます。そのため、スキーマを追加するだけで済みます。どちらの方法でも、レポートを効率的に作成して実行するには、データを適切に保存する必要があります。問題のデータに多数のnullが含まれる場合、一部のデータベースにはそのタイプの情報を格納する機能があります。例えば、

これが、分析、フィルタリング、または並べ替えを行わないデータのバッグだけだった場合、EAVのいくつかのバリエーションでうまくいくと思います。ただし、要件を考えると、最も効率的な解決策は、これらの新しい列を別々のテーブルに格納し、それらのテーブルから動的にフォームを構築する場合でも、適切な仕様を取得することです。

スパース列


5
  1. データ型ごとに1つずつ、複数のUDFテーブルを作成します。したがって、UDFStrings、UDFDatesなどのテーブルがあります。おそらく、#2と同じように実行され、新しいフィールドが追加されるたびにビューを自動生成します。

私の研究によると、データ型に基づいた複数のテーブルはパフォーマンスに役立ちません。特に、50以上のUDFを持つ20Kまたは25Kレコードのようなバルクデータがある場合。パフォーマンスは最悪でした。

あなたは次のような複数の列を持つ単一のテーブルで行くべきです:

varchar Name
varchar Type
decimal NumberValue
varchar StringValue
date DateValue

これは正しく、賛成です。フィル・2011上の前の答えは、もはや今日2016良いアドバイスではありません
ヤップカイ倫レオン

SQLでこのようなプロセスを実行する方法の簡単な例を入手できますか?
Niroj

返信が遅れて申し訳ありませんが、同じデータベース構造が必要です。@Nirojが届きませんでした。何が欲しいのか、詳しく説明して頂けますか?
アミット請負業者

4

これは問題のある状況であり、どの解決策も「正しく」表示されません。ただし、オプション1は、単純さとパフォーマンスの両方の点でおそらく最良です。

これは、一部の商用エンタープライズアプリケーションで使用されるソリューションでもあります。

編集

現在利用できるが、質問が最初に尋ねられたときに存在しなかった(または少なくとも成熟していなかった)別のオプションは、DBでjsonフィールドを使用することです。

多くのリレーショナルDBは、jsonベースのフィールド(サブフィールドの動的リストを含めることができる)をサポートし、それらのクエリを可能にします

ポストグレス

mysql


1
おそらく何百もの未使用の列を作成するという考えは嫌です。SQLデータベースの設計について私が学んだり読んだりした内容に反しています。現在、1300以上の異なるユーザー定義値がありますが、それらの多くは、異なる名前が付けられた既存のアイテムの単なる複製です。
レイチェル

1つのテーブルに1300個の異なるUDFがあるか?各ユーザーにはUDFを追加するオプションがありますか、それとも特定の種類のパワーユーザーのみですか?
Ophir Yoktan、2011

インポートプロセスの一部です。マッピングされていないデータをユーザー定義フィールドに追加します。マップされていないデータを既存のUDFフィールドにマップするのに時間がかかることはないため、新しいフィールドを作成するだけで、長年にわたって多くのものが追加されてきました。
レイチェル

2

私は1、3、4の経験があり、データが何であるか明確でないか、データを動的なタイプのレコードに分類するためのある種のソフト分類で本当に複雑であるかのいずれかで、いずれも混乱します。

私はXMLを試してみたくなりますが、XMLの内容に対してスキーマを適用して、データ型指定などをチェックし、UDFデータの異なるセットを保持するのに役立つはずです。新しいバージョンのSQLサーバーでは、XMLフィールドにインデックスを付けることができます。これにより、パフォーマンスが向上します。(例:http://blogs.technet.com/b/josebda/archive/2009/03/23/sql-server-2008-xml-indexing.aspx)例


正直なところ、XMLについてはまったく調べていません。その主な欠点は、それがどのように機能し、どのようにクエリを実行するかを学ぶ必要があることです。パフォーマンスは他のオプションよりも劣る可能性があると聞きました
Rachel

1
これにはxmlの使用を避けます。これで十分です。過去にxmlでこれを実装しましたが、データ構造が大きくなり、コードの複雑さが高くなるため、パフォーマンスはかなり低下しました。
Kell

2

SQL Serverを使用している場合は、sqlvariant型を見落とさないでください。それはかなり速く、あなたの仕事をする必要があります。他のデータベースも同様のものがあるかもしれません。

XMLデータ型は、パフォーマンス上の理由からあまり良くありません。サーバーで計算を行う場合は、常にこれらを逆シリアル化する必要があります。

オプション1は見た目が悪く、見苦しいように見えますが、パフォーマンスに関しては最善の策です。パフォーマンスに勝てないため、以前にField00〜Field99という名前の列を持つテーブルを作成しました。INSERTのパフォーマンスも考慮する必要がある場合があります。その場合、これも対象になります。見栄えを良くしたい場合は、いつでもこのテーブルにビューを作成できます。


おかげで、SQLバリアントをもう一度見てみましょう。私の最大の懸念はパフォーマンスであり、それがどのように処理されるかはわかりません。特に、50milを超える行について話している場合
Rachel

sql_varientsはLIKE句と一緒に使用できないことがわかりました...これは私にとって大きな欠点です。もちろん、各UDFのビューを作成する場合、SQL_VARIANT_PROPERTY(value、 'BaseType')に基づいて適切なデータ型にビューをキャストできます...それでも、パフォーマンスにとっては悪いようです
Rachel

LIKEを使用できますが、最初に値をキャストする必要があります。LIKEはvarcharでのみ機能するため、sql_variantをvarcharにキャストする必要があります。UDFがvarcharであるかどうかがわかっている限り(たとえば、型がどこかに格納されているため)、すべての行をvarcharにフィルター処理してから、LIKEクエリをキャストして実行できます。select * FROM MyTable where variant_type = 'v' Cast(variant_value as varchar(max))LIKE 'Blah%'この方法では、intなどを文字列に変換しないため、速度が低下します。
Tim Rogers

特に数百万行の場合のパフォーマンスを確認するには、いくつかのテストを実行する必要があります。sql_varientsを使用したパフォーマンスに関するオンライン記事を知っていますか?特にキャストと非常に多数のレコードで?
Rachel


1

私はこれまで、これらのオプション(オプション6?:)を使用せずにこれを非常にうまく管理してきました。

ユーザーが使用するモデルを作成し(xmlとして保存し、カスタムモデリングツールを介して公開)、モデルで生成されたテーブルとビューからベーステーブルをユーザー定義のデータテーブルに結合します。したがって、各タイプには、コアデータを含むベーステーブルと、ユーザー定義フィールドを含むユーザーテーブルがあります。

例としてドキュメントを取り上げます。一般的なフィールドは、名前、タイプ、日付、作成者などです。これはコアテーブルに入ります。次に、contract_end_date、renewal_clause、blah blah blahなどの独自のフィールドを使用して、独自の特別なドキュメントタイプを定義します。そのユーザー定義のドキュメントの場合、コアドキュメントテーブルであるxcontractテーブルが共通の主キーで結合されます(したがって、xcontracts主キーもコアテーブルの主キーで外部になります)。次に、これらの2つのテーブルをラップするビューを生成します。クエリ時のパフォーマンスは高速でした。追加のビジネスルールをビューに埋め込むこともできます。これは私には本当にうまくいきました。


1

私たちのデータベースは、ユーザーが7000以上の「カスタムフィールド」を持っているSaaSアプリ(ヘルプデスクソフトウェア)を強化します。私たちは組み合わせたアプローチを使用します:

  1. (EntityID, FieldID, Value)データを検索するためのテーブル
  2. データの表示entitiesに使用される、すべてのエンティティ値を保持するテーブルのJSONフィールド。(これにより、値を取得するために100万個のJOINを必要としません)。

#1をさらに分割して、この回答が示すように「データ型ごとのテーブル」を作成し、UDFにインデックスを付けることもできます。

PS「Entity-Attribute-Value」アプローチを守るための単語のカップルは、誰もがバッシングし続けます。私たちは何十年もの間#2なしで#1を使用してきましたが、それはうまくいきました。時にはそれはビジネス上の決定です。アプリを書き直してデータベースを再設計する時間はありますか、または最近非常に安いクラウドサーバーに数ドルを投入することができますか?ちなみに、1番のアプローチを使用していたとき、DBは数百万のエンティティを保持し、数十万のユーザーがアクセスし、16GBのデュアルコアdbサーバーは問題なく動作していました


こんにちは@アレックス、私は同様の問題に遭遇しました。私はよく理解していれば、あなたは持っている:1)custom_fieldsを格納するテーブルの値のような1 => last_concert_year、2 => band、3 => musicその後、custom_fields_values値001、1、1976 002、1、1977 003、2、とテーブルIron Maiden003、3 、Metal この例があなたにとって意味のあるものであり、書式設定について申し訳ありません!
チタミ2018年

@thitami正確ではありません。あなたの例を以下に示します。私が持っているbands行を持つテーブルを1,'Iron Maiden'、その後custom_fieldsの行で1,'concert_year' | 2,'music'、その後custom_fields_valuesの行で1,1,'1977'|1,2,'metal'
アレックス・

0

コメントで、UDFフィールドは、ユーザーによって適切にマッピングされていないインポートされたデータをダンプするためのものであると述べたのを見ました。

おそらく別のオプションは、各ユーザーが作成したUDFの数を追跡し、6(または他の同等にランダムな制限)カスタムフィールドトップを使用できると言って、フィールドを再利用するように強制することです。

このようなデータベースの構造化の問題に直面した場合、多くの場合、アプリケーション(基本的にはインポートシステム)の基本設計に戻って、いくつかの制限を加えるのが最善です。

ここで私が行うことは、ユーザーへのリンクを追加したオプション4(編集)です。

general_data_table
id
...


udfs_linked_table
id
general_data_id
udf_id


udfs_table
id
name
type
owner_id --> Use this to filter for the current user and limit their UDFs
string_link_id --> link table for string fields
int_link_id
type_link_id

次に、パフォーマンスを最適化し、インデックスを正しく表示するビューを作成してください。このレベルの正規化により、DBのフットプリントは小さくなりますが、アプリケーションはより複雑になります。


0

このタイプのシステムは、非常に認定されたeコマースCMSプラットフォームであるMagentoで使用されていたため、#4をお勧めします。1つのテーブルを使用して、fieldIdラベル列を使用してカスタムフィールドを定義します。次に、データ型ごとに個別のテーブルを用意し、それらの各テーブル内に、fieldIdとデータ型の値の列でインデックスを付けるインデックスを作成します。次に、クエリで次のようなものを使用します。

SELECT *
FROM FieldValues_Text
WHERE fieldId IN (
    SELECT fieldId FROM Fields WHERE userId=@userId
)
AND value LIKE '%' + @search + '%'

これは、私の考えでは、ユーザー定義型の可能な最高のパフォーマンスを保証します。

私の経験では、月に数百万のユーザーにサービスを提供し、カスタム製品の属性を持つ何千もの製品をホストし、データベースがレポート作成のためにも簡単にワークロードを処理するいくつかのMagento Webサイトに取り組んできました。

レポートの場合、を使用PIVOTしてフィールドテーブルのラベル値を列名に変換し、クエリ結果を各データタイプテーブルからピボットされた列にピボットできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.