艰难的SQL排名查询

时间:2016-08-16 18:42:51

标签: sql-server partitioning rank

场景:主用户表以及跟踪用户名称更改的单独审计表。每次添加用户或编辑其部分名称时,我们都会向审计表写一行。

尝试编写一个提取最直接的前名称的查询

select nsh.AuthEmail, nsh.UserID, nsh.name_lastnamefirst, t.FormerName, t.RankOrder
from (
Select 
    an.AuditNameID, nsh.AuthEmail, nsh.UserID, nsh.name_lastnamefirst, 
    FormerName = CASE WHEN RTRIM(an.LastName) <> RTRIM(nsh.LastName) OR RTRIM(an.FirstName) <> RTRIM(nsh.FirstName) OR RTRIM(an.Suffix) <> RTRIM(nsh.Suffix) OR RTRIM(an.MaidenName)<>RTRIM(nsh.MaidenName) THEN LTRIM(an.LastName + ' ' + an.Suffix + ', ' + an.FirstName + ' ' + ISNULL(an.MiddleName,''))
    ELSE null       
    END,
    RANK() over (partition by an.UserID order by an.AuditNameID DESC) RankOrder
From [dbo].[AuditName] an
INNER JOIN dbo.StudentPrograms p ON an.UserID = p.UserID 
INNER JOIN dbo.NameScalarHelper nsh ON p.UserID = nsh.UserID 
WHERE p.SiteProgramID = 139 AND p.IsActive =1 
) t
RIGHT OUTER JOIN dbo.NameScalarHelper nsh ON nsh.UserID = t.UserID
where FormerName is not null

问题是我无法弄清楚如何从RANK为RANK -1的审计表中返回数据,因为最高等级是当前数据。如果有任何想法,请告诉我。

2 个答案:

答案 0 :(得分:1)

根据您的要求,您基本上需要返回与审核表中的当前名称不匹配的最新名称。

我认为您可以使用OUTER APPLY来实现这一目标:

SELECT *
FROM [dbo].[StudentPrograms] AS SP
INNER JOIN [dbo].[NameScalarHelper] AS NSH
  ON NSH.UserID = SP.UserID
OUTER APPLY (
  SELECT TOP (1) *
  FROM [dbo].[AuditName] AS AN
  WHERE AN.UserID = SP.UserID
    AND (
      RTRIM(AN.LastName) <> RTRIM(NSH.LastName)
      OR RTRIM(AN.FirstName) <> RTRIM(NSH.FirstName)
      OR RTRIM(AN.Suffix) <> RTRIM(NSH.Suffix)
      OR RTRIM(AN.MaidenName) <> RTRIM(NSH.MaidenName)
      )
  ORDER BY AuditNameID DESC
  ) AS AN
WHERE SP.SiteProgramID = 139
  AND SP.IsActive = 1;

这将从您的审核表中找到与最新名称不匹配的最新名称。

顺便说一下,我强烈建议您清理数据库并删除任何尾随/前导空格,这样您就不需要使用LTRIM()RTRIM()了您的where子句,以便SQL Server能够使用索引。有关详情,请参阅this文章。

SELECT *
FROM [dbo].[StudentPrograms] AS SP
INNER JOIN [dbo].[NameScalarHelper] AS NSH
  ON NSH.UserID = SP.UserID
OUTER APPLY (
  SELECT TOP (1) *
  FROM [dbo].[AuditName] AS AN
  WHERE AN.UserID = SP.UserID
    AND (
      AN.LastName <> NSH.LastName
      OR AN.FirstName <> NSH.FirstName
      OR AN.Suffix <> NSH.Suffix
      OR AN.MaidenNam) <> NSH.MaidenName
      )
  ORDER BY AuditNameID DESC
  ) AS AN
WHERE SP.SiteProgramID = 139
  AND SP.IsActive = 1;

我试图了解您存储数据的方式并复制了一个小例子:

DECLARE @User TABLE
(
  UserID INT
  , FirstName VARCHAR(50)
  , LastName VARCHAR(50)
);

DECLARE @Audit TABLE
(
  AuditID INT IDENTITY(1, 1)
  , UserID INT
  , FirstName VARCHAR(50)
  , LastName VARCHAR(50)
);

INSERT INTO @User (UserID, FirstName, LastName)
VALUES (1, 'Ben', 'White');

INSERT INTO @Audit (UserID, FirstName, LastName)
VALUES (1, 'Ben', 'White');

SELECT *
FROM @User AS U
OUTER APPLY (
  SELECT TOP (1) *
  FROM @Audit AS A
  WHERE A.UserID = U.UserID
    AND (
      A.FirstName <> U.FirstName
      OR A.LastName <> U.LastName
    )
  ORDER BY A.AuditID DESC
  ) AS A;

UPDATE U
SET U.LastName = 'Whiter'
FROM @User AS U
WHERE U.UserID = 1;

INSERT INTO @Audit (UserID, FirstName, LastName)
VALUES (1, 'Ben', 'Whiter');

SELECT *
FROM @User AS U
OUTER APPLY (
  SELECT TOP (1) *
  FROM @Audit AS A
  WHERE A.UserID = U.UserID
    AND (
      A.FirstName <> U.FirstName
      OR A.LastName <> U.LastName
    )
  ORDER BY A.AuditID DESC
  ) AS A;

UPDATE U
SET U.LastName = 'Whitest'
FROM @User AS U
WHERE U.UserID = 1;

INSERT INTO @Audit (UserID, FirstName, LastName)
VALUES (1, 'Ben', 'Whitest');

SELECT *
FROM @User AS U
OUTER APPLY (
  SELECT TOP (1) *
  FROM @Audit AS A
  WHERE A.UserID = U.UserID
    AND (
      A.FirstName <> U.FirstName
      OR A.LastName <> U.LastName
    )
  ORDER BY A.AuditID DESC
  ) AS A;

INSERT INTO @User (UserID, FirstName, LastName)
VALUES (2, 'Tom', 'Brooks');

INSERT INTO @Audit (UserID, FirstName, LastName)
VALUES (2, 'Tom', 'Brooks');

SELECT *
FROM @User AS U
OUTER APPLY (
  SELECT TOP (1) *
  FROM @Audit AS A
  WHERE A.UserID = U.UserID
    AND (
      A.FirstName <> U.FirstName
      OR A.LastName <> U.LastName
    )
  ORDER BY A.AuditID DESC
  ) AS A;

我假设您在创建用户时 - 还要将记录添加到Audit表以保持一致性。每次进行更新时 - 您还会将其记录到Audit表中。最后,我刚刚添加了另一个用户并运行了查询。

这是每个查询的输出:

用户已创建:

UserID FirstName LastName AuditID UserID FirstName LastName 
------ --------- -------- ------- ------ --------- -------- 
1      Ben       White    null    null   null      null  

及其&#39;姓氏第一次被更改:

UserID FirstName LastName AuditID UserID FirstName LastName 
------ --------- -------- ------- ------ --------- -------- 
1      Ben       Whiter   1       1      Ben       White    

及其&#39;姓氏第二次被更改:

UserID FirstName LastName AuditID UserID FirstName LastName 
------ --------- -------- ------- ------ --------- -------- 
1      Ben       Whitest  2       1      Ben       Whiter   

添加了新用户:

UserID FirstName LastName AuditID UserID FirstName LastName 
------ --------- -------- ------- ------ --------- -------- 
1      Ben       Whitest  2       1      Ben       Whiter   
2      Tom       Brooks   null    null   null      null    

其他一切只是格式化,你不应该在SQL Server中这样做 - 这应该在应用层完成。

答案 1 :(得分:0)

通过查看您的查询,我猜测您正在收回所有人的姓氏,因为您没有基于您已创建的RankOrder的过滤器。我认为你的当前名字应该是RankOrder中的1,所以你最近的名字将被排名为2.你可以将它添加到派生表的where where子句中:

    select nsh.AuthEmail, nsh.UserID, nsh.name_lastnamefirst, t.FormerName, t.RankOrder
from (
Select 
    an.AuditNameID, nsh.AuthEmail, nsh.UserID, nsh.name_lastnamefirst, 
    FormerName = CASE WHEN RTRIM(an.LastName) <> RTRIM(nsh.LastName) OR RTRIM(an.FirstName) <> RTRIM(nsh.FirstName) OR RTRIM(an.Suffix) <> RTRIM(nsh.Suffix) OR RTRIM(an.MaidenName)<>RTRIM(nsh.MaidenName) THEN LTRIM(an.LastName + ' ' + an.Suffix + ', ' + an.FirstName + ' ' + ISNULL(an.MiddleName,''))
    ELSE null       
    END,
    RANK() over (partition by an.UserID order by an.AuditNameID DESC) RankOrder
From [dbo].[AuditName] an
INNER JOIN dbo.StudentPrograms p ON an.UserID = p.UserID 
INNER JOIN dbo.NameScalarHelper nsh ON p.UserID = nsh.UserID 
WHERE p.SiteProgramID = 139 AND p.IsActive =1 and RankOrder = 2
) t
RIGHT OUTER JOIN dbo.NameScalarHelper nsh ON nsh.UserID = t.UserID
where FormerName is not null

如果我遗失了某些内容,请告诉我。