SQL Server:如何在给定一组项目的情况下选择倒数第二个条目?

时间:2016-07-05 21:40:12

标签: sql sql-server sql-server-2008

我有一个包含五列的表格:

  • mykey(INT pk)
  • 名(VARCHAR)
  • otherdata(VARCHAR)
  • groupID(INT)
  • date DATETIME
  • 有效(INT)

示例数据:

mykey   name        otherdata   groupID  date      active
----------------------------------------------------------
1      123_abc_nt   cat         1        6-6-16    0
2      123_abc_nt   dog         1        6-7-16    0
3      123_abc_nt   car         1        6-8-16    1
4      123_xyz_nt   red         2        6-9-16    0
5      123_xyz_nt   blue        2        6-10-16   1

此表格中的所有条目均按groupID和名称分组。换句话说,如果名称是123_abc_nt,则该组将是" #insertgroupidhere"。由于使用此表的过程的性质(我不能以任何方式改变),对" name"的任何更改都是如此。或"其他数据"列将生成一个新行。

所以说用户更改第5行"其他数据"从bluegreen从使用此表的单独应用程序。在这种情况下,该单独的应用程序将生成行:6, 123_xyz_nt, green并将旧列标记为无效0

mykey   name        otherdata   groupID  date      active
-----------------------------------------------------------
1      123_abc_nt   cat         1        6-6-16    0
2      123_abc_nt   dog         1        6-7-16    0
3      123_abc_nt   car         1        6-8-16    1
4      123_xyz_nt   red         2        6-9-16    0
5      123_xyz_nt   blue        2        6-10-16   0 <--- old row deactivated
6      123_xyz_nt   green       2        6-11-16   1 <--- new row inserted and activated

表格中任何列的更改都会发生这种情况。因此,如果我将名称从_nt更改为_rt,则表格将如下所示:

mykey   name        otherdata   groupID  date      active
---------------------------------------------------------
1      123_abc_nt   cat         1        6-6-16    0
2      123_abc_nt   dog         1        6-7-16    0
3      123_abc_nt   car         1        6-8-16    1
4      123_xyz_nt   red         2        6-9-16    0
5      123_xyz_nt   blue        2        6-10-16   0 
6      123_xyz_nt   green       2        6-11-16   0 <--- old row deactivated
7      123_xyz_rt   green       2        6-12-16   1 <--- new row inserted and activated

我需要编写一个查询,专门检测从_nt_rt的此名称更改(反之亦然,从_rt_nt)并返回 active 行。所以在最后一种情况下,我的查询需要返回7, 123_xyz_rt, green, 2, 1。此外,它需要忽略任何其他列更改,并仅检测nt / rt名称列更改。可能会出现我同时更改多行的情况,我的查询需要检测到这一点。

示例:

mykey   name        otherdata   groupID  date      active
----------------------------------------------------------
1      123_abc_nt   cat         1        6-6-16    0
2      123_abc_nt   dog         1        6-7-16    0
3      123_abc_nt   car         1        6-8-16    0  <-- deactivated
4      123_xyz_nt   red         2        6-9-16    0
5      123_xyz_nt   blue        2        6-10-16   0 
6      123_xyz_nt   green       2        6-11-16   0 
7      123_xyz_rt   green       2        6-12-16   0  <-- deactivated
8      123_xyz_nt   green       2        6-13-16   1  <-- activated
9      123_abc_rt   car         1        6-13-16   1  <-- activated (note the groupID)

我一直试图确定这样做的查询,但这非常困难。我无法在此数据库中的表上使用触发器,因此需要严格基于查询。以下是我的尝试:

我有一个临时表来捕获任何新插入的行:

DECLARE @NEWROWS_TEMP TABLE(mykey, name, active)

INSERT INTO @NEWROWS_TEMP
--PROCESS TO CATCH NEW ROWS....

然后使用该列表,我试图在while循环中找到已更改名称的行:

--Loop Variables:
DECLARE @TOTALCOUNT INT = (SELECT COUNT(*) FROM @TEMP)
DECLARE @NAME_INVERTED VARCHAR(MAX)
DECLARE @NAME VARCHAR(MAX)
DECLARE @LOOPNUM INTEGER = 1
DECLARE @CURRENT_KEY INT = 0

--Loop to find entries that changed from (nt->rt) OR (rt->nt):
WHILE ((SELECT COUNT(*) FROM @TEMP) > 0 AND (SELECT @LOOPNUM) <= @TOTALCOUNT)
BEGIN 
    SET @CURRENT_KEY = (SELECT TOP 1 mykey FROM @TEMP)
    SET @NAME = (SELECT name
                FROM @TEMP 
                WHERE CLASSIC_KEY = @CURRENT_KEY)
    SET @NAME_INVERTED = (SELECT CASE WHEN (CHARINDEX('_rt_',@NAME)  > 0)
                        THEN 
                            REPLACE(@NAME,'_rt_', '_nt_')
                        ELSE 
                            CASE WHEN (CHARINDEX('_nt_',@NAME)  > 0)
                            THEN 
                                REPLACE(@NAME,'_nt_', '_rt_')
                            ELSE
                                NULL
                                END
                        END)

    IF(EXISTS(
                SELECT TOP 1 mykey
                FROM mytable
                WHERE name = @NAME_INVERTED AND 
                active = 0)
             )
        BEGIN
            INSERT INTO @RESULT_LIST
            SELECT @CURRENT_KEY
        END 

    DELETE FROM @TEMP WHERE mykey = @CURRENT_KEY
    SET @LOOPNUM = @LOOPNUM +1
END

除了行名称从未改变但otherdata确实存在并且存在过去的nt-rt变化的情况之外,这是有效的。该循环只是跳过了行的名称没有改变的事实。

例如,如果我这样做:

mykey   name        otherdata   groupID  date      active
----------------------------------------------------------
1      123_abc_nt   cat         1        6-6-16    0
2      123_abc_nt   dog         1        6-7-16    0
3      123_abc_nt   car         1        6-8-16    0  
4      123_xyz_nt   red         2        6-9-16    0
5      123_xyz_nt   blue        2        6-10-16   0 
6      123_xyz_nt   green       2        6-11-16   0 
7      123_xyz_rt   green       2        6-12-16   0  
8      123_xyz_nt   green       2        6-13-16   1  
9      123_abc_rt   car         1        6-13-16   0  
10     123_abc_rt   bike        1        6-13-16   1 <-- Name did not actually change. Otherdata did, but current query picks this up anuway since there once was a change from "123_abc_nt" to "123_abc_rt" 

是否有更简单的方法可以在不使用循环的情况下获得类似的结果?我的方法证明是非常不准确和难以维护的。

1 个答案:

答案 0 :(得分:1)

这应返回预期结果,如果不同,请使用active=0检查上一行的名称:

select * from mytable as t1
where active = 1
and name <>
 ( select top 1 name
   from mytable as t2
   where t1.groupID = t2.groupID
     and active = 0
   order by t2.date desc
 )