根据SQL Server中多个先前行的值提取行

ali*_*ali 6 sql t-sql

我有一个包含这样的示例数据的表:

ID  Key   User
--  ----  -----
1   a     test
2   ab    test
3   abc   test
4   abcd  test
5   e     test1
6   ef    test1
7   efg   test1
8   efgh  test1
9   t     test1
10  ty    test1
11  tyu   test1
12  tyui  test1
Run Code Online (Sandbox Code Playgroud)

数据由用户构造的值的顺序"快照"组成.我想为每个用户实例返回最后一行,构建一个不同的最终Key值.注意大多数行Key包含整个前一行Key加上一个额外的字母?我只想要终止这样一个序列的行,并且是每个链中可能的最长值,Keys它们依次包含前一个Key值.

以上示例数据应返回以下内容:

ID  Key   User
--  ----  -----
4   abcd  test
8   efgh  test1
12  tyui  test1
Run Code Online (Sandbox Code Playgroud)

我该怎么做呢?

Eri*_*ikE 6

如果没有我的问题的答案,我必须做出这些假设:

  • ID列表示年表,并且总是增加一个,没有间隙.
  • SQL Server 2005或更高版本

(更新:我做了一个小调整,使得这个工作与来自不同用户的"交错"数据一起工作,并添加了一些交错和一些棘手的数据给我的小提琴.)

所以这是我的解决方案.在SqlFiddle中看到它.值得注意的是,它模拟了LEADSQL Server 2012中没有的分析JOIN.

WITH Info AS (
  SELECT
     Grp = Row_Number() OVER (PARTITION BY UserName ORDER BY ID, Which) / 2,
     *
  FROM
     dbo.UserEntry U
     CROSS JOIN (
        SELECT 1 UNION ALL SELECT 2
     ) X (Which)
)
SELECT
   ID = Max(V.ID),
   DataKey = Max(V.DataKey),
   UserName = Max(V.UserName)
FROM
   Info I
   OUTER APPLY (SELECT I.* WHERE Which = 2) V
WHERE I.Grp > 0
GROUP BY
   I.UserName,
   I.Grp
HAVING
   Max(I.DataKey) NOT LIKE Min(I.DataKey) + '_';
Run Code Online (Sandbox Code Playgroud)

输入:

INSERT dbo.UserEntry (ID, DataKey, UserName)
VALUES
(1, 'a', 'test'),
(2, 'ab', 'test'),
(3, 'e', 'test1'),
(4, 'ef', 'test1'),
(5, 'abc', 'test'),
(6, 'abcd', 'test'),
(7, 'efg', 'test1'),
(8, 'efgh', 'test1'),
(9, 't', 'test1'),
(10, 'ty', 'test1'),
(11, 'tyu', 'test1'),
(12, 'tyui', 'test1'),
(13, 't', 'test1'),
(14, 'a', 'test'),
(15, 'a', 'test'),
(16, 'ab', 'test'),
(17, 'abc', 'test'),
(18, 'abcd', 'test'),
(19, 'to', 'test1'), 
(20, 'abcde', 'test'),
(21, 'top', 'test1');
Run Code Online (Sandbox Code Playgroud)

输出:

ID  DataKey  UserName
--  -------  --------
6   abcd     test
8   efgh     test1
12  tyui     test1
14  a        test
20  abcde    test
21  top      test1
Run Code Online (Sandbox Code Playgroud)

注意:我使用了不同的列名,因为使用保留字作为列名不是最佳做法(它会强制您在名称周围放置方括号).

我使用的技术将使用单次扫描.它没有连接.使用适当索引的正确构造的基于连接的查询可能在CPU和时间上略胜一筹,但此解决方案肯定会有最少的读取.

更新

虽然我的查询可能很好,但这个问题中的特定数据结构有助于我在第一次回答时没有考虑的非常优雅的解决方案.感谢Andriy的基本想法,这里有一个炸药和超简单的查询(与上面相同的小提琴).

WITH Info AS (
   SELECT
      Grp = Row_Number() OVER (PARTITION BY UserName ORDER BY ID) - Len(DataKey),
         *
   FROM
      dbo.UserEntry U
)
SELECT
   ID = Max(I.ID),
   DataKey = Max(I.DataKey),
   I.UserName
FROM
   Info I
GROUP BY
   I.UserName,
   I.Grp;
Run Code Online (Sandbox Code Playgroud)


Ale*_*nko 0

存在 EXISTS 的选项

SELECT *
FROM dbo.test37 t1
WHERE EXISTS (
              SELECT *
              FROM dbo.test37 t2
              WHERE t1.[user] = t2.[user]
              GROUP BY LEFT([Key], 1), [User]
              HAVING MAX([Key]) = t1.[Key]
              )
Run Code Online (Sandbox Code Playgroud)

SQLFiddle上的演示

更新

;WITH cte AS
 (      
  SELECT t1.[Key], t1.[User], ROW_NUMBER() OVER(ORDER BY t1.[User], t1.[Key]) AS Id
  FROM dbo.test37 t1
  )
  SELECT c1.[Key], c1.[User]
  FROM cte c1 LEFT JOIN cte c2 ON c1.ID + 1 = c2.Id
  WHERE ISNULL(c2.[Key], '') NOT LIKE ISNULL(c1.[Key], '') + '%'
Run Code Online (Sandbox Code Playgroud)