web-dev-qa-db-ja.com

SQLでn番目に高い給与を見つける方法は?

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の完全な初心者です。助けてください!

6
thinker2305

ここで起こっているのは、サブクエリが個々の給与を調べ、基本的にそれらをランク付けしてから、それらの給与を外部給与と比較することです(同じテーブルに対する個別のクエリ)。したがって、この場合、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
_
18
Aaron Bertrand

このクエリを作成する別の方法は、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

6
ypercubeᵀᴹ

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です。

4
Jehad Keriaki

ロジックはとてもシンプルです。同じテーブルの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
              )
2

提案されたMySQLクエリ

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クエリが実行されました(5番目に大きい)

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クエリが実行されました(3番目に大きい)

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>

試してみる !!!

1
RolandoMySQLDBA
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番目を実行できるようにしました。

0
Eric DeMott