1

特定の製品の対象となるすべてのメンバーとその家族を基本的に検索するクエリがあります。同じファミリーに属していても、各ファミリー メンバーは個別の行に表示されます。注目のMySQLを使用しています。

例 (実際に返されたすべてのデータの小さなサンプル):

ID  | familyid |  firstname | lastname | address      | city      | relationship
==============================================================================
1   |    1     |   john     |  davis   | 942 James Ln | cityplace | primary
2   |    1     |   suzy     |  davis   | 942 James Ln | cityplace | spouse
3   |    2     |   andrew   |  smith   | 444 A Rd     | new york  | primary
4   |    3     |   Mike     |  lewis   | 123 Street   | dallas    | primary
5   |    3     |   Donna    |  lewis   | 123 Street   | dallas    | child
1   |    3     |   Jamie    |  lewis   | 123 Street   | dallas    | child

上の表の (作成された) データは、次の 2 つの表から取得されています。

  1. 一次情報 (メンバーと住所に関するすべての一般情報が含まれます)
  2. 家族情報(配偶者と子供の名前と性別情報を含む)

簡単にするために、これらのテーブルに含まれる情報は次のとおりであると仮定します。

メンバー

ID | familyid | firstname | lastname | address | city
-----------------------------------------------------

家族

ID | familyid | firstname | lastname | type | gender
------------------------------------------------------

私たちのシステムのメンバーは、選択した複数の製品を持つことができます (情報は別の表にあります)。残念ながら、上記のようなステートメントを構築する唯一の方法は、基本的に同じ情報を 2 回選択してから、次のサンプル ステートメントのように UNION ALL ステートメントを実行することです。

SELECT r.* FROM (
  SELECT familyid, firstname, lastname, address, city, 'primary'
  FROM member
 UNION ALL
  SELECT a.familyid, b.firstname, b.lastname, a.address, a.city, IF(b.type = 1, 'spouse', 'child')
  FROM member a
  JOIN family b ON (a.familyid = b.familyid)
) r

これはすでに少し混乱していますが、すでに収集されている「名前」情報に他のいくつかのテーブルを結合し始めると、「本当の」問題が発生します。たとえば、クエリ全体に結合ステートメントを追加して、HBO テレビ サービスを選択したメンバーまたは家族のみを表示できます。

after last statement: JOIN channels c ON (r.familyid = c.familyid)

これにより、以前に選択したステートメントで HBO を持っていないすべてのメンバーが除外されますが、HBO のサブセクション (メンバーのみのバージョンと家族全員のバージョンを持っている人など) を選択したい場合はどうなりますか? これらの後続のクエリでは、基本的にまったく同じ呼び出しを 2 回行う必要があり、わずかに異なる情報しか返されません。

SELECT r.* FROM (
  SELECT familyid, firstname, lastname, address, city, 'primary'
  FROM member m
  JOIN channels c ON (m.familyid = c.familyid)
  WHERE c.name = "HBO" and c.memtype = "member only"
 UNION ALL
  SELECT a.familyid, b.firstname, b.lastname, a.address, a.city, IF(b.type = 1, 'spouse', 'child')
  FROM member a
  JOIN family b ON (a.familyid = b.familyid)
  JOIN channels c ON (b.familyid = c.familyid)
  WHERE c.name = "HBO" and c.memtype = "family"
) r

これらのステートメントをより適切に最適化する方法はありますか? この件に関して、MySQL は単なる SOL ですか?

注: 表はそのまま正規化されているため、改善を提案しないでください。この特定のクエリは、1 か月に 1 回、必要に応じて (非常にまれに) 自動的に生成される 1 つのレポート用です。スキーマをそのまま使用して Union ステートメントを回避できるかどうかを調べようとしているだけです。スキーマは変更できません。

4

3 に答える 3

2

コメントで述べたように、私の意見では、以下のようなより柔軟なスキーマを使用する方が実現可能です(簡略化)。

このfamily表は、からのメンバーを含む世帯を想定していますmember。これらのメンバー間の関係はで処理されmember_mapます。はオプションです。このparent_member_id場合、世帯主を示します。

于 2013-01-11T22:13:08.933 に答える
2

テーブルを使用しmemberて家族の「主要な」メンバーと家族の住所を保存し、familyテーブルを使用して家族の残りの人々に関する情報を保存しているようです。これはあまりよく設計されていないようで、名前はあまりよく選ばれていません。

提案: すべての「プライマリ」メンバーもfamilyテーブルに追加し、新しい 3 番目のメンバーを追加しtypeます。

INSERT INTO family
  (family_id, type, firstname, lastname)
SELECT family_id, 2, firstname, lastname
FROM member ;

r次に、クエリに単純結合を使用できます。

SELECT a.familyid, b.firstname, b.lastname, a.address, a.city, 
       CASE b.type 
         WHEN 0 THEN 'child'
         WHEN 1 THEN 'spouse'
         WHEN 2 THEN 'primary'
       END AS type
FROM member a
  JOIN family b 
    ON a.familyid = b.familyid ;

firstnameテーブルからおよびlastname列を削除することもできmemberます (また、使用法をより適切に反映するようにテーブルの名前を変更することもできます)。


2 番目のシナリオは次のように記述されます。

SELECT a.familyid, b.firstname, b.lastname, a.address, a.city, 
       CASE b.type 
         WHEN 0 THEN 'child'
         WHEN 1 THEN 'spouse'
         WHEN 2 THEN 'primary'
       END AS type
FROM member a
  JOIN family b 
    ON a.familyid = b.familyid
  JOIN channels c 
    ON b.familyid = c.familyid
WHERE c.name = 'HBO' AND ( b.type <> 2 AND c.memtype = 'family'  
                        OR b.type  = 2 AND c.memtype = 'member only'
                         ) ;
于 2013-01-11T21:55:52.383 に答える
0

質問を完全に理解しているかどうかはよくわかりません。(期待される結果セットがわからないため)。ただし、家族の概念を個々のメンバーから分離することをお勧めします。これを行うには、Family をテーブルにし、Family members を Member という別のテーブルにする必要があります。(これは明らかかもしれませんが、わかりやすくするためです)

create table Family (Family_ID, Family_Name, Family_Address, . . )

テーブル MEmbers を作成します ( Member_ID Is_Primary / 1 または 0 /、Member_Name /* 必要な数の列に展開します。たとえば、名、姓 ... */、Member_Info /* 性別、年齢 ... */ )

次に、結合を次のように構成する必要があります: 家族が何らかの視聴パターンを持つ家族を取得するためのチャネルに参加し、個々のメンバーが次のように構成されている必要があります。

(これは Stackoverflow への初めてのコメントです。シンタックス ハイライトの方法がよくわかりません ...)。私が見つけたので、それをより洗練させます。

于 2013-01-11T22:29:14.857 に答える