我需要在查询中获取指定行的两个兄弟行。
SELECT pkUserId, name
FROM tblUsers
ORDER BY CreateDate
给出结果:
10 User1
18 User3
25 User4
79 User8
12 User2
如果我提供userId 25,我想要一个给我user3
和user8
的查询
10 User1
18 User3 --> Output
25 User4 <-- Input
79 User8 --> Output
12 User2
如果认为应该可以使用ROW_NUMBER()
通过单个查询(没有Union)来获取它,但我不确定如何创建它。
答案 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
为防止错误结果:
MAXDOP 1
查询提示来阻止“并行”。GROUP BY
和ORDER BY
中使用的字段开头的所有必填字段。INDEX=IX_TestData_CreateDate_PKUserID
索引。 或者最简单的解决方案是使用 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)
按以下顺序使用子查询:
以下是一个例子: