如何在SQL Server中修改重复的字符串?

问题描述 投票:0回答:2

我正在将表中的字符串值的列从15个字符截断为10个字符(这是我想允许该列的新最大长度)。

在表中的一对列上有一个唯一键,这个键就是其中之一。

由于被截断,有可能被违反。

例如:

| ID |        C1       | C2 |
| -- | --------------- | -- |
| 1  | 123456789012345 |  1 |
| 2  | 123456789012346 |  1 |
| 3  | 123456789012345 |  2 |
| 4  | 123456789012346 |  2 |

假设我在C1和C2上有一个唯一的密钥。 C1当前为varchar(15),但由于超出我控制范围的原因,它已更改为varchar(10)。

[我必须将C1中的值截断为长度为10的字符串。但是,如果我不加思索地这样做,显然(在上面的示例中)我将违反唯一键约束。

所以,我知道如何使用类似的方法来查找所有重复项:

select
    t1.ID,
    LEFT(t1.C1, 10) as C1,
    t1.C2
INTO
    #ColumnDuplicates
FROM
    t t1
    join t t2 on
        t1.ID <> t2.ID
        AND LEFT(t1.C1, 10) = LEFT(t2.C1, 10)
WHERE
    t1.C2 = t2.C2

SELECT * FROM #ColumnDuplicates

请参阅上表,此查询将使我:

| ID |     C1     | C2 |
| -- | ---------- | -- |
| 1  | 1234567890 |  1 |
| 2  | 1234567890 |  1 |
| 3  | 1234567890 |  2 |
| 4  | 1234567890 |  2 |

现在是我不确定下一步的位置。我需要做的是通过某种方式到达此位置:

| ID |     C1     | C2 |
| -- | ---------- | -- |
| 1  | 123456_001 |  1 |
| 2  | 123456_002 |  1 |
| 3  | 123456_001 |  2 |
| 4  | 123456_002 |  2 |

有效地,我想为每个C2值查找所有重复的C1值,然后将最后4个字符更改为_ [0-9] [0-9] [0-9]模式,然后从000(或001,我不太在意哪个起点)到999为止,将这些重复项逐渐编号。最大为999。这将为我提供空间来处理每个C2值大约999个重复项。根据我对正在使用的数据的熟悉程度,确定不会有问题。

然后,我可以轻松地使用此临时表来更新我要修改的主表中的C1值。

目前,我对SQL的了解是非常基础的,所以我真的不知道如何实现。

sql sql-server database tsql truncate
2个回答
0
投票

[如果幸运的话,您可以查看前六个字符中的重复项。我说幸运的是,因为这假设您从未有超过1000个此类重复项:

with toupdate as (
      select t.*,
             row_number() over (partition by left(c1, 6), c2 order by c2) as seqnum,
             count(*) over (partition by left(c1, 6), c2) as cnt
      from t
     )
update toupdate
    set c1 = (case when cnt > 1
                   then concat(left(c1, 6), '_', format(seqnum, '000'))
                   else left(c1, 10)
              end);

以上对于重复项有些悲观。在使用row_number()之前过滤掉已知的单例可能是有意义的:

with toupdate as (
      select t.*,
             row_number() over (partition by left(c1, 6), c2,
                                             (case when cnt10 > 1 then 1 else 2 end)
                                order by c2
                               ) as seqnum,
             count(*) over (partition by left(c1, 6), c2,
                                         (case when cnt10 > 1 then 1 else 2 end)
                           ) as cnt6
      from (select t.*,
                   count(*) over (partition by left(c1, 10), c2) as cnt10
            from t
           ) t
     )
update toupdate
    set c1 = (case when cnt10 > 1
                   then concat(left(c1, 6), '_', format(seqnum, '000'))
                   else left(c1, 10)
              end);

0
投票

您可以使用可更新的CTE来实现这一目标:

© www.soinside.com 2019 - 2024. All rights reserved.