3

TBL_ITEMから getまでの 3 つの列をどのように参照できるのか疑問に思っていますSUM(QTY) FROM TBL_INVTY

TBL_ITEM

+------+-------------+----------+
| CODE | PARENT_CODE | OLD_CODE |
+------+-------------+----------+
|    2 |        NULL |       20 |
|   2A |           2 |     NULL |
|   2B |           2 |     NULL | 
|    3 |        NULL |     NULL |
|    4 |        NULL |     NULL |
|   20 |        NULL |     NULL |
+------+-------------+----------+

TBL_INVTY

+------+-----+
| CODE | QTY |
+------+-----+
|    2 |   2 |
|   2A |   4 |
|   2B |   1 |
|    3 |   3 |
|    4 |   5 |
|   20 |   2 |
+------+-----+

そして、これに到達します:

+------+-----+
| CODE | QTY |
+------+-----+
|    2 |   9 |
|    3 |   3 |
|    4 |   5 |
+------+-----+

最後の表の最初の行WHERE CODE=2QTYは 9SUM(QTY) from TBL_INVTY WHERE CODE IN (20, 2A, 2B, 2)ですTBL_ITEM

4

1 に答える 1

3

トリックは、tbl_item から 2 列の結果セットを取得することです。私が「一致」と「マップ」と呼ぶ 2 つの列。「一致」列の値は、「一致する」行を見つけるために JOIN 述語で使用するものです。しかし、マップ列から値を返し、それに対して GROUP BY を実行します。

あなたの例では、次のクエリで必要な結果セットが得られると思います。

SELECT IF(i.old_code IS NOT NULL,i.old_code,i.code) AS `match`
     , IF(i.parent_code IS NOT NULL,i.parent_code,i.code) AS map
  FROM tbl_item i
  LEFT
  JOIN tbl_item j
    ON j.old_code = i.code
 WHERE j.old_code IS NULL
 UNION ALL
SELECT k.code
     , k.code
  FROM tbl_item k
 WHERE k.old_code IS NOT NULL

これにより、次の結果セットが得られます。

+-------+-------+
| match | map   |
+-------+-------+
|   20  |   2   |
|   2A  |   2   |
|   2B  |   2   |
|    3  |   3   |
|    4  |   4   |
|    2  |   2   |
+-------+-------+

「一致」列が一意であることを確認する必要があります。これにより、tbl_invty の行がこのセットの複数の行に誤って一致することはありません。これは、このデータ セットでは問題ではありませんが、より一般的なケースでは問題になる可能性があります。また、これは古いものから新しいものへの「1レベル」のみを行います。「新しい」コードが後で置き換えられた場合、このクエリは「最新」を見つけず、行の値のみを見つけます。繰り返しますが、このデータの問題ではありませんが、より一般的なケースでは、これが問題になる可能性があります。

