SQL Server: эквивалент CROSS APPLY для запроса ROW_NUMBER () - PullRequest
0 голосов
/ 11 октября 2018

Я пытаюсь найти другой способ написать запрос ROW_NUMBER(), используя CROSS APPLY, чтобы я мог сравнить производительность.

В приведенном ниже простом примере создается таблица сотрудников, вставляются некоторые тестовые данные и используется SELECT с оконной функцией ROW_NUMBER() для поиска сотрудников в каждом отделе с самым длительным сроком пребывания.

Есть ли другой способ написать SELECT, используя CROSS APPLY?

CREATE TABLE [dbo].[tblEmployee]
(
    [ID] [INT] NOT NULL,
    [DeptID] [TINYINT] NOT NULL,
    [EmpName] [VARCHAR](100) NOT NULL,
    [Tenure] [TINYINT] NOT NULL,

    CONSTRAINT [PK_tblEmployee] 
        PRIMARY KEY CLUSTERED ([ID] ASC)
) ON [PRIMARY]
GO

INSERT INTO dbo.tblEmployee (ID, DeptID, EmpName, Tenure) 
VALUES ('1', '1', 'John', 2),
       ('2', '1', 'Mary', 5),
       ('3', '2', 'Joe', 3),
       ('4', '3', 'Bill', 10),
       ('5', '2', 'Marilynn', 9),
       ('6', '3', 'Sue', 7);

SELECT 
    EmpName, DeptID, Tenure 
FROM  
    (SELECT 
         EmpName, DeptID, Tenure, 
         ROW_NUMBER() OVER(PARTITION BY DeptID ORDER BY Tenure DESC) TenureRank
     FROM 
         tblEmployee) e 
WHERE 
    e.TenureRank = 1
ORDER BY 
    DeptID

EDIT : Я бы предпочел не использовать CTE как частьSELECT

Ответы [ 4 ]

0 голосов
/ 11 октября 2018

Я попробовал это, получилось в 4 раза медленнее, чем оригинал

SELECT A.EmpName, A.DeptID, A.Tenure FROM tblEmployee A 
               CROSS APPLY 
                    (SELECT TOP 1 B.ID FROM tblEmployee B 
                          WHERE B.DeptID = A.DeptID Order By Tenure Desc) CA
WHERE CA.ID = A.ID;
0 голосов
/ 11 октября 2018

Вы, вероятно, имеете лучший источник для идентификаторов отделов, чем сканирование всей таблицы tblEmployee:

SELECT a.* 
FROM (SELECT DISTINCT DeptID FROM tblEmployee) d
CROSS APPLY (
    SELECT TOP 1 EmpName, DeptID, Tenure
    FROM tblEmployee e
    WHERE e.DeptID = d.DeptID
    ORDER BY Tenure DESC
) a

Этот конкретный пример вряд ли будет поддерживать подход APPLY, так как вам также нужно придуматьисходные значения.

APPLY может иметь больше смысла, когда вы работаете над запросом, в котором у вас уже есть исходные данные (скажем, вам также требовалось название отдела, и вы, естественно, начали с воображаемой таблицы tblDepartment),Это также может быть полезно, когда вам нужно получить информацию такого типа из более чем одного места (гораздо проще иметь более одного APPLY в запросе, чем ROW_NUMBER ()), или когда операция APPLY сама по себе является более сложным подзапросом., глядя в несколько таблиц.

0 голосов
/ 11 октября 2018

Вы можете сделать это без использования CROSS APPLY или ROWNUM.Вы можете создать производную таблицу, в которой указан максимальный срок владения для каждого отдела, а затем присоединить ее к базовой таблице сотрудников.Используя CTE, ваш запрос будет выглядеть так:

WITH 
    DeptWithMaxTenure AS
    (
        SELECT DeptID, Max(Tenure) AS MaxTenure
        FROM tblEmployee
        GROUP BY DeptID
    )
SELECT
    tblEmployee.EmpName,
    tblEmployee.DeptID,
    tblEmployee.Tenure
FROM tblEmployee
    INNER JOIN DeptWithMaxTenure ON
        DeptWithMaxTenure.DeptID = tblEmployee.DeptID
        AND DeptWithMaxTenure.MaxTenure = tblEmployee.Tenure
0 голосов
/ 11 октября 2018

Эквивалент cross apply будет:

select e.*, a.seqnum
from tblEmployee e cross apply
     (select count(*) as seqnum
      from tblEmployee e2
      where e2.deptid = e.deptid and
            (e2.tenure > e.tenure or
             e2.tenure = e.tenure and e2.id >= e.id
            )
     ) a;

Вы не захотите этого делать, потому что это намного, намного менее эффективно, чем row_number().Обратите внимание на использование id для сравнения, чтобы обеспечить уникальные числа.

...