27

ファイルのすべてのメタデータ (つまり、ファイル名、作成者、タイトル、作成日) とカスタム メタデータ (ユーザーによってファイルに追加された、CustUseBy、CustSendBy など) を格納するテーブルを設計する必要があります。カスタム メタデータ フィールドの数を事前に設定することはできません。実際、ファイルに追加されたカスタム タグの種類と数を特定する唯一の方法は、テーブルに存在するものを調べることです。

これを格納するために、ベース テーブル (ファイルのすべての共通メタデータを含む)、Attributesテーブル (ファイルに設定できる追加のオプションの属性を保持する)、およびFileAttributesテーブル (ファイルの属性に値を割り当てる) を作成しました。

CREAT TABLE FileBase (
    id VARCHAR(32) PRIMARY KEY,
    name VARCHAR(255) UNIQUE NOT NULL,
    title VARCHAR(255),
    author VARCHAR(255),
    created DATETIME NOT NULL,
) Engine=InnoDB;

CREATE TABLE Attributes (
    id VARCHAR(32) PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    type VARCHAR(255) NOT NULL
) Engine=InnoDB;

CREATE TABLE FileAttributes (
    sNo INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    fileId VARCHAR(32) NOT NULL,
    attributeId VARCHAR(32) NOT NULL,
    attributeValue VARCHAR(255) NOT NULL,
    FOREIGN KEY fileId REFERENCES FileBase (id),
    FOREIGN KEY attributeId REFERENCES Attributes (id)
 ) Engine=InnoDB;

サンプルデータ:

INSERT INTO FileBase
(id,      title,  author,  name,        created)
  VALUES
('F001', 'Dox',   'vinay', 'story.dox', '2009/01/02 15:04:05'),
('F002', 'Excel', 'Ajay',  'data.xls',  '2009/02/03 01:02:03');

INSERT INTO Attributes
(id,      name,            type)
  VALUES
('A001', 'CustomeAttt1',  'Varchar(40)'),
('A002', 'CustomUseDate', 'Datetime');

INSERT INTO FileAttributes 
(fileId, attributeId, attributeValue)
  VALUES
('F001', 'A001',      'Akash'),
('F001', 'A002',      '2009/03/02');

今問題は、次のような方法でデータを表示したいということです:

FileId, Title, Author, CustomAttri1, CustomAttr2, ...
F001    Dox    vinay   Akash         2009/03/02   ...
F002    Excel  Ajay     

この結果を生成するクエリは何ですか?

4

7 に答える 7

21

質問は MySQL に言及しており、実際、この DBMS にはこの種の問題に対する特別な機能がありますGROUP_CONCAT(expr)group-by-functions に関する MySQL リファレンス マニュアルを参照してください。この機能は、MySQL バージョン 4.1 で追加されました。クエリで使用GROUP BY FileIDします。

結果をどのように表示したいかについてはよくわかりません。すべてのアイテムにすべての属性をリストする場合 (設定されていなくても)、それは難しくなります。ただし、これを行う方法についての私の提案は次のとおりです。

SELECT bt.FileID, Title, Author, 
 GROUP_CONCAT(
  CONCAT_WS(':', at.AttributeName, at.AttributeType, avt.AttributeValue) 
  ORDER BY at.AttributeName SEPARATOR ', ') 
FROM BaseTable bt JOIN AttributeValueTable avt ON avt.FileID=bt.FileID 
 JOIN AttributeTable at ON avt.AttributeId=at.AttributeId 
GROUP BY bt.FileID;

これにより、すべての属性が同じ順序で表示されるので便利です。出力は次のようになります。

'F001', 'Dox', 'vinay', 'CustomAttr1:varchar(40):Akash, CustomUseDate:Datetime:2009/03/02'

この方法では、必要な DB クエリは 1 つだけで、出力は簡単に解析できます。属性を実際の Datetime などとして DB に保存する場合は、動的 SQL を使用する必要がありますが、私はそれを避けて、値を varchars に保存します。

于 2009-03-17T01:33:07.283 に答える
10