(クエリを記述する他の方法があります。たとえば、IF ではなく CASE 式を使用するか、より簡潔に:

SELECT IFNULL(i.old_code,i.code) AS `match`
     , IFNULL(i.parent_code,i.code) AS map
  FROM tbl_item i 
  LEFT
  JOIN tbl_item j
    ON j.old_code = i.code
 WHERE j.old_code IS NULL
 UNION ALL
SELECT k.code
     , k.code
  FROM tbl_item k
 WHERE k.old_code IS NOT NULL      

とにかく、適切な結果セットを取得するクエリがあれば、SUM(QTY) を簡単に取得できます。そのクエリを別のクエリの行ソースとして使用するだけです。(これを「インライン ビュー」と呼びますが、MySQL では「派生テーブル」と呼んでいます。これは、MySQL が実際にビュー クエリを処理する方法をより正確に表しています。)

SELECT m.map      AS CODE
     , SUM(v.qty) AS QTY
  FROM tbl_invty v
  JOIN (
         SELECT IFNULL(i.old_code,i.code) AS `match` 
              , IFNULL(i.parent_code,i.code) AS map 
           FROM tbl_item i 
           LEFT 
           JOIN tbl_item j 
             ON j.old_code = i.code 
          WHERE j.old_code IS NULL 
          UNION ALL
         SELECT k.code
              , k.code
           FROM tbl_item k
          WHERE k.old_code IS NOT NULL
       ) m 
    ON m.match = v.code 
 GROUP 
    BY m.map 

ファローアップ

Q: CODE に PARENT_CODE と OLD_CODE の両方が含まれている場合はどうなりますか (これが可能なシナリオであることがわかりました...

A: "match" および "map" コードを返すには、クエリをテストする必要があります。

その新しいケース (PARENT_CODE と OLD_CODE の両方を含む行) を考えると、調整が必要です。実際には、各ケースを実行して、各ケースで何を返す必要があるかを判断する必要があります。

テーブル内のすべてのコード (CODE 列、PARENT_CODE 列、または OLD_CODE 列にあるかどうか) を「一致」コードとして選択し、それぞれに適切な「マップ」コードを導き出したいようです。

テーブルの CODE 列は NOT NULL であると仮定しtbl_itemます (これを単純化するためです)。

これらは、これらすべてのケースを処理するために、私が考えている 4 つのクエリです。

-- rows with PARENT_CODE,  match=CODE map=PARENT_CODE
SELECT i.code AS `match_code`
     , i.parent_code AS `map_code`
  FROM tbl_item i
 WHERE i.parent_code IS NOT NULL


-- rows with PARENT_CODE and OLD_CODE,  match=OLD_CODE map=PARENT_CODE
SELECT j.old_code
     , j.parent_code
  FROM tbl_item j
 WHERE j.parent_code IS NOT NULL
   AND j.old_code IS NOT NULL 

-- rows with no PARENT_CODE,  match=CODE map=CODE   
SELECT k.code
     , k.code
  FROM tbl_item k
 WHERE k.parent_code IS NULL

-- rows with OLD_CODE and no PARENT_CODE,  match=OLD_CODE map=CODE
SELECT l.old_code
     , l.code
  FROM tbl_item l
 WHERE l.parent_code IS NULL
   AND l.old_code IS NOT NULL

これらは、UNION ALL 演算子を使用して結合されます。

同じ CODE が複数回出現し、それぞれが異なる PARENT_CODE を指している可能性がある奇抜なデータを想像できます。

TBL_ITEM の奇抜な (予期しない) 行の例。

+------+-------------+----------+
| CODE | PARENT_CODE | OLD_CODE |
+------+-------------+----------+
|  77A |         77A |     NULL |
|  77A |         77B |      77A |
|   77 |          77 |      77A |
|   77 |         77A |     NULL |
+------+-------------+----------+

このような混乱をどうしますか?

SUM(QTY) を取得する限り、"map/match" 行セットで保証する必要があるのは、特定の CODE が 1 回だけ出現することです。(そこに倍数があると、複数に一致するため、SUM が高くなりすぎます...

簡単な解決策は、クエリを別のクエリでラップして、重複を排除し、マップするコードを 1 つだけ選択することです。これは「正しい」修正ではないかもしれませんが、作業できる結果セットが得られます。

SELECT u.match
     , MIN(u.map) AS map
  FROM (
          -- query to get match/map rowset here
       ) u
 GROUP BY u.match

次に、このクエリを、以前と同じクエリである SUM(QTY) を取得するインライン ビューとして使用します。としてエイリアス化されたインライン ビューでそのクエリを変更したところmです。

SELECT m.map      AS CODE
     , SUM(v.qty) AS QTY
  FROM tbl_invty v
  JOIN (
         SELECT u.match
              , MIN(u.map) AS map
           FROM (
                  -- query to get match/map rowset here
                ) u
          GROUP BY u.match
       ) m 
    ON m.match = v.code 
 GROUP
    BY m.map

この時点で、言うまでもなく、クエリを単純にするために本当に必要なのは、 (一意null ではない)match_codeとオプションの の 2 つの列だけを持つ ですmap_code

これにより、非常に単純なクエリが作成されます。

SELECT IFNULL(m.map_code,m.match_code) AS CODE
     , SUM(v.qty) AS QTY
  FROM tbl_invty v
  JOIN tbl_match_map m
    ON m.match_code = v.code
 GROUP
    BY IFNULL(m.map_code,m.match_code)

大変な作業である特定のテーブルから「tbl_match_map」行セットを生成しています。

于 2013-07-18T06:34:21.337 に答える