1

私は次のクエリを持っています:

SELECT child.id_catalog_category AS id_category, ancestor.id_catalog_category AS tree
FROM catalog_category AS child
JOIN catalog_category AS ancestor
ON (child.lft BETWEEN ancestor.lft AND ancestor.rgt)
WHERE ancestor.id_catalog_category != 1
ORDER BY id_category ASC, tree ASC

これは、階層的な製品カテゴリのバイナリツリーの再構築です。例に示すように、1つのid_categoryに対して、最大4つの「ツリー」値を持つことができます。

id_category/ツリー

3 2

3 3

4 2

4 3

4 4

5 2

5 3

5 5

6 2

6 3

6 6

7 2

7 3

7 7

望ましい結果は次のようになります。

id /id_category/ツリー

1 3 2

2 3 3

3 null null

4 null null

1 4 2

2 4 3

3 4 4

4 nullnull....。

つまり、id_categoryごとに1〜4の範囲IDを追加します。ここで、id_categoryの値が4未満の場合は、null値が表示されます。

よろしく

4

1 に答える 1

1

指定したデータのセットを複製するために、これを実行してワーキング セットを取得しました。

CREATE TABLE cc (id_category INT, tree INT);
INSERT INTO cc VALUES (3,2),(3,3),(4,2),(4,3),(4,4),(5,2),(5,3),
                      (5,5),(6,2),(6,3),(6,6),(7,2),(7,3),(7,7);
SELECT cc.* FROM cc ORDER BY 1,2;

SQL フィドルはこちら: http://sqlfiddle.com/#!2/16249/3


これが私が問題にアプローチする方法です。まず、個別のid_category値のリストを取得します。それは簡単です。(GROUP BY ではなく DISTINCT キーワードを使用できます。)

SELECT id_category
  FROM cc
 GROUP BY id_category
 ORDER BY id_category

次に、それらの各行から 4 つの行を生成します。そこで、前のクエリをインライン ビューとしてラップします (一連の括弧で囲み、エイリアスを付けて、混乱全体を単なるテーブル名のように参照します。このようなもの:

SELECT c.id_category, j_.j
  FROM (SELECT 1 AS j UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) j_
  JOIN (
        SELECT cc.id_category
          FROM cc cc
         GROUP BY cc.id_category
         ORDER BY cc.id_category
       ) c
 ORDER BY c.id_category, j_.j

別のインライン ビューを使用して 1 ~ 4 の整数を返し、CROSS JOIN を実行して、個別の id_category ごとに 4 つの行を取得しています。それは基本的に私が返したい結果セットの概要を取得します...しかし、ツリー列の値(NULL以外)はありません。

ここで、バックアップを取り、別の行セットの作業を開始する必要があります。基本的には cc テーブルの順序付きセットですが、今回はツリー列の値を含みます。ここでは、正確に 4 つの行を取得することには関心がありません。ツリー列に値がある行だけです。繰り返しますが、非常に簡単です。

SELECT s.id_category_id, s.tree
  FROM cc s
 ORDER BY s.id_category, s.tree

しかしここで、各 id_category 内で、これらの各行に相対的な行番号を割り当てたいと考えています。次のように、そのクエリを一連の括弧で囲み、エイリアスを付けて、テーブルのように扱うことで実行できます。

SELECT @i := IF(r.id_category = @prev_idcat,@i + 1,1) AS i
     , @prev_idcat := r.id_category AS id_category
     , r.tree
  FROM (SELECT @i := 0, @prev_idcat := NULL) i_ 
  JOIN (
        SELECT s.id_category, s.tree
          FROM cc s
         ORDER BY s.id_category, s.tree
       ) r

ユーザー変数で MySQL のトリックを使用して、個別の id_category ごとに 1 から始まる昇順の整数値を割り当てています。ここでの秘訣は、MySQL に行を順序付けさせることです (インライン ビューでは、r としてエイリアス化され、前の行の id_category をユーザー変数に「保存」して、次の行と比較できるようにします。

そして今、MySQL で Common Table Expressions を利用できるようになれば、本当に素晴らしいことになるところまで来ています。しかし、そうではないので、先に進み、インライン ビューをネストします。

そこで、これらの「行番号付け」クエリのそれぞれにエイリアスを与え、それらをテーブルのように参照します。クエリは次の形式になります...

SELECT b.*, q.*
  FROM ( ) b
  LEFT
  JOIN ( ) q
    ON q.id_category = b.id_category AND q.i = b.j

(ステートメントが実際に行っていることの概要を把握するために、これらのインライン ビューの内容は省略します。)

これは醜く見え始めますが、ここで魔法が起こります。b から id_category ごとに 4 つの行を取得し、それを q に結合して、id_category と「行番号」を照合します。これは LEFT OUTER 結合なので、b からすべての行を取得し、q から「一致する」行を選択します。

SELECT b.id_category, q.tree
  FROM (SELECT c.id_category, j_.j
          FROM (SELECT 1 AS j UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
               ) j_
          JOIN (
                SELECT cc.id_category
                  FROM cc cc
                 GROUP BY cc.id_category
                 ORDER BY cc.id_category
               ) c
         ORDER BY c.id_category, j_.j
       ) b   
  LEFT 
  JOIN (SELECT @i := IF(r.id_category = @prev_idcat,@i + 1,1) AS i
             , @prev_idcat := r.id_category AS id_category
             , r.tree
          FROM (SELECT @i := 0, @prev_idcat := NULL) i_ 
          JOIN (
                SELECT s.id_category, s.tree
                  FROM cc s
                 ORDER BY s.id_category, s.tree
               ) r
       ) q
    ON q.id_category = b.id_category AND q.i = b.j
 ORDER BY b.id_category, b.j

仕様に残っているのは、id列の値の生成だけです。テーブルに挿入する場合は、AUTO_INCREMENT 列を使用してそれを行うことができます。しかし、それがなければ、値を生成するのに最も便利な場所idは、エイリアスとしてインラインビューにありますb。少し微調整すると、最終的に、指定された結果セットを返すこの巨大なクエリが得られます。


SELECT b.k AS id, b.id_category, q.tree
  FROM (SELECT @k := @k + 1 AS k
             , c.id_category
             , j_.j
          FROM (SELECT @k := 0) k_
          JOIN (SELECT 1 AS j UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
               ) j_ 
          JOIN (
                SELECT cc.id_category
                  FROM cc cc
                 GROUP BY cc.id_category
                 ORDER BY cc.id_category
               ) c
         ORDER BY c.id_category, j_.j
       ) b
  LEFT
  JOIN (SELECT @i := IF(r.id_category = @prev_idcat,@i + 1,1) AS i
             , @prev_idcat := r.id_category AS id_category
             , r.tree
          FROM (SELECT @i := 0, @prev_idcat := NULL) i_ 
          JOIN (
                SELECT s.id_category, s.tree
                 FROM cc s
                 ORDER BY s.id_category, s.tree
               ) r
       ) q
    ON q.id_category = b.id_category AND q.i = b.j 
 ORDER BY b.id_category, b.j

これを行セットで機能させるには、ccテーブルへの各参照を括弧で囲まれたクエリに置き換えます。または、私が行ったように cc という名前のテーブルを作成し、クエリの結果をそこに挿入することもできます。

同じ結果セットを確実に生成する、より単純な SQL ステートメントを使用する人もいるかもしれません。もっと簡単な方法を学ぶことに非常に興味があります。

于 2012-07-06T23:20:29.307 に答える