繁体   English   中英

SQL Server存储过程:具有最低值的UPDATE,如果已存在值则INSERT

[英]SQL Server stored procedure: UPDATE with lowest value, else INSERT if there's already a value

希望一次迁移大约2万行。 脱离SQL已有10多年了,一直在挣扎。

我可能会完全不对。 需要更新Table1.Value2从最低值Table2.Value1 ,除非已经是一个Value2 如果是后者,则需要插入具有该值的行作为Table1.Value1

Table1.Value1是该行的每个ID的最小值。 Value2必须是第二低的值。

当前表1:

ID1, 123, [empty]
ID4, 111, [empty]

当前表2:

ID1, 224
ID1, 331
ID4, 210
ID4, 551

表1-所需状态:

ID1, 123, 224
ID1, 331, [empty]
ID4, 111, 210
ID4, 551, [empty]

表2-所需状态:

[empty]

这是我尝试过的内容,“更新”部分正常运行。 插入永远不会起作用。 我想我把自己编码成一个角落。

CREATE PROCEDURE dbo.Broken
AS
    --DECLARE VARIABLES
    DECLARE @ID INT,
            @Value1 INT,
            @Value2 INT,
            @tmpValue INT

    --DECLARE COUNTER
    DECLARE @Counter INT
    SET @Counter = 1

    --DECLARE CURSOR FOR QUERY
    DECLARE cTable1 CURSOR READ_ONLY FOR
        SELECT ID,Value1, Value2
        FROM Table1 

    OPEN cTable1

    --FETCH VARIABLES
    FETCH NEXT FROM cTable1 INTO @ID, @Value1, @Value2

    --LOOP 
    WHILE @@FETCH_STATUS = 0
    BEGIN
        IF (@Value2 = '' OR @Value2 is NULL)
        BEGIN
            --UPDATE Table1.Value2 with next lowest Table1.Value2 > Table1.Value1
            SET @tmpValue = (SELECT MIN(Value1) FROM Table2  WHERE Value1 > @Value1 AND ID = @ID)

            UPDATE Table1 
            SET Value2 = @tmpValue     
            WHERE ID = @ID

            --DELETE AFFECTED ROW FROM Table2
            DELETE FROM Table2 
            WHERE Value1 = @tmpValue AND ID = @ID;
        END
        ELSE
        BEGIN
            --INSERT ROW IN Table1 WITH ID, Value1 FROM Table2 
            SET @ID = (SELECT MIN(ID) FROM Table2)
            SET @tmpValue = (SELECT MIN(Value1) FROM Table2 WHERE ID = @ID)

            INSERT INTO Table1(ID, Value1, Value2) 
            VALUES (@ID, @tmpValue, '')

            DELETE FROM Table2 
            WHERE ID = @ID
        END

        --FETCH NEXT VARIABLES
        FETCH NEXT FROM cTable1 INTO @ID, @Value1, @Value2
    END

    --CLOSE THE CURSOR TABLE
    CLOSE cTable1
    DEALLOCATE cTable1

大约有2万行,所以我希望存储过程可以完成所有工作。 这是一次性的,所以我并不担心开销。

这样的更新所需的基本查询非常清楚:

UPDATE Table1
SET VALUE2 = (
    SELECT MIN(t2.Value1) 
    FROM Table2 t2
    WHERE t2.Value1 > Table1.Value1 
      AND t2.ID = Table1.ID
    )

然后,我将执行以下插入操作:

INSERT INTO Table1 (ID, Value1, Value2)
SELECT ID,
    Value1,
    [Empty]
From Table2 t2
LEFT JOIN Table1 t1
    ON t2.ID = t1.ID
    AND t2.Value1 = t1.Value1
LEFT JOIN Table1 t1a
    ON t2.ID = t1a.Id
    AND t2.Value1 = t1a.Value2
WHERE t1.Id IS NULL
    AND t1a.Id IS NULL

DELETE FROM Table2

请注意,这符合您声明的要求,但可能不符合您的实际要求-@JeroenMostert的评论涵盖了许多此类问题。

以下查询似乎会产生所需的输出:

declare @Table1 table (
    Id varchar(30) not null,
    Value1 int null,
    Value2 int null
);

declare @Table2 table (
    Id varchar(30) not null,
    Value int null
);

insert into @Table1 (Id, Value1)
values
    ('ID1', 123),
    ('ID4', 111);

insert into @Table2 (Id, Value)
values
    ('ID1', 224),
    ('ID1', 331),
    ('ID4', 210),
    ('ID4', 551);

select t2.Id,
    case
        when t2.Value = min(t2.Value) over(partition by t1.Id) then t1.Value1
        else t2.Value
    end as [V1],
    case
        when t2.Value = min(t2.Value) over(partition by t1.Id) then t2.Value
    end as [V2]
from @Table1 t1
    left join @Table2 t2 on t1.Id = t2.Id
where t1.Value1 < t2.Value;

考虑到这一点,简单的MERGE就可以解决问题:

merge @Table1 t
using (
    select t2.Id,
    case
        when t2.Value = min(t2.Value) over(partition by t1.Id) then t1.Value1
        else t2.Value
    end as [V1],
    case
        when t2.Value = min(t2.Value) over(partition by t1.Id) then t2.Value
    end as [V2]
from @Table1 t1
    left join @Table2 t2 on t1.Id = t2.Id
where t1.Value1 < t2.Value
) s on s.Id = t.Id and s.V1 = t.Value1
when not matched by target then
    insert (Id, Value1, Value2)
    values (s.Id, s.V1, s.V2)
when matched then
    update set Value2 = s.V2;

对于某些极端情况,我可能是错的,但原则上应该可以。

尝试这个

;With updateData as (
Select Id , Value1 , row_number() over ( partition by id order by value1 ) rowNumber from table2
)
Update table1 set value2 = t.value1 from updateData t where t.Id = table1.Id and t.rowNumber=1 

;With insertData as(
Select Id , Value1 , row_number() over ( partition by id order by value1 ) rowNumber from table2
)
Insert into table1(id,value1)
Select t.id , t.value1 from insertData t
Where  t.rowNumber>1

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM