0

バックグラウンド

MySQL には、さまざまな種類のフィードバック項目を含む 2 つのテーブルがあります。これらのテーブルを組み合わせてFULL OUTER JOIN(実際には、MySQL では 2 つの結合とユニオンとして記述されています)、平均成績を数えるクエリを作成しました。このクエリは完全に機能するようです:

  (SELECT name, AVG(l.overallQuality) AS avgLingQual,
    AVG(s.overallSatisfaction) AS avgSvcQual
  FROM feedback_linguistic AS l
  LEFT JOIN feedback_service AS s USING(name)
  GROUP BY name)
UNION ALL
  (SELECT name, AVG(l.overallQuality) AS avgLingQual, 
    AVG(s.overallSatisfaction) AS avgSvcQual
  FROM feedback_linguistic AS l
  RIGHT JOIN feedback_service AS s USING(name)
  WHERE l.id IS NULL
  GROUP BY name)
ORDER BY name;

(これは読みやすくするためにいくらか単純化されていますが、ここでは違いはありません)

問題

次に、日付によるフィルタリングを追加してみました (つまり、特定の日付以降に作成されたフィードバック項目のみが考慮されます)。私のSQLスキルと私が行った調査により、私はこれを思いつくことができました:

  (SELECT name, AVG(l.overallQuality) AS avgLingQual,
    AVG(s.overallSatisfaction) AS avgSvcQual
  FROM feedback_linguistic AS l
  LEFT JOIN feedback_service AS s USING(name)
  WHERE (s.createdTime >= '" & date & "' OR s.createdTime IS NULL)
    AND (l.createdTime >= '" & date & "' OR l.createdTime IS NULL)
  GROUP BY name)
UNION ALL
  (SELECT name, AVG(l.overallQuality) AS avgLingQual, 
    AVG(s.overallSatisfaction) AS avgSvcQual
  FROM feedback_linguistic AS l
  RIGHT JOIN feedback_service AS s USING(name)
  WHERE l.id IS NULL
    AND (s.createdTime >= '" & date & "' OR s.createdTime IS NULL)
  GROUP BY name)
ORDER BY name;

これはほとんど機能します。得られる結果はほぼ正しいように見えます。ただし、いくつかのフィードバック項目がありません。たとえば、1 か月前の日付を設定すると、データベース内の 21 人の異なる人のフィードバックを数えましたが、このクエリでは 19 人しか返されません。最悪のことは、欠落しているアイテムの間に類似点が見つからないように見えることです.

このクエリで何か間違っていますか? WHEREこの句は、の後に日付のフィルタリングを行うと思いますJOIN。理想的には、おそらく前に行うことになるでしょう。繰り返しますが、これが私の問題の原因であるかどうかはわかりません。また、このクエリを別の方法で記述する方法もわかりません。

4

2 に答える 2

2

Johans の回答を受け入れました。彼はこのことを私に説明してくれました。その回答は、より一般的な意味でも役立ちます。ただし、最初にたどり着いた解決策も投稿すると思いました。サブクエリを使用していました:

  (SELECT name, AVG(l.overallQuality) AS avgLingQual,
    AVG(s.overallSatisfaction) AS avgSvcQual
  FROM (
    SELECT * FROM feedback_linguistic WHERE createdTime >= '" & date & "'
  ) AS l
  LEFT JOIN (
    SELECT * FROM feedback_service WHERE createdTime >= '" & date & "'
  ) AS s USING(name)
  GROUP BY name)
UNION ALL
  (SELECT name, AVG(l.overallQuality) AS avgLingQual, 
    AVG(s.overallSatisfaction) AS avgSvcQual
  FROM (
    SELECT * FROM feedback_linguistic WHERE createdTime >= '" & date & "'
  ) AS l
  RIGHT JOIN (
    SELECT * FROM feedback_service WHERE createdTime >= '" & date & "'
  ) AS s USING(name)
  WHERE l.id IS NULL
  GROUP BY name)
ORDER BY name;

このクエリの結果は正しいです。ただし、私の経験ではサブクエリが遅い場合があるため、ソリューションは実際には最適に見えません。繰り返しになりますが、パフォーマンス分析は行っていないので、ここでサブクエリを使用してもボトルネックにはならないかもしれません。いずれにせよ、私のアプリケーションでは十分に高速に動作しました。

于 2011-10-29T08:48:46.810 に答える
1

完全外部結合は、次の 3 つの結合の組み合わせです。

1- A と B の間の内部結合
2- A と B の間の左除外結合
3- A と B の間の右除外結合

内部除外結合と左除外結合の組み合わせは左外部結合であるため、通常はクエリをleft outer join+として書き直すことに注意してくださいright exclusion join
ただし、デバッグ目的では、3 つの結合すべてに役立ち、unionどの結合が何を行うかについてのマーカーを追加できます。

  /*inner join*/
  (SELECT
     'inner' as join_type 
     , COALESCE(s.name, l.name) as listname
     , AVG(l.overallQuality) AS avgLingQual
     , AVG(s.overallSatisfaction) AS avgSvcQual 
  FROM feedback_linguistic l 
  INNER JOIN feedback_service s ON (l.name = s.name) 
  WHERE (s.createdTime >= '" & date & "' OR s.createdTime IS NULL) 
    AND (l.createdTime >= '" & date & "' OR l.createdTime IS NULL) 
  GROUP BY l.name) 
UNION ALL
  (SELECT
     'left exclusion' as join_type 
     , COALESCE(s.name, l.name) as listname
     , AVG(l.overallQuality) AS avgLingQual
     , AVG(s.overallSatisfaction) AS avgSvcQual 
  FROM feedback_linguistic l 
  LEFT JOIN feedback_service s ON (l.name = s.name) 
  WHERE s.id IS NULL
    /*AND (s.createdTime >= '" & date & "' OR s.createdTime IS NULL) */
    AND (l.createdTime >= '" & date & "' OR l.createdTime IS NULL) 
  GROUP BY l.name) 
UNION ALL
  (SELECT 
     'right exclusion' as join_type
     , COALESCE(s.name, l.name) as listname
     , AVG(l.overallQuality) AS avgLingQual 
     , AVG(s.overallSatisfaction) AS avgSvcQual 
  FROM feedback_linguistic l 
  RIGHT JOIN feedback_service s ON (s.name = l.name) 
  WHERE l.id IS NULL
    AND (s.createdTime >= '" & date & "' OR s.createdTime IS NULL) 
    /*AND (l.createdTime >= '" & date & "' OR l.createdTime IS NULL) */
  GROUP BY s.name) 
ORDER BY listname; 

WHERE 句は JOIN の後に日付のフィルタリングを行うと思いますが、理想的には、おそらく前に行うことになるでしょう。

前にフィルタリングを行いたい場合は、結合句に入れます。

于 2011-10-27T11:56:17.447 に答える