group-concat の結果よりも使いやすい (そして結合可能な) 何かを探している場合は、以下の解決策を試してください。これを理解するために、あなたの例に非常によく似たテーブルをいくつか作成しました。

これは次の場合に機能します。

  • 純粋な SQL ソリューションが必要な場合 (コードなし、ループなし)
  • 予測可能な一連の属性がある (例: 動的ではない)
  • 新しい属性タイプを追加する必要がある場合、クエリを更新しても問題ありません
  • 副選択として JOIN したり、UNION したり、ネストしたりできる結果を好む

表 A (ファイル)

FileID, Title, Author, CreatedOn

表 B (属性)

AttrID, AttrName, AttrType [not sure how you use type...]

表 C (Files_Attributes)

FileID, AttrID, AttrValue

従来のクエリでは、多くの冗長な行がプルされます。

SELECT * FROM 
Files F 
LEFT JOIN Files_Attributes FA USING (FileID)
LEFT JOIN Attributes A USING (AttributeID);
AttrID FileID Title Author CreatedOn AttrValue AttrName AttrType
50 1 TestFile Joe 2011-01-01 true 読み取り専用 bool
60 1 TestFile ジョー 2011-01-01 xls FileFormat テキスト
70 1 TestFile ジョー 2011-01-01 false プライベート ブール
80 1 TestFile ジョー 2011-01-01 2011-10-03 LastModified date
60 2 LongNovel Mary 2011-02-01 json FileFormat テキスト
80 2 LongNovel メアリー 2011-02-01 2011-10-04 LastModified date
70 2 LongNovel Mary 2011-02-01 true プライベート bool
50 2 LongNovel Mary 2011-02-01 true ReadOnly bool
50 3 ShortStory Susan 2011-03-01 false ReadOnly bool
60 3 ShortStory Susan 2011-03-01 ascii FileFormat テキスト
70 3 ShortStory スーザン 2011-03-01 false Private bool
80 3 ShortStory スーザン 2011-03-01 2011-10-01 LastModified date
50 4 ProfitLoss Bill 2011-04-01 false ReadOnly bool
70 4 ProfitLoss Bill 2011-04-01 true プライベート bool
80 4 ProfitLoss Bill 2011-04-01 2011-10-02 LastModified date
60 4 ProfitLoss Bill 2011-04-01 text FileFormat text
50 5 MonthlyBudget ジョージ 2011-05-01 false ReadOnly bool
60 5 MonthlyBudget ジョージ 2011-05-01 バイナリ ファイル形式 テキスト
70 5 MonthlyBudget ジョージ 2011-05-01 false プライベート bool
80 5 MonthlyBudget ジョージ 2011-05-01 2011-10-20 LastModified date

この合体クエリ (MAX を使用したアプローチ) は、行をマージできます。

SELECT
F.*,
MAX( IF(A.AttrName = 'ReadOnly', FA.AttrValue, NULL) ) as 'ReadOnly',
MAX( IF(A.AttrName = 'FileFormat', FA.AttrValue, NULL) ) as 'FileFormat',
MAX( IF(A.AttrName = 'Private', FA.AttrValue, NULL) ) as 'Private',
MAX( IF(A.AttrName = 'LastModified', FA.AttrValue, NULL) ) as 'LastModified'
FROM 
Files F 
LEFT JOIN Files_Attributes FA USING (FileID)
LEFT JOIN Attributes A USING (AttributeID)
GROUP BY
F.FileID;
FileID タイトル 作成者 CreatedOn ReadOnly FileFormat Private LastModified
1 TestFile Joe 2011-01-01 true xls false 2011-10-03
2 LongNovel Mary 2011-02-01 true json true 2011-10-04
3 ShortStory Susan 2011-03-01 false ascii false 2011-10-01
4 ProfitLoss Bill 2011-04-01 false text true 2011-10-02
5 MonthlyBudget ジョージ 2011-05-01 false binary false 2011-10-20
于 2011-10-27T19:00:18.610 に答える
9

このようなクエリの一般的な形式は次のようになります。

SELECT file.*,
   attr1.value AS 'Attribute 1 Name', 
   attr2.value AS 'Attribute 2 Name', 
   ...
FROM
   file 
   LEFT JOIN attr AS attr1 
      ON(file.FileId=attr1.FileId and attr1.AttributeId=1)
   LEFT JOIN attr AS attr2 
      ON(file.FileId=attr2.FileId and attr2.AttributeId=2)
   ...

したがって、必要な属性からクエリを動的に構築する必要があります。PHPっぽい疑似コードで

$cols="file";
$joins="";

$rows=$db->GetAll("select * from Attributes");
foreach($rows as $idx=>$row)
{
   $alias="attr{$idx}";
   $cols.=", {$alias}.value as '".mysql_escape_string($row['AttributeName'])."'";   
   $joins.="LEFT JOIN attr as {$alias} on ".
       "(file.FileId={$alias}.FileId and ".
       "{$alias}.AttributeId={$row['AttributeId']}) ";
}

 $pivotsql="select $cols from file $joins";
于 2009-03-16T10:23:39.933 に答える
6

これは、SQLの標準的な「行から列へ」の問題です。

これはSQLの外部で最も簡単に実行できます。

アプリケーションで、次の手順を実行します。

  1. ファイル、システム属性、およびユーザー属性のコレクションを含む単純なクラスを定義します。この顧客属性のコレクションには、リストが適しています。このクラスをFileDescriptionと呼びましょう。

  2. ファイルとファイルのすべての顧客属性の間で単純な結合を実行します。

  3. クエリ結果からFileDescriptionsをアセンブルするループを記述します。

    • 最初の行を取得し、FileDescriptionを作成して、最初の顧客属性を設定します。

    • フェッチする行はまだありますが、次のようになります。

      • 行をフェッチします
      • この行のファイル名が作成中のFileDescriptionと一致しない場合は、次のようにします。FileDescriptionの作成を終了します。これをファイル記述の結果コレクションに追加します。指定された名前と最初の顧客属性を使用して、新しい空のFileDescriptionを作成します。
      • この行のファイル名が作成中のFileDescriptionと一致する場合:現在のFileDescriptionに別の顧客属性を追加します
于 2009-03-16T10:10:23.443 に答える
4

私はさまざまな答えを試してきましたが、メタイの答えが私にとって最も便利でした。私の現在のプロジェクトは、MySQL で Doctrine を使用していますが、かなりの数のルーズ テーブルがあります。

以下は、Methai のソリューションに関する私の経験の結果です。

エンティティ テーブルを作成する

DROP TABLE IF EXISTS entity;
CREATE TABLE entity (
    id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    title VARCHAR(255),
    author VARCHAR(255),
    createdOn DATETIME NOT NULL
) Engine = InnoDB;

属性テーブルを作成する

DROP TABLE IF EXISTS attribute;
CREATE TABLE attribute (
    id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL,
    type VARCHAR(255) NOT NULL
) Engine = InnoDB;

属性値テーブルを作成する

DROP TABLE IF EXISTS attributevalue;
CREATE TABLE attributevalue (
    id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    value VARCHAR(255) NOT NULL,
    attribute_id INT UNSIGNED NOT NULL,
    FOREIGN KEY(attribute_id) REFERENCES attribute(id)
 ) Engine = InnoDB;

entity_attributevalue 結合テーブルを作成する

DROP TABLE IF EXISTS entity_attributevalue;
CREATE TABLE entity_attributevalue (
    entity_id INT UNSIGNED NOT NULL,
    attributevalue_id INT UNSIGNED NOT NULL,
    FOREIGN KEY(entity_id) REFERENCES entity(id),
    FOREIGN KEY(attributevalue_id) REFERENCES attributevalue(id)
) Engine = InnoDB;

エンティティ テーブルに入力する

INSERT INTO entity
    (title, author, createdOn)
