SQL Update问题

时间:2011-08-22 07:04:27

标签: sql sql-server-2008

我想知道如何实现这一目标。

假设我有一个包含两列的表(IU(uniqueidentifier),(ID(int),SEL(char(1))

ID列在每行中具有以下值(按IU排序):

0,1,2,2,0,0,1,2,2,2,0,0,4,2,2,0,0,1,2,0,0

对于属于该组的行,我需要使用“Y”更新列SEL:    1,2,2,2 ...... (从1开始,在下一行中是2的。(第4组,第2组,2不正确)。

所以在这个示例列中:SEL应该是:

null,Y,Y,Y,null,null,Y,Y,Y,Y,null,null,4,2,2,null,null,Y,Y,null,null

谢谢!

4 个答案:

答案 0 :(得分:2)

这是一种基于集合的方法。

DDL&样本数据:

DECLARE @atable TABLE (
  UI uniqueidentifier DEFAULT NEWSEQUENTIALID(),
  ID int,
  SEL char(1)
);
INSERT INTO @atable (ID)
SELECT 0 UNION ALL
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 2 UNION ALL
SELECT 0 UNION ALL
SELECT 0 UNION ALL
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 2 UNION ALL
SELECT 2 UNION ALL
SELECT 0 UNION ALL
SELECT 0 UNION ALL
SELECT 4 UNION ALL
SELECT 2 UNION ALL
SELECT 2 UNION ALL
SELECT 0 UNION ALL
SELECT 0 UNION ALL
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 0 UNION ALL
SELECT 0;

UPDATE语句:

WITH marked AS (
  SELECT
    *,
    grp = CASE ID WHEN 0 THEN 0 ELSE 1 END
  FROM @atable
),
grouped AS (
  SELECT
    *,
    grpID = ROW_NUMBER() OVER (ORDER BY UI)
          - ROW_NUMBER() OVER (PARTITION BY grp ORDER BY UI)
  FROM marked
),
ranked AS (
  SELECT
    *,
    rnk = ROW_NUMBER() OVER (PARTITION BY grp, grpID ORDER BY UI)
  FROM grouped
)
UPDATE g
  SET SEL = CASE r.ID
    WHEN 0 THEN NULL
    WHEN 1 THEN 'Y'
    ELSE CAST(g.ID AS varchar)
  END
FROM grouped g
  INNER JOIN ranked r ON g.grp = r.grp AND g.grpID = r.grpID
WHERE r.rnk = 1;

更新后SELECT * FROM @atable的结果:

UI                                   ID          SEL
------------------------------------ ----------- ----
A4095E70-A0CC-E011-813B-20CF30905E89 0           NULL
A5095E70-A0CC-E011-813B-20CF30905E89 1           Y
A6095E70-A0CC-E011-813B-20CF30905E89 2           Y
A7095E70-A0CC-E011-813B-20CF30905E89 2           Y
A8095E70-A0CC-E011-813B-20CF30905E89 0           NULL
A9095E70-A0CC-E011-813B-20CF30905E89 0           NULL
AA095E70-A0CC-E011-813B-20CF30905E89 1           Y
AB095E70-A0CC-E011-813B-20CF30905E89 2           Y
AC095E70-A0CC-E011-813B-20CF30905E89 2           Y
AD095E70-A0CC-E011-813B-20CF30905E89 2           Y
AE095E70-A0CC-E011-813B-20CF30905E89 0           NULL
AF095E70-A0CC-E011-813B-20CF30905E89 0           NULL
B0095E70-A0CC-E011-813B-20CF30905E89 4           4
B1095E70-A0CC-E011-813B-20CF30905E89 2           2
B2095E70-A0CC-E011-813B-20CF30905E89 2           2
B3095E70-A0CC-E011-813B-20CF30905E89 0           NULL
B4095E70-A0CC-E011-813B-20CF30905E89 0           NULL
B5095E70-A0CC-E011-813B-20CF30905E89 1           Y
B6095E70-A0CC-E011-813B-20CF30905E89 2           Y
B7095E70-A0CC-E011-813B-20CF30905E89 0           NULL
B8095E70-A0CC-E011-813B-20CF30905E89 0           NULL

答案 1 :(得分:1)

表格中的行没有inherent order,因此您的分组(1,2,2,2)完全是任意的。我们无法保证您的ID始终按此顺序排列:

0, 1, 2, 2, 0, 0, 1, 2, 2, 2, 0, 0, 4, 2, 2, 0, 0, 1, 2, 0, 0

可能是他们完全按照其他顺序进行。因此,您需要指定ORDER BY子句来获取订单。由于您的表中没有其他字段,但SEL和ID,我认为这是不可能的。

答案 2 :(得分:1)

我真的希望有人提出比这更好的东西,因为我讨厌这个答案。

create table #test (
    IU  int identity    primary key,
    id  int,
    sel varchar(1)
)

insert into #test(id)
values (0), (1), (2), (2), (0), (0), (1), (2), (2), (2), (0), (0), (4), (2), (2), (0), (0), (1), (2), (0), (0)

DECLARE myCur CURSOR FORWARD_ONLY
FOR
    select t.ID
    from #test t
    order by t.IU
FOR UPDATE OF t.sel

DECLARE @ID int, @lagSel varchar(1)

OPEN myCur
FETCH myCur INTO @ID

WHILE (@@FETCH_STATUS = 0) BEGIN
    SET @lagSel = CASE 
                    WHEN @lagSel = 'Y' AND @ID in (1,2) THEN 'Y'
                    WHEN @ID = 1 THEN 'Y'
                    ELSE NULL
                  END

    UPDATE #test
    SET sel = @lagSel
    WHERE CURRENT OF myCur

    FETCH myCur INTO @ID
END

CLOSE myCur
DEALLOCATE myCur

有几点需要注意:

  • 我们在游标中手动管理@lagSel的值,以便我们可以将值从一行传送到下一行。
  • 为了能够使用游标FOR UPDATE,该表必须有一个主键。
  • 在UPDATE语句中,myCur的WHERE CURRENT(至少在理论上)比任何其他where子句都有很大的性能提升。

我首先尝试使用滞后连接执行此操作,但无法完全实现此目的。这是我的工作,万一其他人可以做得更好:

select main.IU, main.id,
    CASE 
        WHEN main.id = 1 THEN 'Y'
        WHEN main.id = 2 AND lag.id in (1, 2) THEN 'Y'
        ELSE NULL
    END as new_sel
from #test main left outer join
    #test lag on main.IU = lag.IU + 1

答案 3 :(得分:0)

我认为你在这里遇到了设计错误。 MS SQL Server对“下一行”和“上一行”一无所知。如果您尝试选择记录,则可以不时更改记录的顺序,除非您使用ORDER BY语句指定顺序。 我认为你需要先改变表格的结构。

编辑:正如我所见,你有这个领域,可以订购你的记录。现在,您可以使用CURSOR实现目标。 简而言之,您可以创建CURSOR FOR SELECT IU, ID ORDER BY IU ASC。 循环遍历光标记录,可以检查ID字段值的顺序,当序列完全等效时,可以更新相应的记录。