リレーショナル理論でプログラミング
データベース実践講義 ―エンジニアのためのリレーショナル理論 (THEORY/IN/PRACTICE)をデータベースではなくC#とLINQを使って勉強してみる。(^^;
すべての関係には、見出し(heading)と本体(body)がある。見出しとは、属性の集合であり(ここでいう属性は、属性名と型名の組みである)、本体とは、その見出しに準拠するタプルの集合である。
上記は関係の説明ですが、プログラミング言語で表現すると型名はそのまま型、属性名はフィールド名なので、属性の集合は複数のフィールドを持った型(クラス)になります。これが見出しとなりますが、C#のクラス的には見出しとタプルでは、同じフィールドを持つので、区別するべきかどうか悩むところ。だた、見出しはインスタンスを持つことがないと思われるので、抽象クラスとして表現し、そこからタプルを派生させます。最後に本体ですが、これはタプルのコレクションで良いでしょう。(注意:単なる私の独自解釈です)
では、これをコードにすると、
using System; using System.Collections.Generic; using System.Reflection; public class Relation <Tuple> { public Relation () { body = new List <Tuple> (); } public Relation (IEnumerable <Tuple> e) { body = new List <Tuple> (e); } List <Tuple> body; public List <Tuple> Body { get { return body; } } // 次数 public int Degree { get { return typeof (Tuple).GetProperties (BindingFlags.Public | BindingFlags.Instance).Length; } } // 濃度 public int Cardinality { get { return body.Count; } } } public class S : Relation <S.Tuple> { public S () {} public S (IEnumerable <Tuple> e) : base (e) {} // 見出し public abstract class Heading { protected SNo sno; public SNo SNo { get { return sno; } set { sno = value; } } protected Name sname; public Name SName { get { return sname; } set { sname = value; } } protected int status; public int Status { get { return status; } set { status = value; } } protected string city; public string City { get { return city; } set { city = value; } } } // タプル public class Tuple : Heading { public Tuple () {} public Tuple (SNo sno, Name sname, int status, string city) { this.sno = sno; this.sname = sname; this.status = status; this.city = city; } } }
長くなるので関係Sのみを紹介。関係P,SPも内容は一緒です。このクラスを使ってデータを作成して、LINQから操作してみます。
using System; using System.Collections.Generic; using System.Query; public class Program { static S CreateS () { return new S (new List <S.Tuple> { new S.Tuple { SNo = new SNo ("S1"), SName = new Name ("Smith"), Status = 20, City = "London" }, new S.Tuple { SNo = new SNo ("S2"), SName = new Name ("Jones"), Status = 10, City = "Paris" }, new S.Tuple { SNo = new SNo ("S3"), SName = new Name ("Blake"), Status = 30, City = "Paris" }, new S.Tuple { SNo = new SNo ("S4"), SName = new Name ("Clark"), Status = 20, City = "London" }, new S.Tuple { SNo = new SNo ("S5"), SName = new Name ("Adams"), Status = 30, City = "Athens" } }); } static P CreateP () { return new P (new List <P.Tuple> { new P.Tuple { PNo = new PNo ("P1"), PName = new Name ("Nut" ), Color = new Color ("Red" ), Weight = new Weight (12.0), City = "London" }, new P.Tuple { PNo = new PNo ("P2"), PName = new Name ("Bolt" ), Color = new Color ("Green"), Weight = new Weight (17.0), City = "Paris" }, new P.Tuple { PNo = new PNo ("P3"), PName = new Name ("Screw"), Color = new Color ("Blue" ), Weight = new Weight (17.0), City = "Oslo" }, new P.Tuple { PNo = new PNo ("P4"), PName = new Name ("Screw"), Color = new Color ("Red" ), Weight = new Weight (14.0), City = "London" }, new P.Tuple { PNo = new PNo ("P5"), PName = new Name ("Cam" ), Color = new Color ("Blue" ), Weight = new Weight (12.0), City = "Paris" }, new P.Tuple { PNo = new PNo ("P6"), PName = new Name ("Cog" ), Color = new Color ("Red" ), Weight = new Weight (19.0), City = "London" } }); } static SP CreateSP () { return new SP (new List <SP.Tuple> { new SP.Tuple { SNo = new SNo ("S1"), PNo = new PNo ("P1"), Qty = new Qty (300) }, new SP.Tuple { SNo = new SNo ("S1"), PNo = new PNo ("P2"), Qty = new Qty (200) }, new SP.Tuple { SNo = new SNo ("S1"), PNo = new PNo ("P3"), Qty = new Qty (400) }, new SP.Tuple { SNo = new SNo ("S1"), PNo = new PNo ("P4"), Qty = new Qty (200) }, new SP.Tuple { SNo = new SNo ("S1"), PNo = new PNo ("P5"), Qty = new Qty (100) }, new SP.Tuple { SNo = new SNo ("S1"), PNo = new PNo ("P6"), Qty = new Qty (100) }, new SP.Tuple { SNo = new SNo ("S2"), PNo = new PNo ("P1"), Qty = new Qty (300) }, new SP.Tuple { SNo = new SNo ("S2"), PNo = new PNo ("P2"), Qty = new Qty (400) }, new SP.Tuple { SNo = new SNo ("S3"), PNo = new PNo ("P2"), Qty = new Qty (200) }, new SP.Tuple { SNo = new SNo ("S4"), PNo = new PNo ("P2"), Qty = new Qty (200) }, new SP.Tuple { SNo = new SNo ("S4"), PNo = new PNo ("P4"), Qty = new Qty (300) }, new SP.Tuple { SNo = new SNo ("S4"), PNo = new PNo ("P5"), Qty = new Qty (400) } }); } public static void Main () { var s = CreateS (); var p = CreateP (); var sp = CreateSP (); Console.WriteLine ("■制限"); var r1 = from x in s.Body where x.City == "Paris" select x; foreach (var r in r1) Console.WriteLine ("{0,-2}, {1,-5}, {2,2}, {3}", r.SNo, r.SName, r.Status, r.City); Console.WriteLine ("■射影"); var r2 = from x in s.Body select new {x.SName, x.City, x.Status}; foreach (var r in r2) Console.WriteLine ("{0,-5}, {1,-6}, {2}", r.SName, r.City, r.Status); Console.WriteLine ("■結合"); var r3 = from x in p.Body, y in s.Body where x.City == y.City select new {x.PNo, x.PName, x.Color, x.Weight, x.City, y.SNo, y.SName, y.Status}; foreach (var r in r3) Console.WriteLine ("{0}, {1,-5}, {2,-5}, {3:f1}, {4,-6}, {5}, {6,-5}, {7}", r.PNo, r.PName, r.Color, r.Weight.Value, r.City, r.SNo, r.SName, r.Status); } } /* ■制限 S2, Jones, 10, Paris S3, Blake, 30, Paris ■射影 Smith, London, 20 Jones, Paris , 10 Blake, Paris , 30 Clark, London, 20 Adams, Athens, 30 ■結合 P1, Nut , Red , 12.0, London, S1, Smith, 20 P1, Nut , Red , 12.0, London, S4, Clark, 20 P2, Bolt , Green, 17.0, Paris , S2, Jones, 10 P2, Bolt , Green, 17.0, Paris , S3, Blake, 30 P4, Screw, Red , 14.0, London, S1, Smith, 20 P4, Screw, Red , 14.0, London, S4, Clark, 20 P5, Cam , Blue , 12.0, Paris , S2, Jones, 10 P5, Cam , Blue , 12.0, Paris , S3, Blake, 30 P6, Cog , Red , 19.0, London, S1, Smith, 20 P6, Cog , Red , 19.0, London, S4, Clark, 20 */
交わり(intersect)が実行時エラーになるのでここまで。結論としては、餅は餅屋。リレーショナルモデルをオブジェクト指向プログラミング言語で扱うのは、まだまだ辛いです。(^^;