2

以下の質問に基づいて、すべての関係を挿入しようとしています。A から C へのすべての関係を取得することができました (以下の質問に従って)。しかし、私が得たのは、「C friends with A」という記録も得ています。「重複した友情はありません」という質問文を理解する限り、友情をAからCに挿入する必要があり、その逆ではありません. 問題を間違って理解しているか、目的の結果が得られません。
そのため、取得したすべての値をテーブルに挿入しようとすると、結果が間違っています。一部の人は、値が 2 である必要以上の友達を持っています。

テーブルの構造は次のとおりです。

Friend ( ID1, ID2 )

ID1の生徒はID2の生徒と友達です。友情は相互的なものなので、(123, 456) が Friend テーブルにある場合、(456, 123) もそうです。

(主キーなし)私が解決しようとしている状況は次のとおりです。

「A が B と友達で、B が C と友達であるすべての場合について、A と C のペアに新しい友情を追加します。重複した友情、既存の友情、または自分自身との友情を追加しないでください。」

私はこの問題を2日間解決しようとしています。助けてください。

前もって感謝します。

----私のSQLクエリ-----

select B.ID1 as ID1,B.ID3 as ID2
from (select A.ID1 as ID1,A.ID2 as ID2,A.ID3 as ID3,F3.ID2 as ID4
from (select F1.ID1 as ID1,F1.ID2 as ID2,F2.ID2 as ID3
from Friend F1 join Friend F2
on F1.ID2=F2.ID1
where F1.ID1<>F2.ID2) A join Friend F3
on A.ID3=F3.ID1) B
where B.ID1<>B.ID4
group by  B.ID1,B.ID3
4

2 に答える 2

2

(例として) フレンド テーブルにこれらの行が含まれていると仮定します。

ID1 ID2
--- ---
 a   b
 a   c
 b   a
 b   c
 b   d
 c   b

まず、次のようなクエリを使用して、フレンド テーブルから「完全なフレンド」タプルを特定することから始めます。

SELECT fa.ID1
     , fa.ID2
  FROM friend fa
  JOIN friend fb
    ON fb.ID1 = fa.ID2
   AND fb.ID2 = fa.ID1

fa.ID1 fa.ID2
------ ------
  a      b
  b      a
  b      c
  c      b

この結果は、a が b と友達であり、b が c と友達であることを示しています。(a,c)との行は、逆のor (b,d)がないため省略されています。(c,a)(d,b)

当分の間、このセットを " ft" (フレンド タプル) と呼びます。これで、そのセット (ft) に対するクエリを記述して、"a->b->c" および "c->b->a" のすべてのフレンド ペアを取得できます。

SELECT fx.ID1
     , fy.ID2
  FROM ft fx
  JOIN ft fy
    ON fy.ID1 = fx.ID2 
   AND fy.ID2 <> fx.ID1

fx.ID1 fy.ID2
------ ------
  a      c
  c      a

ただし、友人テーブルに既に存在する行を複製しないようにする必要があるため、NOT IN または NOT EXISTS 述語を使用するか、結合防止パターンを使用して、友人テーブルにある行を削除できます。フレンド テーブルに既にある行と一致します。

SELECT fx.ID1
     , fy.ID2
  FROM ft fx
  JOIN ft fy
    ON fy.ID1 = fx.ID2 
   AND fy.ID2 <> fx.ID1
  -- eliminate rows that match
  LEFT
  JOIN friend fe
    ON fe.ID1 = fx.ID1
   AND fe.ID2 = fy.ID2
 WHERE fe.ID1 IS NULL

fx.ID1 fy.ID2
------ ------
  c      a

ここで、への参照をft、セットを生成するクエリ (インライン ビューとして) に置き換えることができます。

SELECT fx.ID1
     , fy.ID2
  FROM ( SELECT fa.ID1
              , fa.ID2
           FROM friend fa
           JOIN friend fb
             ON fb.ID1 = fa.ID2
            AND fb.ID2 = fa.ID1
       ) fx
  JOIN ( SELECT fc.ID1
              , fc.ID2
           FROM friend fc
           JOIN friend fd
             ON fd.ID1 = fc.ID2
            AND fd.ID2 = fc.ID1
       ) fy
    ON fy.ID1 = fx.ID2 
   AND fy.ID2 <> fx.ID1
  -- eliminate rows that match
  LEFT
  JOIN friend fe
    ON fe.ID1 = fx.ID1
   AND fe.ID2 = fy.ID2
 WHERE fe.ID1 IS NULL
 GROUP 
    BY fx.ID1
     , fy.ID2

((ID1、ID2)が一意であることが保証されている限り、このクエリは重複を生成しないと考えています。そして、このクエリは指定された一致のみを生成し、余分な一致は生成しないと考えています. 確認のためにいくつかの追加のテスト ケースがあります. クエリが重複を生成する場合は、クエリに aGROUP BY fx.ID1, fy.ID2を追加するとそれらが削除されます.)

最後に、これらの行をフレンド テーブルに入れるには、クエリの前に次を付けます。

INSERT INTO friend (ID1,ID2)

アップデート

返してほしい結果は、「友情」がどのように表現されるかによって異なります。

私は、「友達」のペアがfriend2 つのタプルの存在によって表に表されていると想定していました:(a,b)と (b,a) の両方が存在する必要があります。(友情は、「a の友人 b」と「b の友人 a」で形成されます)。

