【发布时间】:2016-07-05 21:40:12
【问题描述】:
我有一个有五列的表格:
- mykey (INT pk)
- 名称(VARCHAR)
- 其他数据 (VARCHAR)
- 组ID (INT)
- 日期日期时间
- 有效 (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”。由于使用此表的过程的性质(我无法以任何方式更改),对“名称”或“其他数据”列的任何更改都会产生一个新行。
假设用户从使用此表的单独应用程序将第 5 行列“其他数据”从 blue 更改为 green。在这种情况下,该单独的应用程序将生成一个新行: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"
有没有更简单的方法可以在不使用循环的情况下获得类似的结果?事实证明,我的方法非常不准确且难以维护。
【问题讨论】:
-
每组总是只有一行(最新的)
active = 1?当上一行有不同的名称时,你想返回它吗? -
绝对正确,每组始终只有一个活动行。这是一个硬性规定。我只想在前一行名称更改时返回(特别是
_nt到_rt或反之亦然)。其他的,我不在乎。
标签: sql sql-server sql-server-2008