0

で結合された 2 つの select ステートメントがありますUNION ALL。最初のステートメントの where 句は、以前にユーザーに表示された行のみを収集します。2 番目のステートメントは、ユーザーに表示されていないすべての行を収集するため、表示された結果が最初に表示され、その後に表示されていない結果が表示されます。

もちろん、これは単純な を使用した同じ select ステートメントで簡単にORDER BY実現できますが、2 つの別個の select の理由は単純です。

次の構造とデータを検討してください。

+----+------+-----+--------+------+
| id | from | to  | viewed | data |
+----+------+-----+--------+------+
| 1  | 1    | 10  | true   | .... |
| 2  | 10   | 1   | true   | .... |
| 3  | 1    | 10  | true   | .... |
| 4  | 6    | 8   | true   | .... |
| 5  | 1    | 10  | true   | .... |
| 6  | 10   | 1   | true   | .... |
| 7  | 8    | 6   | true   | .... |
| 8  | 10   | 1   | true   | .... |
| 9  | 6    | 8   | true   | .... |
| 10 | 2    | 3   | true   | .... |
| 11 | 1    | 10  | true   | .... |
| 12 | 8    | 6   | true   | .... |
| 13 | 10   | 1   | false  | .... |
| 14 | 1    | 10  | false  | .... |
| 15 | 6    | 8   | false  | .... |
| 16 | 10   | 1   | false  | .... |
| 17 | 8    | 6   | false  | .... |
| 18 | 3    | 2   | false  | .... |
+----+------+-----+--------+------+

基本的に、表示されていないすべての行がステートメントによって選択されることを望みます。これは、viewed列がtrueorの天気をチェックすることによって達成されfalseます。非常にシンプルで簡単で、ここで心配する必要はありません。

ただし、既に表示されている行、つまり columnviewed is TRUEに関しては、これらのレコードについては、グループごとに 3 行のみが返されるようにします。

この場合の適切な結果は、各グループの最新の 3 行です。

+----+------+-----+--------+------+
| id | from | to  | viewed | data |
+----+------+-----+--------+------+
| 6  | 10   | 1   | true   | .... |
| 7  | 8    | 6   | true   | .... |
| 8  | 10   | 1   | true   | .... |
| 9  | 6    | 8   | true   | .... |
| 10 | 2    | 3   | true   | .... |
| 11 | 1    | 10  | true   | .... |
| 12 | 8    | 6   | true   | .... |
+----+------+-----+--------+------+

理想的な結果セットからわかるように、3 つのグループがあります。したがって、表示された結果の目的のクエリは、検出されたグループごとに最大 3 行を表示する必要があります。この場合、これらのグループは 10 と 1 および 8 と 6 で、どちらも 3 つの行を表示する必要がありましたが、他のグループ 2 と 3 では 1 つの行しか表示できませんでした。

from = xここでandは、 andto = yであるかのように同じグループ化を行うことに注意してfrom = yくださいto = x。したがって、最初のグループ分け (10 と 1) を考えると、 と が であった場合、とfrom = 10to = 1同じグループです。from = 1to = 10

ただし、テーブル全体にはたくさんのグループがあり、それぞれの最新の 3 つだけが select ステートメントで返されることを望んでいます。それが私の問題です。ある時点で、数千ではないにしても数百のレコードを持つことになります。

ご協力いただきありがとうございます。

注:idfromtoおよびviewedはインデックス化されており、パフォーマンスに役立ちます。

PS:この質問に正確に名前を付ける方法がわかりません。より良いアイデアがある場合は、ゲストになってタイトルを編集してください。

4

1 に答える 1

3

なんと毛玉!これは、最新、2 番目、3 番目に移動するにつれて、徐々に難しくなります。

必要な ID のリストを取得して、これをまとめましょう。次に、ID でテーブルから項目を取得できます。

この比較的簡単なクエリは、最新のアイテムの ID を取得します

 SELECT id FROM
    (SELECT max(id) id, fromitem, toitem
       FROM stuff
      WHERE viewed = 'true'
      GROUP BY fromitem, toitem
    )a

フィドル: http://sqlfiddle.com/#!2/f7045/27/0

次に、2 番目に新しいアイテムの ID を取得する必要があります。これを行うには、自己結合スタイルのクエリが必要です。同じ集計を行う必要がありますが、最新の項目を省略した仮想テーブルで行います。

select id from (
  select max(b.id) id, b.fromitem, b.toitem
    from stuff a
    join
           (select id, fromitem, toitem
            from stuff
           where viewed = 'true'
            ) b on (    a.fromitem = b.fromitem 
                    and a.toitem = b.toitem
                    and b.id < a.id)
   where a.viewed = 'true'
   group by fromitem, toitem
  )c

フィドル: http://sqlfiddle.com/#!2/f7045/44/0

最後に、3 番目に新しい項目の ID を取得する必要があります。慈悲!先ほど行ったクエリをテーブルに再度結合する必要があります。

