web-dev-qa-db-ja.com

サブクエリがスーパークエリの結合から列を見つけることができません

SQLで問題が発生しています。基本的に、従業員に尋ねられたすべての質問の合計を含む結果セットを取得しようとしています(会社ごとにグループ化されています)。また、手動で追加された項目である「onetime_items」を別のテーブル。

私は現在このSQLステートメントを持っています(私はMySQLを使用しています):

SELECT 
CONCAT_WS(
    ', ', count(DISTINCT CONCAT(emailaddress, '_', e.id)),
    (
        SELECT GROUP_CONCAT(items SEPARATOR '; ') as OneTimeItems
        FROM ( 
            SELECT CONCAT_WS(
                ': ', oi.item_name, SUM(oi.item_amount)
            ) items 
            FROM onetime_item oi 
            WHERE oi.company_id = e.company_id
            AND oi.date BETWEEN '2015-12-01'
            AND LAST_DAY('2015-12-01') 
            GROUP BY oi.item_name 
        ) resulta
    )
) as AllItems,
e.id,
LEFT(e.firstname, 1) as voorletter,
e.lastname
FROM question q 
LEFT JOIN employee e ON q.employee_id = e.id 
WHERE 1=1 
AND YEAR(created_at) = '2015'
AND MONTH(created_at) = '12' 
GROUP BY e.company_id

今、私は次のエラーを受け取ります:

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'e.company_id' in where clause

使用される日付はダミーの日付であり、下部のwhere句の1 = 1は、ユーザー入力値に基づいてwhereステートメントを生成しているためです。

すべての列はテーブルemployeeに存在し、左の結合は機能します(列参照を使用する代わりに手動でIDを入力してみましたが、機能しました。正しい結果が返されました)

また、申し訳ありませんが、テーブルスキーマやDB構造に関連するものをオンラインで投稿することはできません。

E.company_idへの参照が失敗する理由について何か考えはありますか?

編集:これは私が必要とする結果セットです:

5, Het is je verjaardag: 1; Skivakantie: 1; Telefonische leadvergoeding: 8

構成は次のとおりです。5 =残りを取得するためにリードカンマで区切ります。リードは、質問テーブルの一意の組み合わせであり、残りの結果はonetime_itemsから作成されます。

EDIT 2:SQLフィドルでエラーが発生し続けるので、作成した小さなデータを含む小さな作成したDBを投稿します: http://Pastebin.com/cCveVtGr

7
FMashiro

問題の原因は コメントの@Phil によって特定されました:

おそらくネストが深すぎるためです

ネストが2層あり、テーブルeの参照はMySQLでこれらの2層を「見る」ことができません。

通常、相関インラインサブクエリは派生テーブルに変換してから、LEFTFROM句で他のテーブルに結合できますが、それらは非相関に変換する必要があります(MySQLでは。他のDBMSでは、 LATERAL結合または同様の_OUTER APPLY_を使用します。

ジョブを完了するための最初の書き換え:

_SELECT 
    CONCAT_WS(
        ', ', count(DISTINCT CONCAT(q.emailaddress, '_', e.id)),
        dv.OneTimeItems
    ) as AllItems,
    e.id,
    LEFT(e.firstname, 1) as voorletter,
    e.lastname
FROM question q 
LEFT JOIN employee e ON q.employee_id = e.id 
LEFT JOIN
    (
        SELECT company_id,
               GROUP_CONCAT(items SEPARATOR '; ') AS OneTimeItems
        FROM ( 
            SELECT oi.company_id,
                   CONCAT_WS(
                ': ', oi.item_name, SUM(oi.item_amount)
            ) items 
            FROM onetime_item oi 
            WHERE oi.date BETWEEN '2015-12-01'
                              AND LAST_DAY('2015-12-01') 
            GROUP BY oi.company_id, oi.item_name 
        ) resulta
        GROUP BY company_id
    ) AS dv
    ON dv.company_id = e.company_id
WHERE 1=1 
AND YEAR(q.created_at) = '2015'
AND MONTH(q.created_at) = '12' 
GROUP BY e.company_id ;
_

SQLfiddleでテストします。


問題のコメントとは無関係:

  • _GROUP BY e.company_id_がありますが、selectリストにはe.id, LEFT(e.firstname, 1), e.lastnameがあります。これらはすべて、各会社の(多かれ少なかれランダムな)従業員からの任意の結果をもたらします-または非常にまれなケースでさえ、2人または3人の異なる従業員からの任意の結果をもたらします! MySQLは(5.7より前では)このようなグループの不適切な使用を許可し、誤った結果を引き起こす可能性がありました。これは5.7で修正されており、デフォルト設定ではこのクエリが拒否されます。
  • 状態:

    _YEAR(created_at) = '2015' AND MONTH(created_at) = '12'
    _

    インデックスを使用できません。列がBETWEENタイプの包括的および排他的範囲条件である場合、DATEのいずれかで書き換えることをお勧めします。これは、任意の日時タイプ(DATEDATETIMETIMESTAMP)任意の精度:

    _-- use only when the type is DATE only
    date BETWEEN '2015-12-01' AND LAST_DAY('2015-12-01')
    _

    または:

    _-- use when the type is DATE, DATETIME or TIMESTAMP
    created_at >= '2015-12-01' AND created_at < '2016-01-01'     
    _
4
ypercubeᵀᴹ