SQL Server:ROW_NUMBER()OVER on table变量修改重复列

时间:2016-06-13 16:59:55

标签: sql sql-server duplicates row-number

让我说我有这样的表变量......

DECLARE @LocalTable TABLE
(
    IdField NVARCHAR(MAX),
    NameField NVARCHAR(MAX)
)

我就像这样填充它......

INSERT INTO @LocalTable
SELECT
    IdColumn,
    NameColumn
FROM SourceTable
源表中的

NameColumn可能具有重复值,因此本地表中的NameField将具有相同的重复值。

让我们说我想将本地表插入到目标表中,就像这样......

INSERT INTO TargetTable (NewIdColumn, NewNameColumn)
    SELECT
        IdField,
        NameField
    FROM 
        @LocalTable

但是NewNameColumn中的TargetTable有一个UNIQUE约束,因此重复会导致异常。

我想应用这个例子,

ROW_NUMBER() OVER(PARTITION BY NameField ORDER BY NameField)

这样NameField附加/后缀一个数字,表示其重复。

我有这个可以选择正确附加值的工作示例,但是我无法在这样的更新语句中使用它:

UPDATE localtable 
SET NameField = AppendedNameField 
FROM @LocalTable AS localtable

SELECT 
    CONCAT(Ref.NameField, ROW_NUMBER() OVER (PARTITION BY Ref.NameField 
                                             ORDER BY Source.IdField)), *
FROM 
    @LocalTable AS Source
INNER JOIN 
    @LocalTable AS Ref ON Ref.NameField = Source.NameField  
                       AND Ref.IdField != Source.IdField

提前致谢。

5 个答案:

答案 0 :(得分:5)

如果我理解你要做的事情。

WITH CTE AS
(
SELECT 
CONCAT(NameField, ROW_NUMBER()
    OVER(PARTITION BY NameField ORDER BY IdField)) AS NewName, *
FROM @LocalTable
)
UPDATE
CTE SET Name = NewName

如果您只想对重复的名称执行此操作,则可以使用该名称将COUNT(*) OVER (PARTITION BY Name)添加到CTE和条件逻辑中。

答案 1 :(得分:1)

您不一定需要更新表,不能在插入时添加后缀吗?

DECLARE @LocalTable TABLE (IdField INT, NameField VARCHAR(50));
INSERT @LocalTable VALUES (1, 'Not Duplicate'), (2, 'Duplicate'), (3, 'Duplicate');

INSERT INTO TargetTable (NewIdColumn, NewNameColumn)
SELECT  IdField,
        CONCAT(NameField, 
            CASE WHEN COUNT(*) OVER(PARTITION BY NameField) > 1 
                THEN ROW_NUMBER() OVER(PARTITION BY NameField ORDER BY IdField) 
                ELSE '' 
            END)
FROM    @LocalTable
ORDER BY IdField;

或者,您可以通过简单地将上面的select包装在子查询中来更新,并更新:

DECLARE @LocalTable TABLE (IdField INT, NameField VARCHAR(50));
INSERT @LocalTable VALUES (1, 'Not Duplicate'), (2, 'Duplicate'), (3, 'Duplicate');

UPDATE  t
SET     NameField = NewNameField
FROM    
(
    SELECT  IdField, NameField,
            NewNameField = CONCAT(NameField, 
                                CASE WHEN COUNT(*) OVER(PARTITION BY NameField) > 1 
                                    THEN ROW_NUMBER() OVER(PARTITION BY NameField ORDER BY IdField) 
                                    ELSE '' 
                                END)
    FROM    @LocalTable
) AS t;

SELECT * FROM @LocalTable;

答案 2 :(得分:0)

只需在temp表中添加一个标识字段即可。

DECLARE @LocalTable TABLE
(
ix int identity primary key,
IdField NVARCHAR(MAX),
NameField NVARCHAR(MAX)
)

Insert into @LocalTable(IdColumn, NameColumn)
SELECT
IdColumn,
NameColumn
FROM SourceTable
-- Make sure same names are consecutive in the table
ORDER BY NameColumn

设置NameColumn如下:

update lt set 
NameColumn = NameColumn 
-- Add a number based on the ix, minus the lowest ix entry for the same name
+ cast(
    (select lt.ix - min(lt2.ix) + 1
    from @localTable lt2 where lt2.name = lt.name) 
as nvarchar(10))
from @LocalTable lt
-- Only do those with duplicated names
where lt.NameColumn in (
  select NameColumn from @localtable group by NameColumn having count(1) > 1
)

答案 3 :(得分:0)

首先,考虑使用Temp表而不是temp变量 其次,尝试将NVARCHAR(MAX)改为更小的东西,如INT
以下是仅在NameField中包含唯一值的代码:

CREATE TABLE #LocalTable 
(
    IdField NVARCHAR(MAX),
    NameField NVARCHAR(MAX)
)

INSERT INTO #LocalTable
VALUES (1,'A'), (2,'B'), (3,'B')

INSERT INTO TargetTable
(
    NewIdColumn,
    NewNameColumn
)
SELECT IdField, NameField
FROM #LocalTable 
WHERE IdField in (
    SELECT MIN(IdField) FROM #LocalTable 
    GROUP BY NameField
);

请注意,重复记录的“IdField”将被忽略,不会插入目标表。

答案 4 :(得分:0)

尝试使用以下代码:

WITH TargetTable AS(
SELECT  *,
        ROW_NUMBER() OVER(PARTITION BY Ref.NameField ORDER BY Source.IdField) AS UniqueID

FROM @LocalTable AS L
),
UpdatedData AS(
    SELECT Source.NameField, 
           ROW_NUMBER() OVER(PARTITION BY Ref.NameField ORDER BY Source.IdField) AS UniqueID,
           CONCAT(Ref.NameField, ROW_NUMBER() OVER(PARTITION BY Ref.NameField ORDER BY Source.IdField)) AS AppendedNameField
    FROM @LocalTable AS Source
    INNER JOIN @LocalTable AS Ref ON Ref.NameField = Source.NameField AND Ref.IdField != Source.IdField
)
UPDATE T
SET NameField=U.AppendedNameField
FROM TargetTable AS T
JOIN UpdatedData AS U ON T.NameField=U.NameField AND T.UniqueID=U.UniqueID;