行の 1 つだけが存在する場合、それは本当の友情ではなく、中途半端な友情です。

いくつかのテストケースを実行しました。それらを介して作業するのはちょっと面倒です。ORDER BY を追加して行を決定論的な順序に戻し、SELECT リストに列を追加して「パス」(共有フレンド) を検証することで、クエリを拡張しました。WHERE 句をコメントアウトしたので、すべての潜在的な友達を見ることができました。

GROUP BY重複を排除するために a を追加する必要があることがわかりました。a-c友情は、2 人以上の共有された友人 (例:bと) から派生させることができますra-b + b-cとの両方がa-r + r-c得られa-cます。

これは私がテストした最後のクエリです。GROUP BY が追加されていることを除けば、基本的に前のものと同じです。

SELECT fx.ID1
     , fy.ID2
 --  , fx.ID1>fy.ID2 AS d
 --  , fx.ID1 AS x1
 --  , fx.ID2 As x2
 --  , fy.ID1 AS y1
 --  , fy.ID2 As y2
 --  , fe.ID1 AS e1
 --  , fe.ID2 AS e2
  FROM ( SELECT fa.ID1
              , fa.ID2
              , fa.ID1>fa.ID2 AS d
           FROM friend fa
           JOIN friend fb
             ON fb.ID1 = fa.ID2
            AND fb.ID2 = fa.ID1
       -- ORDER
       --    BY LEAST(fa.ID1,fa.ID2)
       --     , GREATEST(fa.ID1,fa.ID2)
       --     , fa.ID1>fa.ID2
       ) fx
  JOIN ( SELECT fc.ID1
              , fc.ID2
           FROM friend fc
           JOIN friend fd
             ON fd.ID1 = fc.ID2
            AND fd.ID2 = fc.ID1
       -- ORDER
       --    BY LEAST(fc.ID1,fc.ID2)
       --     , GREATEST(fc.ID1,fc.ID2)
       --     , fc.ID1>fc.ID2
       ) fy
    ON fy.ID1 = fx.ID2 
   AND fy.ID2 <> fx.ID1
  -- eliminate rows that match existing row
  LEFT
  JOIN friend fe
    ON fe.ID1 = fx.ID1
   AND fe.ID2 = fy.ID2
 WHERE fe.ID1 IS NULL
 GROUP
    BY fx.ID1
     , fy.ID2
 ORDER
    BY LEAST(fx.ID1,fy.ID2)
     , GREATEST(fx.ID1,fy.ID2)
     , fx.ID1>fy.ID2

完全な友情関係がただ 1 つのタプルの存在によって表される場合、"(a,b)" は "(b,a)" を意味するため、クエリを変更する必要があります。

fxandのインライン ビュー クエリはfy、「欠落している」逆タプルを返すように拡張する必要があります... (a,b) がフレンド テーブルにある場合、クエリは (a,b) と (b,a) の両方を返す必要があります。 )。SELECT リストの列の順序を逆にして、2 つの同一のクエリ間で UNION ALL 操作を実行することで、これを実現できます。(ここでは、UNION ALL の代わりに UNION を実際に使用して、重複を排除することができます。) fxandのインライン ビュー クエリは次のfyようになります。

SELECT fa.ID1, fa.ID2 FROM ...
 UNION ALL
SELECT fa.ID2, fa.ID1 FROM ...

フレンド テーブル内の一致する行を除外するチェックも変更する必要があります (既存の (a,b) または ( b,a) 行)

ON ( fe.ID1 = fx.ID1 AND fe.ID2 = fy.ID2 )
OR ( fe.ID1 = fy.ID2 AND fe.ID2 = fx.ID1 )

また、「余分な」逆タプルを削除するには、SELECT リストと GROUP BY を変更する必要があります。ORDER BY のような式を使用できます

SELECT LEAST(fx.ID1,fy.ID2) AS ID1
     , GREATEST(fx.ID1,fy.ID2) AS ID2
       ...
 GROUP
    BY LEAST(fx.ID1,fy.ID2)
     , GREATEST(fx.ID1,fy.ID2)
于 2013-08-23T18:58:34.693 に答える
1

上記の各回答は、私には面倒に見えます。クエリを表す簡単な方法があると思います。上記の回答で述べたように、Friend テーブルが次のようになっていると仮定しましょう。

ID1 ID2
--- ---
 ab
 交流
 バ
 紀元前
 BD
 CB

友達が互角であるケースが必要な場合は、'a' が 'b' と友達で、'b' が 'a' と友達であるケースを探します。このようなクエリは、次の結果を生成する必要があります。

ID1 ID2
--- ---
 ab
 バ
 紀元前
 CB

次の sql コマンドは、非常にうまく追跡し、正しいタプルを識別します。

選択する *
友達から
where (ID2, ID1) in (select * From Friend)

これにより、以下が生成されます。

ID1 ID2
--- ---
 ab
 バ
 紀元前
 CB

リストを一度作成したい場合は、重複を排除し、可能性のあるタプルの 1 つを削除する「and」オプションを追加するだけで、ID2 が ID1 より小さいと言えます。完全な SQL ステートメントは次のようになります。

select * from Friend where (ID2, ID1) in (select * From Friend) and ID2 < ID1;

そして、重複は排除されます。

ID1 ID2
--- ---
 ab
 紀元前
于 2015-09-17T20:44:37.320 に答える