Employeeテーブルからn番目に高い給与を検索するクエリを見つけましたが、(N-1)のロジックがわかりませんか?
EmpID Salary
1 90000
2 80000
3 54000
4 37000
5 12000
6 69000
7 50000
SELECT * FROM Employee E1
WHERE (N-1) = (
SELECT COUNT(DISTINCT(E2.Salary))
FROM Employee E2
WHERE E2.Salary > E1.Salary
)
N = 4の場合、クエリはどのように機能しますか?私はSQLの完全な初心者です。助けてください!
ここで起こっているのは、サブクエリが個々の給与を調べ、基本的にそれらをランク付けしてから、それらの給与を外部給与と比較することです(同じテーブルに対する個別のクエリ)。したがって、この場合、N = 4と言った場合、次のようになります。
_WHERE 3 = (number of salaries > outer salary)
_
だからあなたが持っているデータを見て、それらを順番にランク付けして比較しましょう。
_EmpID Salary How many *distinct* salaries are greater than this one?
----- ------ -------------------------------------------------------
5 12000 6
4 37000 5
7 50000 4
3 54000 3
6 69000 2
2 80000 1
1 90000 0
_
したがって、n = 4の場合、返される行はEmpID 3(54000)です。
私の意見では、このクエリを書くためのはるかに直感的な方法は、RANK()
、ROW_NUMBER()
またはDENSE_RANK()
などのウィンドウ関数を使用することです(あなたが関係が欲しい)。これらのさまざまな関数がデータに対してどのように機能するかを見てみましょう(4番目の順位を表すために8行目を追加しました)。
_DECLARE @salary TABLE(EmpID INT, Salary INT);
INSERT @salary VALUES
(1,90000),(2,80000),(3,54000),(4,37000),
(5,12000),(6,69000),(7,50000),(8,54000);
;WITH x AS
(
SELECT EmpID, Salary,
r = RANK() OVER (ORDER BY Salary),
dr = DENSE_RANK() OVER (ORDER BY Salary),
rn = ROW_NUMBER() OVER (ORDER BY Salary)
FROM @salary
)
SELECT EmpID, Salary, r, dr, rn FROM x;
_
結果:
_EmpID Salary r dr rn
----- ------ -- -- --
5 12000 1 1 1
4 37000 2 2 2
7 50000 3 3 3
8 54000 4 4 4
3 54000 4 4 5
6 69000 6 5 6
2 80000 7 6 7
1 90000 8 7 8
_
この特定の問題にはRANK()
を使用したくないと思います。たとえば、5番目の場所は機能しないためです。したがって、同点の場合に複数の行を含めるかどうか、そうでない場合は、任意の行が必要か、またはいくつかの基準に基づいて特定の行が必要かが決まります。したがって、ステートメントを少し調整します。
_-- if you want ties:
;WITH x AS
(
SELECT EmpID, Salary,
dr = DENSE_RANK() OVER (ORDER BY Salary)
FROM @salary
)
SELECT EmpID, Salary FROM x WHERE dr = 4;
-- results:
-- 3 54000
-- 8 54000
-- to take the *lowest* EmpID:
;WITH x AS
(
SELECT EmpID, Salary,
rn = ROW_NUMBER() OVER (ORDER BY Salary, EmpID)
FROM @salary
)
SELECT EmpID, Salary FROM x WHERE rn = 4;
-- results:
-- 3 54000
-- to take the *highest* EmpID:
;WITH x AS
(
SELECT EmpID, Salary,
rn = ROW_NUMBER() OVER (ORDER BY Salary, EmpID DESC)
FROM @salary
)
SELECT EmpID, Salary FROM x WHERE rn = 4;
-- results:
-- 8 54000
_
このクエリを作成する別の方法は、2012 + OFFSET / FETCH
N番目の給与を検索する構文:
; WITH Nth AS -- To find the Nth highest salary,
(
SELECT DISTINCT Salary -- get all the distinct salary values
FROM Employee
ORDER BY Salary DESC -- order them from high to low
OFFSET 3 ROWS -- skip (N-1) values
FETCH NEXT 1 ROWS ONLY -- and keep the next one (Nth).
)
SELECT EmpID, Salary -- Then show
FROM Employee -- all employees that have
WHERE Salary = (SELECT Salary FROM Nth) ; -- have a salary equal to that.
または2012年より前のバージョンの場合、2つのステップで。最初にDESC
、次にASC
の順に並べます。
; WITH TopN AS -- Find the top N salaries,
(
SELECT DISTINCT TOP (4) Salary
FROM Employee
ORDER BY Salary DESC
),
Nth AS -- then keep only the Nth one,
(
SELECT TOP (1) Salary
FROM TopN
ORDER BY Salary
)
SELECT EmpID, Salary -- and show
FROM Employee -- all employees that have
WHERE Salary = (SELECT Salary FROM Nth) ; -- have a salary equal to that.
テスト SQLfiddle
N = 4の場合、4-1 = 3高い給与がある給与を返します。つまり、4番目に高い給与を返します。
例:
Salaries (500, 400, 400, 300, 250, 200).
望ましい結果は(250)です(DISTINCT
により、4番目は「400」を1回だけ数えるため)。 N-1 = 3は、250を超える3つの異なる給与、つまり(500、400、300)があることを意味します。
給与の繰返しがない場合の例では、望ましい結果は(5400)で、4番目に高いです。そのため、クエリは給与を返します。給与の数が多いほうが4-1です。
ロジックはとてもシンプルです。同じテーブルの2つのインスタンスを取得しています。サブクエリの2番目。メインテーブルの最初の給与を選択し、サブクエリテーブルのすべての給与と比較して、検討中のメインテーブルの給与よりも多い給与の数を取得します。カウントがN-1の場合。この場合、メインテーブルの給与はN-1の給与であるため、最大給与はN番目であることを意味します。
SELECT * FROM Employee E1
WHERE (N-1) = (
SELECT COUNT(DISTINCT(E2.Salary))
FROM Employee E2
WHERE E2.Salary > E1.Salary
)
5番目に大きい給与を取得するには
SET @nth = 5;
SET @ndx = 0;
SELECT @nth nth,EmpID,salary FROM
(
SELECT (@ndx:=@ndx+1) ndx,EmpID,salary
FROM employee ORDER BY salary DESC
) A WHERE ndx = @nth;
DROP DATABASE IF EXISTS thinker2305;
CREATE DATABASE thinker2305;
USE thinker2305
CREATE TABLE employee
(EmpID int not null auto_increment primary key,
salary int not null);
INSERT INTO employee (salary) values
(90000),(80000),(54000),(37000),
(12000),(69000),(50000);
SELECT * FROM employee ORDER BY salary DESC;
mysql> DROP DATABASE IF EXISTS thinker2305;
Query OK, 1 row affected (0.03 sec)
mysql> CREATE DATABASE thinker2305;
Query OK, 1 row affected (0.00 sec)
mysql> USE thinker2305
Database changed
mysql> CREATE TABLE employee
-> (EmpID int not null auto_increment primary key,
-> salary int not null);
Query OK, 0 rows affected (0.03 sec)
mysql> INSERT INTO employee (salary) values
-> (90000),(80000),(54000),(37000),
-> (12000),(69000),(50000);
Query OK, 7 rows affected (0.00 sec)
Records: 7 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM employee ORDER BY salary DESC;
+-------+--------+
| EmpID | salary |
+-------+--------+
| 1 | 90000 |
| 2 | 80000 |
| 6 | 69000 |
| 3 | 54000 |
| 7 | 50000 |
| 4 | 37000 |
| 5 | 12000 |
+-------+--------+
7 rows in set (0.00 sec)
mysql>
mysql> SET @nth = 5;
Query OK, 0 rows affected (0.00 sec)
mysql> SET @ndx = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @nth nth,EmpID,salary FROM
-> (
-> SELECT (@ndx:=@ndx+1) ndx,EmpID,salary
-> FROM employee ORDER BY salary DESC
-> ) A WHERE ndx = @nth;
+------+-------+--------+
| nth | EmpID | salary |
+------+-------+--------+
| 5 | 7 | 50000 |
+------+-------+--------+
1 row in set (0.00 sec)
mysql>
mysql> SET @nth = 3;
Query OK, 0 rows affected (0.00 sec)
mysql> SET @ndx = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @nth nth,EmpID,salary FROM
-> (
-> SELECT (@ndx:=@ndx+1) ndx,EmpID,salary
-> FROM employee ORDER BY salary DESC
-> ) A WHERE ndx = @nth;
+------+-------+--------+
| nth | EmpID | salary |
+------+-------+--------+
| 3 | 6 | 69000 |
+------+-------+--------+
1 row in set (0.00 sec)
mysql>
Declare @nth varchar(4) = '10' ;
Declare @inner varchar(max) =
'Select top ' + @nth + ' * from employee order by salary desc'
Declare @outer varchar(max) =
'Select top 1 * from (' + @inner +') a order by salary';
--exec (@inner)
exec (@outer);
これは私がTSQLでこれにアプローチした方法です。順序をパラメタライズすることについても半ば考えました。それにより、順序が反転して上位n番目または下位n番目を実行できるようにしました。