我正在处理将表中的一列字符串值从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进行编号。这将为我提供处理每个C2值大约999重复项的空间,根据对所使用数据的熟悉程度,我可以确定这不会成为问题。
然后我可以轻松地使用此临时表来更新我要修改的主表中的C1值。
此刻我对SQL的了解是非常基础的,所以我真的不知道如何实现。
答案 0 :(得分: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);
答案 1 :(得分:0)
您可以使用可更新的CTE来实现:
CREATE TABLE dbo.YourTable (ID int NOT NULL,
C1 varchar(15) NOT NULL,
C2 int NOT NULL);
CREATE UNIQUE INDEX YourIndex ON dbo.YourTable (C1,C2);
GO
INSERT INTO dbo.YourTable (ID, C1, C2)
VALUES (1,'123456789012345',1),
(2,'123456789012346',1),
(3,'123456789012345',2),
(4,'123456789012346',2);
GO
WITH CTE AS(
SELECT C1,
LEFT(YT.C1,6) + '_' + RIGHT(CONCAT('000',ROW_NUMBER() OVER (ORDER BY YT.C1, YT.C2 ASC)),3) AS NewC1
FROM dbo.YourTable YT
WHERE LEN(YT.C1) > 10) --Unsure if that WHERE is needed
UPDATE CTE
SET C1 = NewC1;
GO
DROP INDEX YourIndex ON dbo.YourTable; --Has to be dropped to alter
ALTER TABLE dbo.YourTable ALTER COLUMN C1 varchar(10) NOT NULL;
GO
CREATE UNIQUE INDEX YourIndex ON dbo.YourTable (C1,C2); --Recreate
GO
SELECT *
FROM dbo.YourTable;
GO
DROP TABLE dbo.YourTable;