select id from
(
  select max(d.id) id, d.fromitem, d.toitem
    from stuff d
     join 
    (
       select max(b.id) id, b.fromitem, b.toitem
          from stuff a
          join
            (
               select id, fromitem, toitem
                 from stuff
                where viewed = 'true'
            ) b on  (    a.fromitem = b.fromitem 
                     and a.toitem = b.toitem
                     and b.id < a.id)
          where a.viewed = 'true'
          group by fromitem, toitem
     ) c on (    d.fromitem = c.fromitem
             and d.toitem = c.toitem
             and d.id < c.id)
    where d.viewed='true'
  group by d.fromitem, d.toitem
 ) e

フィドル: http://sqlfiddle.com/#!2/f7045/45/0

これで、これらすべての ID の結合を取得し、それらを使用してテーブルから適切な行を取得し、完了です。

SELECT * 
  FROM STUFF
 WHERE ID IN
(

SELECT id FROM
    (SELECT max(id) id, fromitem, toitem
       FROM stuff
      WHERE viewed = 'true'
      GROUP BY fromitem, toitem
    )a
UNION
select id from (
  select max(b.id) id, b.fromitem, b.toitem
    from stuff a
    join
           (select id, fromitem, toitem
            from stuff
           where viewed = 'true'
            ) b on (    a.fromitem = b.fromitem 
                    and a.toitem = b.toitem
                    and b.id < a.id)
   where a.viewed = 'true'
   group by fromitem, toitem
  )c
UNION
select id from
(
  select max(d.id) id, d.fromitem, d.toitem
    from stuff d
     join 
    (
       select max(b.id) id, b.fromitem, b.toitem
          from stuff a
          join
            (
               select id, fromitem, toitem
                 from stuff
                where viewed = 'true'
            ) b on  (    a.fromitem = b.fromitem 
                     and a.toitem = b.toitem
                     and b.id < a.id)
          where a.viewed = 'true'
          group by fromitem, toitem
     ) c on (    d.fromitem = c.fromitem
             and d.toitem = c.toitem
             and d.id < c.id)
    where d.viewed='true'
  group by d.fromitem, d.toitem
 ) e
UNION
select id from stuff where viewed='false'
)
order by viewed desc, fromitem, toitem, id desc

ティヒ。SQL が多すぎます。フィドル: http://sqlfiddle.com/#!2/f7045/47/0

そして今、最後の要件である、グラフが順序付けられていないという要件に対処する必要があります。つまり、from=n to=m は from=m to=n と同じです。

これを行うには、物理​​テーブルではなく仮想テーブルが必要です。これでうまくいきます。

 SELECT id, least(fromitem, toitem) fromitem, greatest(fromitem,toitem) toitem, data
   FROM stuff

ここで、物理テーブルが表示されていたすべての場所で、この仮想テーブル、このビューを使用する必要があります。これを行うためにビューを使用しましょう。

CREATE VIEW 
AS 
SELECT id,
       LEAST(fromitem, toitem) fromitem,
       GREATEST (fromitem, toitem) toitem,
       viewed,
       data;

したがって、最終的なクエリは次のとおりです。

SELECT *
      FROM stuff
     WHERE ID IN
    (

    SELECT id FROM
        (SELECT max(id) id, fromitem, toitem
           FROM STUFF_UNORDERED
          WHERE viewed = 'true'
          GROUP BY fromitem, toitem
        )a
    UNION
    SELECT id FROM (
      SELECT max(b.id) id, b.fromitem, b.toitem
        FROM STUFF_UNORDERED a
        JOIN
               (SELECT id, fromitem, toitem
                FROM STUFF_UNORDERED
               WHERE viewed = 'true'
                ) b ON (    a.fromitem = b.fromitem
                        AND a.toitem = b.toitem
                        AND b.id < a.id)
       WHERE a.viewed = 'true'
       GROUP BY fromitem, toitem
      )c
    UNION
    SELECT id FROM
    (
      SELECT max(d.id) id, d.fromitem, d.toitem
        FROM STUFF_UNORDERED d
         JOIN
        (
           SELECT max(b.id) id, b.fromitem, b.toitem
              FROM STUFF_UNORDERED a
              JOIN
                (
                   SELECT id, fromitem, toitem
                     FROM STUFF_UNORDERED
                    WHERE viewed = 'true'
                ) b ON  (    a.fromitem = b.fromitem
                         AND a.toitem = b.toitem
                         AND b.id < a.id)
              WHERE a.viewed = 'true'
              GROUP BY fromitem, toitem
         ) c ON (    d.fromitem = c.fromitem
                 AND d.toitem = c.toitem
                 AND d.id < c.id)
        WHERE d.viewed='true'
      GROUP BY d.fromitem, d.toitem
     ) e
    UNION
    SELECT id FROM STUFF_UNORDERED WHERE viewed='false'
    )
    ORDER BY viewed DESC,
            least(fromitem, toitem),
            greatest(fromitem, toitem),
            id DESC

フィドル: http://sqlfiddle.com/#!2/8c154/4/0

于 2012-10-08T21:17:33.413 に答える