web-dev-qa-db-ja.com

mysqlで生成された列としてのサブクエリ?

テーブルBの列とテーブルAの行のtableA_idを合計する、生成された列をテーブルAに作成できますか?

家族のテーブルと子供たちのテーブルがあるとします。家族ごとに子供の年齢の合計が欲しいです。

ALTER TABLE people.families 
ADD COLUMN sumofages DECIMAL(10,2) GENERATED ALWAYS AS 
(SELECT SUM(age) FROM people.children WHERE family_id = people.families.id) STORED;

ERROR 3102: Expression of generated column 'sumofages' contains a disallowed function.

タイプVIRTUALとしても保存できません。私はここで何が間違っているのですか?

ALTER TABLE people.families 
ADD COLUMN sumofages DECIMAL(10,2) GENERATED ALWAYS AS 
(SELECT SUM(age) FROM people.children WHERE family_id = people.families.id) VIRTUAL;

ERROR 3102: Expression of generated column 'sumofages' contains a disallowed function.

どの機能が許可されていないのかわかりません。 SUMはそうではないようです。多分SELECT?

10
user1955162

https://dev.mysql.com/doc/refman/5.7/en/create-table-generated-columns.html

生成される列式は、次の規則に従う必要があります。式に許可されていない構造が含まれていると、エラーが発生します。

  • サブクエリ、パラメータ、変数、保存された関数、およびユーザー定義関数は許可されていません。

生成された列の式が、同じ行内のonly列を参照できることは合理的です。生成された列は、サブクエリを使用したり、他のテーブルを参照したり、非決定論的な出力を持つ関数を参照したりすることはできません。

生成された列がクロステーブル参照をサポートしていたとします。特に、STOREDで生成された列の場合を考えてみてください。

テーブルを更新する場合、MySQLは、更新した行を参照している場合、データベースの他の場所で生成された列の参照も更新する必要があります。 MySQLがこれらすべての参照を追跡することは、複雑で費用がかかります。

次に、格納された関数を介して間接参照を追加することを検討してください。

次に、更新がトランザクション内のInnoDBテーブルに対するものであるが、生成された列が非トランザクション(MyISAM、MEMORY、ARCHIVEなど)テーブルにある可能性があることを考慮してください。更新を行うときに、生成された列に更新を反映する必要がありますか?ロールバックするとどうなりますか?コミット時に更新を反映する必要がありますか?それでは、MySQLはこれらのテーブルに適用するためにどのように変更を「キューに入れる」必要がありますか?複数のトランザクションが、生成された列参照に影響する更新をコミットした場合はどうなりますか?最後に変更を適用したものと最後にコミットしたもののどちらが勝つ必要がありますか?

これらの理由から、生成された列が同じテーブルの同じ行の列以外のものを参照できるようにすることは実用的でも効率的でもありません。

13
Bill Karwin

計算列の考え方は、レコード内の他の列からデータを取得することです。国コードと郵便番号を組み合わせると、DEと12345を保存し、住所で使用できるDE-12345を取得できます。

しかし、あなたがやろうとしていることは、まったく異なるものです。別のテーブルのデータにアクセスしています。ただし、そのテーブルのデータは変更される可能性があるため、同じレコードにアクセスすると、突然まったく異なる結果が得られる可能性があります。計算列には決定論的な値が含まれている必要があるため、レコードのデータが変更されない限り、それらは変更されません。この点でMySQLについてはわかりませんが、サブクエリなどの非決定論的データはおそらく禁止されています。

あなたが実際に探しているのはビューです。ビューは、必要に応じて、異なるテーブルからの選択を組み合わせることができます。だから使用する

create view familydata as
(
  select f.*, sum(c.age) as sumofages
  from families f
  join children c on c.family_id = f.id
  group by f.id
);

または

create view familydata as
(
  select f.*,
  (
    select sum(age)
    from children c
    where c.family_id = f.id
  ) as sumofages
  from families f
);

構文が正しいことを願っています。

5