私はテーブルの従業員がいます
id name salary city
1 ram 50000 c1
2 sham 20000 c2
3 jadu 80000 c1
4 madhu 90000 c4
5 hari 10000 c2
6 gopal 34000 c3
7 komal 55000 c3
8 bappa 98000 c4
クエリは、最も高い収入を得ている都市です。私は試した
SELECT city, SUM(salary) AS maxSalary
FROM employee GROUP BY city ORDER BY salary DESC LIMIT 1;
それは正常に動作しますが、最大収入都市が複数ある場合、他の最大都市は出力されず、最初の都市のみが出力されます。
だから私はこのクエリを試しました
SELECT city, MAX(totalSalary) maxSalary
FROM( SELECT city, SUM(salary) AS totalSalary FROM employee GROUP BY city ) AS tempTable
それは与えています
city max
c1 188000
しかし正しいです
city max
c4 188000
これは、c1
であるテーブルの最初の都市名を出力していますが、c4
である正しい最大収入都市名ではありません。正しいクエリは何ですか?
間違ったクエリをデータベースに送信しました。 manual で説明されているmysql拡張をヒットしました。次のようなクエリ
SELECT city, MAX(salary)
from employee
標準SQLでは機能しません。 Oracle (エラーメッセージ: "ORA-00937:単一グループグループ関数ではありません")でエラーを発生させます MSSqlServer 2012 (エラーメッセージ: "Column 'employee .city 'は、集約関数またはGROUP BY句のいずれにも含まれていないため、選択リストでは無効です ")または postgresql (エラーメッセージ:"エラー:列 "employee.city"が必要ですGROUP BY句で使用するか、集計関数で使用します」)。
選択リストの標準SQL式では、group by句でも使用される列の式と集計関数のみを使用できます。 group by句で使用される列の値は、これらのグループのすべての行で同じです。集計もグループごとに一意です。 selectは、グループに対してこの一意に定義された値を含むグループごとに1行を返します。
集約クエリにgroup by句がない場合、それらは行の1つのグループにすぎません。
標準では、その値が一意に定義されていないため、集計クエリの選択リストで任意の列を使用することは許可されていません。列はクエリの行に対して異なる値を持っているため、クエリによってこのグループに返される値はどれですか?
Mysqlには、group by句にない式(集約関数式ではない)の列がある場合、このグループのselectステートメントによって返される値が、このグループの任意の行。
したがって、クエリ
SELECT city, MAX(salary)
from employee
すべての従業員の給与の合計と、これらの行の1つの市を返します。ただし、クエリは集約クエリであり、従業員テーブルのすべてのレコードを含むグループが1つしかないため、正確に1つのreowを返します。
クエリ
SELECT city, MAX(salary)
from employee
group by city
各都市の都市と各都市の給与の行を返します。
クエリ
SELECT city, salary
from employee
group by city
この都市の任意の従業員のsalatyを持つ各都市の行を返します。サーバーは、都市のどの従業員を選択するかを決定します。
クエリ
SELECT city, zipcode
from employee
group by city
また、この都市の郵便番号を含む各都市の行を返します。すべての都市が1つの都市で定義された各グループの行よりも1つだけ郵便番号を持っていると仮定すると、同じ郵便番号が含まれます。そのため、行とは関係なく、サーバーは返された郵便番号を常に都市の郵便番号として選択します。
標準SQLでは、このクエリは次のように記述されます
SELECT city, zipcode
from employee
group by city, zipcode
期待される結果を生み出す
group by city,zipcode
およびgroup city
zipcdoeと都市の間の1対1の対応のため、同じグループを定義します。
クエリ
SELECT city, SUM(salary)
from employee
group by city
必要な行だけでなく、その他の行も含まれます。
このクエリから必要な行を除外する方法は次のとおりです
SELECT city, SUM(salary)
from employee
group by city
having SUM(salary) >= all (select SUM(salary) city_salary
from employee
group by city)
フィルタリングする他の方法があります。
最大の給与額は次のクエリで見つけることができます
select MAX(city_salary)
from (select SUM(salary) city_salary
from employee
group by city) tab
(注:Oracleでも同じことを実現できます。
select MAX(SUM(salary)) city_salary
from employee
group by city
)
これを使用して、必要なものを除外できます。
SELECT city, SUM(salary)
from employee
group by city
having SUM(salary) = (select MAX(city_salary)
from (select SUM(salary) city_salary
from employee
group by city) tab)
このソリューションは、@ Mihaiが comment ですでに提示しています。
グループ化基準を提供せずに、tempTable
結果セットに対してmax()
集約関数を使用しています。グループなしで集約関数を使用すると、すべての行が1つのグループと見なされ、結果は次のようになります。都市のような不定の順序は不定の順序で返されます。max()
は結果セットからの最大値を保証しますが、関連する列の値を保証しません(都市が関連する列の場合)。 c4の代わりに都市c1を取得しています。
ただし、tempTable
の結果セットの値と一致するようにすべての行に対してサブクエリを実行する必要があるため、あまり効率的ではないサブクエリを使用して回答を達成しました。クロス結合を使用することをお勧めします集計関数の結果を一致させるためにHAVING
を使用するサブクエリの代わりに副選択を使用
サンプルデータ
INSERT INTO employee
(`id`, `name`, `salary`, `city`)
VALUES
(1, 'ram', 50000, 'c1'),
(2, 'sham', 20000, 'c2'),
(3, 'jadu', 80000, 'c1'),
(4, 'madhu', 90000, 'c4'),
(5, 'hari', 10000, 'c2'),
(6, 'gopal', 34000, 'c3'),
(7, 'komal', 55000, 'c3'),
(8, 'bappa', 98000, 'c4')
;
クエリ
SELECT
e.city,
SUM(e.salary) AS totalSalary ,
e1.maxSalary
FROM employee e
CROSS JOIN (SELECT city, SUM(salary) AS maxSalary
FROM employee
GROUP BY city
ORDER BY maxSalary
DESC LIMIT 1
) e1
GROUP BY e.city
HAVING totalSalary = e1.maxSalary
結果の例
CITY TOTALSALARY MAXSALARY
c4 188000 188000
Fiddle Demo 1
副選択でクロス結合を使用する利点は、クロス結合のサブクエリが回答のサブクエリと比較して1回だけ評価され、クロス結合のサブ選択では、降順で並べることによって従業員の最大合計給与を取得するだけです。制限1の方法では、最高の給与合計が得られ、クエリの合計結果では、having句のクロス結合の副選択によって計算されたmaxsalaryが比較されます。
フィドルデモ1の最大給与は188000だったので、2人の従業員が同じ最大給与を持っている場合は、両方を返すので、(9, 'test', 188000, 'c5')
と同じ最高給与を持つ別の従業員がもう1行あるサンプルデータを少し変更しました。フィドルデモ2が同じクエリを使用して、最高の給与が同じ2人の従業員を返すかどうかを評価していることがわかります
サンプルデータ
INSERT INTO employee
(`id`, `name`, `salary`, `city`)
VALUES
(1, 'ram', 50000, 'c1'),
(2, 'sham', 20000, 'c2'),
(3, 'jadu', 80000, 'c1'),
(4, 'madhu', 90000, 'c4'),
(5, 'hari', 10000, 'c2'),
(6, 'gopal', 34000, 'c3'),
(7, 'komal', 55000, 'c3'),
(8, 'bappa', 98000, 'c4'),
(9, 'test', 188000, 'c5')
;
結果セットの例
CITY TOTALSALARY MAXSALARY
c4 188000 188000
c5 188000 188000
Fiddle Demo 2
私は答えを見つけたと思います。正解しない理由は、試してみたのと同じ理由
SELECT name, MAX(salary) FROM employee
これは機能しません。これはnameの最初のレコードのみを出力するためです。ただし、最大の給与が得られるため、給与フィールドと最大の給与を比較する必要があります。
SELECT name, salary FROM employee
WHERE salary = (SELECT MAX(salary) FROM employee)
だから私の質問の実際のクエリは
SELECT city, totalSalary
FROM( SELECT city, SUM(salary) AS totalSalary FROM employee GROUP BY city ) AS tempTable
WHERE totalSalary =
(SELECT MAX(totalSalary) FROM ( SELECT city, SUM(salary) AS totalSalary FROM employee GROUP BY city ) AS tempTable)
tempTableを再度宣言する必要があります。そうしないと、tempTableが存在しないと表示されます。