在SQL Server 2005中,给出以下结果集
ID | InstanceNumber | IsArchived
5000 | 1 | True
8347 | 2 | True
9343 | 3 | False
11048 | 4 | False
我想回来的是:
ID | InstanceNumber | IsArchived
9343 | 1 | False
11048 | 2 | False
返回“IsArchived”为false的行,但从结果集中减去max InstanceNumber列。
这是一个示例SQL语句,它返回我正在寻找的行为:
DECLARE @tbl TABLE
(ID INT NOT NULL, InstanceNumber INT NOT NULL, IsArchived BIT NOT NULL)
INSERT INTO @tbl VALUES (5000, 1, 1)
INSERT INTO @tbl VALUES (8347, 2, 1)
INSERT INTO @tbl VALUES (9343, 3, 0)
INSERT INTO @tbl VALUES (11048, 4, 0)
SELECT ID, InstanceNumber - (SELECT MAX(InstanceNumber) FROM @tbl WHERE IsArchived = 1), IsArchived
FROM @tbl
WHERE IsArchived = 0
这是最有效的方法吗?还是有另一种方法可以实现同样的行为?我有额外的where子句需要进入完整的语句(如5-6语句),我想避免必须声明它们2X,一次返回已归档的max实例,以及结果集过滤。
修改的 为了澄清查询的要求,“InstanceNumber”列可以跳过数字。所以可能有一个InstanceNumber = 6的记录而没有返回5,所以返回的所有记录都不是顺序的。
答案 0 :(得分:1)
在我的测试中,解释计划在您使用子选择的版本与使用CROSS JOIN的版本之间是相同的:
SELECT x.id,
x.instancenumber - y.max_num AS instancenumber,
x.isarchived
FROM @tbl x
CROSS JOIN (SELECT MAX(InstanceNumber) AS max_num
FROM @tbl
WHERE IsArchived = 1) y
WHERE x.isarchived = 0
答案 1 :(得分:0)
试试这个:
SELECT
ID,
InstanceNumber = Row_Number() OVER (ORDER BY InstanceNumber),
IsArchived
FROM @tbl
WHERE IsArchived = 0
但有一个问题是,如果存档数据存在差距,该怎么办?
INSERT INTO @tbl VALUES (5000, 1, 1)
INSERT INTO @tbl VALUES (8347, 2, 0)
INSERT INTO @tbl VALUES (9343, 3, 1)
INSERT INTO @tbl VALUES (11048, 4, 0)
在这种情况下你想要什么结果?您当前的查询将InstanceNumber设置为-1和1.我上面的查询给出了InstanceNumber 1和2.另一个可能的答案是返回InstanceNumber 1和3(表示8347和11048之间的InstanceNumber步骤2)。
<强>更新强>
因此,如果我正确理解间隙的可能性,则需要更改查询以处理以下情况:
INSERT INTO @tbl VALUES (5000, 1, 1)
INSERT INTO @tbl VALUES (8347, 2, 1)
INSERT INTO @tbl VALUES (9343, 4, 0)
INSERT INTO @tbl VALUES (11048, 5, 0)
SELECT
ID,
NewInstanceNumber = InstanceNumber + 1
- (SELECT Min(InstanceNumber) FROM @tbl WHERE IsArchived = 0),
IsArchived
FROM @tbl
WHERE IsArchived = 0
所以编号总是从1开始。你也可以试试这个:
SELECT
ID,
NewInstanceNumber = InstanceNumber + 1 - Min(InstanceNumber) OVER (),
IsArchived
FROM @tbl
WHERE IsArchived = 0
但我不知道这会比你当前的查询更好还是更差。
答案 2 :(得分:0)
SELECT id,
instancenumber -
MAX(CASE IsArchived WHEN true THEN instancenumber ELSE 0 END)
OVER () as NewInstanceNumber,
false AS IsArchived
FROM @tbl
WHERE IsArchived = false
警告。我根本没有测试过这个。
对其他回复的警告:
在查询中间插入具有IsArchived = true的新记录(或者将现有记录从IsArchived = false更改为IsArchived = true)时,使用CROSS JOIN
或子选择可能会导致极少数问题。
如果先查询SELECT MAX(InstanceNumber)
部分查询,则查询的主要部分可能会减去当时不再是MAX(InstanceNumber)
的值。
使用聚合窗口函数MAX() OVER()
,实际数据只扫描一次,这样可以完全避免这个问题。