在SQL中获取兄弟行

时间:2011-09-30 14:53:34

标签: sql sql-server sql-server-2005 tsql common-table-expression

我需要在查询中获取指定行的两个兄弟行。

SELECT pkUserId, name
FROM tblUsers
ORDER BY CreateDate

给出结果:

10 User1
18 User3
25 User4
79 User8
12 User2

如果我提供userId 25,我想要一个给我user3user8的查询

10 User1
18 User3 --> Output
25 User4 <-- Input
79 User8 --> Output
12 User2

如果认为应该可以使用ROW_NUMBER()通过单个查询(没有Union)来获取它,但我不确定如何创建它。

4 个答案:

答案 0 :(得分:4)

编辑:我为第二个解决方案添加了四个评论。

两种解决方案:

(1)第一种解决方案基于MAX,MIN功能:

CREATE TABLE dbo.TestData
(
     PKUserID INT PRIMARY KEY
    ,Name VARCHAR(25) NOT NULL
    ,CreateDate DATETIME NOT NULL
);
CREATE UNIQUE INDEX IX_TestData_CreateDate_PKUserID
ON  dbo.TestData(CreateDate, PKUserID);

INSERT  dbo.TestData
SELECT 10,'User1','2011-01-01T00:00:00'
UNION ALL
SELECT 18,'User3','2011-01-01T00:10:00'
UNION ALL
SELECT 25,'User4','2011-01-01T00:20:00'
UNION ALL
SELECT 79,'User8','2011-01-01T00:30:00'
UNION ALL
SELECT 12,'User2','2011-01-01T00:40:00';

DECLARE @UserID INT;
SELECT  @UserID = 25;

DECLARE @UserCreateDate DATETIME;
SELECT  @UserCreateDate = a.CreateDate
FROM    dbo.TestData a
WHERE   a.PKUserID = @UserID;

SELECT *
FROM
(
    SELECT  TOP 1 a.PKUserID
    FROM    dbo.TestData a
    WHERE   a.CreateDate < @UserCreateDate
    ORDER BY a.CreateDate DESC, a.PKUserID DESC
) a
UNION ALL
SELECT  *
FROM
(
    SELECT  TOP 1 a.PKUserID
    FROM    dbo.TestData a
    WHERE   @UserCreateDate < a.CreateDate
    ORDER BY a.CreateDate ASC, a.PKUserID ASC
) b

DROP TABLE dbo.TestData;

结果( 10次逻辑读取):

PKUserID
-----------
18
79

(2)第二个解决方案受某种方式的启发,来自quirky update方法,但并不意味着任何UPDATE,它只是一个简单的SELECT

CREATE TABLE dbo.TestData
(
     PKUserID INT PRIMARY KEY
    ,Name VARCHAR(25) NOT NULL
    ,CreateDate DATETIME NOT NULL
);
CREATE UNIQUE INDEX IX_TestData_CreateDate_PKUserID
ON  dbo.TestData(CreateDate, PKUserID);

INSERT  dbo.TestData
SELECT 10,'User1','2011-01-01T00:00:00'
UNION ALL
SELECT 18,'User3','2011-01-01T00:10:00'
UNION ALL
SELECT 25,'User4','2011-01-01T00:20:00'
UNION ALL
SELECT 79,'User8','2011-01-01T00:30:00'
UNION ALL
SELECT 12,'User2','2011-01-01T00:40:00';

DECLARE @UserID INT;
SELECT  @UserID = 25;

DECLARE @PreviousID INT
        ,@NextID INT
        ,@IsFound BIT
        ,@CountAfter INT; 

SELECT   @IsFound = 0
        ,@CountAfter = 0;

SELECT   @IsFound = CASE WHEN a.PKUserID = @UserID THEN 1 ELSE @IsFound END
        ,@PreviousID = CASE WHEN @IsFound = 0 THEN a.PKUserID ELSE @PreviousID END
        ,@CountAfter = CASE WHEN @IsFound = 1 THEN @CountAfter + 1 ELSE 0 END
        ,@NextID = CASE WHEN @CountAfter = 2 THEN a.PKUserID ELSE @NextID END
FROM    dbo.TestData a WITH(INDEX=IX_TestData_CreateDate_PKUserID)
GROUP BY a.CreateDate, a.PKUserID
ORDER BY a.CreateDate ASC, a.PKUserID ASC
OPTION  (MAXDOP 1);

SELECT @UserID UserID, @IsFound IsFound, @PreviousID [Prev], @NextID [Next]

DROP TABLE dbo.TestData;

结果( 2次逻辑读取):

UserID      IsFound Prev        Next
----------- ------- ----------- -----------
25          1       18          79

为防止错误结果:

  1. 我使用MAXDOP 1查询提示来阻止“并行”。
  2. 我创建了一个唯一索引,其中包含以GROUP BYORDER BY中使用的字段开头的所有必填字段。
  3. 我强迫INDEX=IX_TestData_CreateDate_PKUserID索引。
  4. 或者最简单的解决方案是使用OPTION (USE PLAN N'<?xml...><ShowPlanXML...')强制执行计划。

答案 1 :(得分:1)

试试这个:

WITH CTE
AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY CreateDate) [rn], pkUserId, name
    FROM tblUsers
)

SELECT *
FROM CTE c
WHERE c.rn =
(
    SELECT c2.rn
    FROM CTE c2
    WHERE c2.pkUserId = 25
) - 1
OR
c.rn =
(
    SELECT c3.rn
    FROM CTE c3
    WHERE c3.pkUserId = 25
) + 1

答案 2 :(得分:1)

以下是整个解决方案,包括虚拟声明,以便任何人都可以验证解决方案:

declare @tblUsers table (pkUserId int, name varchar(20), createdate datetime)

insert into @tblUsers values 
(10, 'User1','2011-01-01'),
(18, 'User3','2011-01-02'),
(25, 'User4','2011-01-03'),
(79, 'User8','2011-01-04'),
(12, 'User2','2011-01-05')

;with sel as(
SELECT pkUserId, name, ROW_NUMBER() over (order by createdate) rn
FROM @tblUsers
) 
select sel.pkUserId, sel.name 
from sel, 
(
select rn from sel where pkUserId = 25
) item
where sel.rn in (item.rn -1, item.rn+1)

答案 3 :(得分:0)

按以下顺序使用子查询:

  1. 查询输入行
  2. 查询外键
  3. 查询兄弟姐妹
  4. 以下是一个例子:

    Search Text in Other Answers to Questions I've Answered