我正在将表中的字符串值的列从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的了解是非常基础的,所以我真的不知道如何实现。
[如果幸运的话,您可以查看前六个字符中的重复项。我说幸运的是,因为这假设您从未有超过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);
您可以使用可更新的CTE来实现这一目标: