0

宿題をして、ネットやStackOverflowで情報を探しましたが、質問に対する答えが見つかりませんでした。私は見た:

テーブルの行から2つの列を作成する(理解していませんでした) MySQLは同じテーブルに列を表示する行を結合します(複雑)(違い、問題は同じではないようです) テーブルの行から2つの列を作成します

これを使用してクエリを作成しました。

MySQLで行を列として表示する方法は?

次のようなテーブル「レッスン」があります。

ID  title
---------
1   Math
2   Latin
3   English
4   Science 

各レッスンで合格したテストユーザーのタイプを格納する2番目のテーブル「結果」があります。現在、oとwの2種類のテストがあります(将来さらに増える可能性があります)。表は次のようになります。

lesson_id  usr_id  test_type   course_id
----------------------------------------
1          100       o          1
1          100       w          1
1          24        o          1
1          36        w          1

上記の表では、ユーザー100が数学のテストoとwに合格しました。上記の表では、ユーザー24が数学のテストoに合格しました。上記の表では、ユーザー36が数学のテストwに合格しました。

ここで、ユーザー100のレポートを取得したいのですが、次のようなものが必要です。

ID  title      o       w
------------------------------
1    Math       TRUE    TRUE
2    Latin      FALSE   FALSE
3    English    FALSE   FALSE
4    Science    FALSE   FALSE

ユーザー36の場合:

ID  title      o       w
------------------------------
1    Math       FALSE    TRUE
2    Latin      FALSE   FALSE
...

ユーザー24の場合:

ID  title      o       w
------------------------------
1    Math       TRUE    FALSE
2    Latin      FALSE   FALSE
...

その他のユーザー:

ID  title      o       w
------------------------------
1    Math       FALSE   FALSE
2    Latin      FALSE   FALSE
...

私は次のようにクエリでこれを達成することができます:

SELECT l.id, l.title, COALESCE(s.o,FALSE) AS o, COALESCE(t.w, FALSE) AS w FROM lessons l 
LEFT JOIN (
    SELECT r.lesson_id,
    CASE
        WHEN r.test_type='o' THEN TRUE
        ELSE FALSE
    END AS o
    FROM results r WHERE r.itype='o' AND r.usr_id=100
    ) AS s ON l.id=s.lesson_id 
LEFT JOIN (
    SELECT rr.lesson_id,
    CASE
        WHEN rr.test_type='w' THEN TRUE
        ELSE FALSE
    END AS w 
    FROM results rr WHERE rr.test_type='w' AND rr.usr_id=100
    ) AS t ON l.id=t.lesson_id
WHERE l.course_id=1 ORDER BY l.id ASC;

このコードは機能しているように見えますが(まだテストで忙しいです)、2つの結合が必要であり、各結合は将来非常に大きくなる可能性のあるテーブルからの選択で構成されているため、私は好きではありません。このクエリは頻繁に実行されます

  1. これを行うためのより良い方法を提案できますか(より良いとは、より賢く、より簡単で、より速く、または他の設計を意味する可能性があります...または他の提案:-))?

  2. これに固執する必要がある場合、このクエリを高速に実行するための最適なインデックスを設定する方法として、参照をお勧めしますか?リンク、使用した記事は?

  3. さらにtest_typesが作成されるとどうなりますか?私のクエリはwとoのみを使用します。明日もっとある場合はどうなりますか?この種のことを行う一般的な方法はありますか?

どんな助け/アドバイスも大歓迎です、

フィリップ

4

2 に答える 2

1

あなたがやろうとしていることはPIVOT、他のデータベースでは a と呼ばれています。残念ながら、MySQL にはこれがないため、集計関数とCASEステートメントを使用して作成する必要があります。

値が 2 つしかないことがわかっている場合はtest_type、これをハードコーディングできます。

select l.id,
  l.title,
  max(case when test_type = 'o' then 'true' else 'false' end) as o,
  max(case when test_type = 'w' then 'true' else 'false' end) as w
from lessons l
left join results r
  on l.id = r.lesson_id
  and r.usr_id = 100  -- the user id will change for each person
group by l.id, l.title

デモで SQL Fiddle を参照してください

これを動的に実行する場合、つまり転置する数が事前にわからないtest_types場合は、次の記事を確認する必要があります。

動的ピボット テーブル (行を列に変換)

コードは次のようになります。

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'MAX(CASE WHEN test_type = ''',
      test_type,
      ''' THEN ''true'' ELSE ''false'' END) AS ',
      test_type
    )
  ) INTO @sql
FROM Results;


SET @sql 
  = CONCAT('SELECT l.id,
      l.title, ', @sql, ' 
     from lessons l
     left join results r
       on l.id = r.lesson_id
       and r.usr_id = 100
     group by l.id, l.title');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

デモで SQL Fiddle を参照してください

于 2012-09-07T15:02:21.167 に答える
0

これは単純な集計クエリだと思います。集計は、lesson_id と title によって行われます。計算は、o と w の結果をピボットするだけです。

select l.lesson_id, l.title,
       max(case when r.test_type = 'o' then 'TRUE' else 'FALSE' end) as o,
       max(case when r.test_type = 'w' then 'TRUE' else 'FALSE' end) as w
from lessons l left outer join
     results r
     on l.lesson_id = r.lesson_id
where r.user_id = <whatever>
group by l.lesson_id, l.title
order by 1

このレベルでユーザーが選択するのは問題です。これは結果から取得する必要があり、左外部結合のために NULL になる可能性があるためです。問題を修正するバリエーションは次のとおりです。

select l.lesson_id, l.title,
       max(case when r.test_type = 'o' then 'TRUE' else 'FALSE' end) as o,
       max(case when r.test_type = 'w' then 'TRUE' else 'FALSE' end) as w
from lessons l left outer join
     results r
     on l.lesson_id = r.lesson_id and
        r.user_id = <whatever>
group by l.lesson_id, l.title
order by 1

条件を「on」句に移動することで、すべてのレッスンが保持されることが保証されます。

于 2012-09-06T21:06:58.527 に答える