VALUES
    ('TestFile', 'Joe', '2011-01-01'),
    ('LongNovel', 'Mary', '2011-02-01'),
    ('ShortStory', 'Susan', '2011-03-01'),
    ('ProfitLoss', 'Bill', '2011-04-01'),
    ('MonthlyBudget', 'George', '2011-05-01'),
    ('Paper', 'Jane', '2012-04-01'),
    ('Essay', 'John', '2012-03-01'),
    ('Article', 'Dan', '2012-12-01');

属性テーブルへの入力

INSERT INTO attribute
    (name, type)
VALUES
    ('ReadOnly', 'bool'),
    ('FileFormat', 'text'),
    ('Private', 'bool'),
    ('LastModified', 'date');

属性値テーブルに入力する

INSERT INTO attributevalue 
    (value, attribute_id)
VALUES
    ('true', '1'),
    ('xls', '2'),
    ('false', '3'),
    ('2011-10-03', '4'),
    ('true', '1'),
    ('json', '2'),
    ('true', '3'),
    ('2011-10-04', '4'),
    ('false', '1'),
    ('ascii', '2'),
    ('false', '3'),
    ('2011-10-01', '4'),
    ('false', '1'),
    ('text', '2'),
    ('true', '3'),
    ('2011-10-02', '4'),
    ('false', '1'),
    ('binary', '2'),
    ('false', '3'),
    ('2011-10-20', '4'),
    ('doc', '2'),
    ('false', '3'),
    ('2011-10-20', '4'),
    ('rtf', '2'),
    ('2011-10-20', '4');

entity_attributevalue テーブルに入力する

INSERT INTO entity_attributevalue 
    (entity_id, attributevalue_id)
VALUES
    ('1', '1'),
    ('1', '2'),
    ('1', '3'),
    ('1', '4'),
    ('2', '5'),
    ('2', '6'),
    ('2', '7'),
    ('2', '8'),
    ('3', '9'),
    ('3', '10'),
    ('3', '11'),
    ('3', '12'),
    ('4', '13'),
    ('4', '14'),
    ('4', '15'),
    ('4', '16'),
    ('5', '17'),
    ('5', '18'),
    ('5', '19'),
    ('5', '20'),
    ('6', '21'),
    ('6', '22'),
    ('6', '23'),
    ('7', '24'),
    ('7', '25');

すべてのレコードを表示

SELECT * 
FROM `entity` e
LEFT JOIN `entity_attributevalue` ea ON ea.entity_id = e.id
LEFT JOIN `attributevalue` av ON ea.attributevalue_id = av.id
LEFT JOIN `attribute` a ON av.attribute_id = a.id;
ID タイトル 作成者 createdOn entity_id attributevalue_id ID 値 attribute_id ID 名前 タイプ
1 TestFile Joe 2011-01-01 00:00:00 1 1 1 true 1 1 ReadOnly ブール
1 TestFile Joe 2011-01-01 00:00:00 1 2 2 xls 2 2 FileFormat テキスト
1 TestFile Joe 2011-01-01 00:00:00 1 3 3 false 3 3 プライベート ブール
1 TestFile Joe 2011-01-01 00:00:00 1 4 4 2011-10-03 4 4 LastModified 日付
2 LongNovel Mary 2011-02-01 00:00:00 2 5 5 true 1 1 ReadOnly ブール
2 LongNovel Mary 2011-02-01 00:00:00 2 6 6 json 2 2 FileFormat テキスト
2 LongNovel Mary 2011-02-01 00:00:00 2 7 7 true 3 3 Private bool
2 LongNovel Mary 2011-02-01 00:00:00 2 8 8 2011-10-04 4 4 最終更新日
3 ShortStory スーザン 2011-03-01 00:00:00 3 9 9 false 1 1 ReadOnly bool
3 ShortStory Susan 2011-03-01 00:00:00 3 10 10 ascii 2 2 FileFormat テキスト
3 ShortStory スーザン 2011-03-01 00:00:00 3 11 11 false 3 3 Private bool
3 ShortStory Susan 2011-03-01 00:00:00 3 12 12 2011-10-01 4 4 最終更新日
4 ProfitLoss Bill 2011-04-01 00:00:00 4 13 13 false 1 1 ReadOnly bool
4 ProfitLoss Bill 2011-04-01 00:00:00 4 14 14 テキスト 2 2 FileFormat テキスト
4 ProfitLoss Bill 2011-04-01 00:00:00 4 15 15 true 3 3 プライベート bool
4 ProfitLoss Bill 2011-04-01 00:00:00 4 16 16 2011-10-02 4 4 LastModified date
5 MonthlyBudget ジョージ 2011-05-01 00:00:00 5 17 17 false 1 1 ReadOnly bool
5 MonthlyBudget ジョージ 2011-05-01 00:00:00 5 18 18 バイナリ 2 2 FileFormat テキスト
5 MonthlyBudget ジョージ 2011-05-01 00:00:00 5 19 19 false 3 3 Private bool
5 MonthlyBudget ジョージ 2011-05-01 00:00:00 5 20 20 2011-10-20 4 4 LastModified date
6 Paper Jane 2012-04-01 00:00:00 6 21 21 バイナリ 2 2 FileFormat テキスト
6 Paper Jane 2012-04-01 00:00:00 6 22 22 false 3 3 Private bool
6 紙ジェーン 2012-04-01 00:00:00 6 23 23 2011-10-20 4 4 LastModified date
7 エッセイ ジョン 2012-03-01 00:00:00 7 24 24 バイナリ 2 2 FileFormat テキスト
7 エッセイ ジョン 2012-03-01 00:00:00 7 25 25 2011-10-20 4 4 LastModified date
8 Article Dan 2012-12-01 00:00:00 NULL NULL NULL NULL NULL NULL NULL NULL

ピボットテーブル

SELECT e.*,
    MAX( IF(a.name = 'ReadOnly', av.value, NULL) ) as 'ReadOnly',
    MAX( IF(a.name = 'FileFormat', av.value, NULL) ) as 'FileFormat',
    MAX( IF(a.name = 'Private', av.value, NULL) ) as 'Private',
    MAX( IF(a.name = 'LastModified', av.value, NULL) ) as 'LastModified'
FROM `entity` e
LEFT JOIN `entity_attributevalue` ea ON ea.entity_id = e.id
LEFT JOIN `attributevalue` av ON ea.attributevalue_id = av.id
LEFT JOIN `attribute` a ON av.attribute_id = a.id
GROUP BY e.id;
id title author createdOn ReadOnly FileFormat Private LastModified
1 TestFile Joe 2011-01-01 00:00:00 true xls false 2011-10-03
2 LongNovel Mary 2011-02-01 00:00:00 true json true 2011-10-04
3 ShortStory Susan 2011-03-01 00:00:00 false ascii false 2011-10-01
4 ProfitLoss Bill 2011-04-01 00:00:00 false テキスト true 2011-10-02
5 MonthlyBudget ジョージ 2011-05-01 00:00:00 false binary false 2011-10-20
6 紙ジェーン 2012-04-01 00:00:00 NULL バイナリ false 2011-10-20
7 エッセイ ジョン 2012-03-01 00:00:00 NULL バイナリ NULL 2011-10-20
8 Article Dan 2012-12-01 00:00:00 NULL NULL NULL NULL
于 2013-02-28T16:53:35.427 に答える
0

ただし、行を列として使用する、つまりデータを転置するソリューションがあります。純粋な SQL で実行するにはクエリのトリックが必要です。または、ピボット テーブル (またはクロス テーブル) を使用して、特定のデータベースでのみ使用できる特定の機能に依存する必要があります。

例として、Oracle (11g) でこれを行う方法を確認できます。

プログラミング バージョンは、保守と作成がより簡単になり、さらに、任意のデータベースで動作します。

于 2009-03-16T10:34:19.503 に答える
-2

私はMySQLを知らないので部分的な答えです(よく)。MSSQLでは、ピボットテーブルを確認するか、ストアドプロシージャに一時テーブルを作成します。大変かもしれません...

于 2009-03-16T10:08:39.627 に答える