web-dev-qa-db-ja.com

複数の行のデータを複数の列に挿入する

私は、多くの調査の後、これがどのように行われるかを見つけることができません。

私のウェブ検索はピボット、連結、ケース、サブクエリなどを取得しますが、どれも私にとって問題を完全に解決していません。複数の行から1つの行への質問は役に立ちません。

問題:

1つのテーブルに個人があります。別のテーブルには、これらの個人のアドレス(複数の場合もあります)があります。個人ごとに1つの行(適切な列)に複数の住所を配置するクエリが必要です。

これは、MySQL Fiddleテーブルとクエリを示します。

そのSQLでは、Fiddle結果には、6つの一意の個人の9つのレコードがあります。

Number  | Name              | EyeColor  | HairColor | Street                | City      | State     | Zip   | Street2   | City2 | State2| Zip2  | Street3   | ...
1       | John Smith        | blue      | red       | 100 Pine Street       | New York  | NY        | 10019 |           |       |       | 0     |           | ...
2       | Nancy Jones       | green     | red       | 200 Pine Street       | New York  | NY        | 10018 |           |       |       | 0     |           | ...
3       | Bobby Joe         | blue      | black     | 310 Oak Street        | New York  | NY        | 10018 |           |       |       | 0     |           | ...
7       | Little Lebowski   | green     | blond     | 100 Apple Street      | New York  | NY        | 10018 |           |       |       | 0     |           | ...
7       | Little Lebowski   | green     | blond     | 200 Hickory Street    | New York  | NY        | 10018 |           |       |       | 0     |           | ...
7       | Little Lebowski   | green     | blond     | 1234 Pineapple Street | New York  | NY        | 10018 |           |       |       | 0     |           | ...
2       | Nancy Jones       | green     | red       | 230 Golden Street     | New York  | NY        | 10018 |           |       |       | 0     |           | ...
8       | Sarah Shepard     | brown     | brown     | (null)                | (null)    | (null)    | (null)| (null)    | (null)| (null)| (null)| (null)    | ...

これはSQL Fiddleで、最終結果が次のようになる必要があります。

最終結果SQL Fiddleには、6人の一意の個人の合計6つのレコードと、個人の行の列にある複数の住所があります。

Number  | Name              | EyeColor  | HairColor | Street            | City      | State | Zip   | Street2           | City2     | State2| Zip2  | Street3               | City3     | State3    | Zip3  | Street4   | City4     | State4    | Zip4  | Street5   | City5     | State5    | Zip5  | Street6   | City6     | State6    | Zip6
1       | John Smith        | blue      | red       | 100 Pine Street   | New York  | NY    | 10019 |                   |           |       | 0     |                       |           |           | 0     |           |           |           | 0     |           |           |           | 0     |           |           |           | 0
2       | Nancy Jones       | green     | red       | 200 Pine Street   | New York  | NY    | 10018 | 230 Golden Street | New York  | NY    | 10018 |                       |           |           | 0     |           |           |           | 0     |           |           |           | 0     |           |           |           | 0
3       | Bobby Joe         | blue      | black     | 310 Oak Street    | New York  | NY    | 10018 |                   |           |       | 0     |                       |           |           | 0     |           |           |           | 0     |           |           |           | 0     |           |           |           | 0
7       | Little Lebowski   | green     | blond     | 100 Apple Street  | New York  | NY    | 10018 | 200 Hickory Street| New York  | NY    | 10018 | 1234 Pineapple Street | New York  | NY        | 10018 |           |           |           | 0     |           |           |           | 0     |           |           |           | 0
8       | Sarah Shepard     | brown     | brown     |                   |           |       | 0     |                   |           |       | 0     |                       |           |           | 0     |           |           |           | 0     |           |           |           | 0     |           |           |           | 0
9       | Joe Profigliani   | brown     | brown     |                   |           |       | 0     |                   |           |       | 0     |                       |           |           | 0     |           |           |           | 0     |           |           |           | 0     |           |           |           | 0

ちなみに私は与えられたMySQLテーブルを使用していますが、ソリューションの一時テーブルを構築する方法は開いていますが、問題は、最終的な結果に示されているようにデータを組み合わせる方法についてであり、エレガントさについてではありません。元のテーブル。

何千ものレコードが存在する可能性がありますが、1人の個人が6つを超えるアドレスを持っているとは思いません。 (もしも利用可能なアドレスフィールド以上あるならそれが優雅に失敗することは便利でしょうがそれは問題の核心ではありません。)

これが私が正しい質問をしていない単純なものであることを願っています。

どの(名前、住所)の組み合わせが「正しい」組み合わせであるかをどのように決定していますか?

まあ、それが私の問題の最初の50%かもしれません。すべての住所は、個人の行に書き込まれるという点で「正しい」ものです。たとえば、IndividualNumber 7はLittleLebowskiです。彼には3つの住所があり、3つすべてが最終結果のその個人の行に表示されます。私の想定では、最初に「出現」するものが最初のアドレスとして入力されます。 (自動インクリメントされた値を持つ一時テーブル?)レコードの順序についての質問に答えたら、問題の残りの50%は、それらをその個人の行の対応する列に書き込むことです。

6
C0MPU7ER

質問は3つの主な操作に分けることができます。

  1. 行をIndividualNumberで分割する(変数を使用)
  2. パーティション化された行を列にピボットする(CASEを使用)
  3. 名前とその他の情報を追加し、NULLを削除します

あなたはここでサンプルを見ることができます: SQL Fiddle

行をIndividualNumberで分割:

このクエリの動作は、Oracle(> = 10g)、PostgreSQL(> = 8.4)、およびSQL Server(> = 2012)で使用可能なROW_NUMBER()ウィンドウ関数に似ています。

MySQLはそれを実装しておらず、変数とCASEステートメントを使用して実行する必要があります。

  SELECT @row := CASE WHEN inf.IndividualNumber = @id 
      THEN @row + 1 ELSE 1 END as row 
    , @id := inf.IndividualNumber as IndividualNumber
      , inf.IndividualAddressStreet
      , inf.IndividualCity
      , inf.IndividualState
      , inf.IndividualZip
  FROM (SELECT @row := 0, @id := 0) v
    , InfoaboutThemTable as inf 
  ORDER BY inf.IndividualNumber

rowIndividualNumber(パーティション)ごとに、1からnまでの一意のInfoaboutThemTable値を返します。

row     IndividualNumber    IndividualAddressStreet     IndividualCity  IndividualState     IndividualZip
1       1                   100 Pine Street             New York        NY                  10019
1       2                   200 Pine Street             New York        NY                  10018
2       2                   201 Pine Street             New York        NY                  10018
3       2                   230 Golden Street           New York        NY                  10018
4       2                   456 Golden Street           New York        NY                  10018
1       3                   310 Oak Street              New York        NY                  10018
1       7                   100 Apple Street            New York        NY                  10018
2       7                   200 Hickory Street          New York        NY                  10018
3       7                   1234 Pineapple Street       New York        NY                  10018   

Address1Address2、...の順序付け方法がまだわからないため、ORDER BY inf.IndividualNumberを使用して、特定の順序でIndividualNumberでパーティション化していません。

IndividualNumberでパーティション分割し、IndividualAddressStreetで順序付けられたパーティションのメンバーに番号を付ける場合は、ORDER BY inf.IndividualNumber, inf.IndividualAddressStreetに置き換えることができます。

ピボット

IndividualNumberの各パーティションの各行に一意のrow値が設定されると、それを使用して行を列に転置/ピボットできます。

MySQLはPIVOT演算子を実装していません。データはGROUP BY d.IndividualNumberを使用して列Xに移動およびピボットできます。転置された列ごとに、CASE WHEN row = X THEN ... ENDが集計(MAX)と共に使用されます。

MAX(CASE WHEN row = 1 THEN d.IndividualAddressStreet END) AS Street1
MAX(CASE WHEN row = 1 THEN d.IndividualCity END) AS City1
...
MAX(CASE WHEN row = 2 THEN d.IndividualAddressStreet END) AS Street2
...

これには3つのグループしか含まれていませんが、6個またはN個のグループに簡単に拡張できます。

名前情報を追加:

最終的にIndividualsTableは、名前と色を目的の出力に追加するために、ピボットサブクエリにLEFT JOINです。

NULLの値は、COALESCEを使用して空の文字列に置き換えられます。

完全なクエリ:

SELECT idt.IndividualNumber
    , idt.Name
    , idt.IndividualEyeColor
    , idt.IndividualHairColor
    , COALESCE(grp.Street1, '') as Street1
    , COALESCE(grp.City1, '') as City1
    , COALESCE(grp.State1, '') as State1
    , COALESCE(grp.Zip1, '') as Zip1
    , COALESCE(grp.Street2, '') as Street2
    , COALESCE(grp.City2, '') as City2
    , COALESCE(grp.State2, '') as State2
    , COALESCE(grp.Zip2, '') as Zip2
    , COALESCE(grp.Street3, '') as Street3
    , COALESCE(grp.City3, '') as City3
    , COALESCE(grp.State3, '') as State3
    , COALESCE(grp.Zip3, '') as Zip3
FROM IndividualsTable idt
LEFT JOIN (
    SELECT d.IndividualNumber as IndividualNumber
        , MAX(CASE WHEN row = 1 THEN d.IndividualAddressStreet END) AS Street1
        , MAX(CASE WHEN row = 1 THEN d.IndividualCity END) AS City1
        , MAX(CASE WHEN row = 1 THEN d.IndividualState END) AS State1
        , MAX(CASE WHEN row = 1 THEN d.IndividualZip END) AS Zip1
        , MAX(CASE WHEN row = 2 THEN d.IndividualAddressStreet END) AS Street2
        , MAX(CASE WHEN row = 2 THEN d.IndividualCity END) AS City2
        , MAX(CASE WHEN row = 2 THEN d.IndividualState END) AS State2
        , MAX(CASE WHEN row = 2 THEN d.IndividualZip END) AS Zip2
        , MAX(CASE WHEN row = 3 THEN d.IndividualAddressStreet END) AS Street3
        , MAX(CASE WHEN row = 3 THEN d.IndividualCity END) AS City3
        , MAX(CASE WHEN row = 3 THEN d.IndividualState END) AS State3
        , MAX(CASE WHEN row = 3 THEN d.IndividualZip END) AS Zip3
    FROM
    (
      SELECT @row := CASE WHEN inf.IndividualNumber = @id 
          THEN @row + 1 ELSE 1 END as row 
        , @id := inf.IndividualNumber as IndividualNumber
          , inf.IndividualAddressStreet
          , inf.IndividualCity
          , inf.IndividualState
          , inf.IndividualZip
      FROM (SELECT @row := 0, @id := 0) v
        , InfoaboutThemTable as inf 
      ORDER BY inf.IndividualNumber
    ) d 
    GROUP BY d.IndividualNumber
) grp
    ON grp.IndividualNumber = idt.IndividualNumber
;
7