【问题标题】:Stored procedure to update rows if exists如果存在则更新行的存储过程
【发布时间】:2016-11-16 17:00:27
【问题描述】:

我有一个存储过程,如果它们不存在,我正在运行它来插入值,如果存在则更新。我有一个数据来自的表

Table1
Name (nvarchar)
Data (int)
Timestamp (datetime)

数据是这样的

Name1       5       2016-11-16 09:46:40.490
Name2       10      2016-11-16 09:48:35.240
Name1       7       2016-11-16 09:35:24.350
Name2       8       2016-11-15 02:27:44.670

我正在尝试将名称插入到新表中,对整数进行平均,然后按天分组。这是第二张表

Table2
Name (nvarchar)
Data (int)
Timestamp (date)

数据如下所示。 Name1 是同一天,所以取平均值。 Name2 是不同的日子,所以它们不是平均的。

Name1      6       2016-11-16 00:00:00.000
Name2      10      2016-11-16 00:00:00.000
Name2      8       2016-11-15 00:00:00.000

我将时间戳转换为日期,以便在当天更容易合并。我的存储过程是这样的

IF NOT EXISTS (SELECT t.Name, t.Timestamp
    FROM Table2 t
    JOIN Table1 a
    ON t.Name = a.Name AND t.Timestamp = CONVERT(date, a.Timestamp)
    GROUP BY t.Name, t.Timestamp)

INSERT INTO Table2 (Name, Timestamp, Data)
SELECT 
    Name,
    CAST(Timestamp AS DATE) as Date,
    AVG(Data) as Average_Data
FROM Table1
GROUP BY CAST(Timestamp AS DATE), Name

ELSE
UPDATE Table1
SET
WHERE

所以我第一次运行它,没有问题。这些值已正确添加和分组。但是,第二次运行时,它总是插入。

我的更新声明在我删除之前看起来像这样

SET Name = Name, Timestamp = Timestamp, Data = Date
WHERE Name = Name, Timestamp = Timestamp

我知道这是不对的,但它似乎永远不会点击更新,因为插入总是运行。如果我在 If Not Exists 中运行 Select,我会看到数据并且它应该认为它存在。

我想我需要帮助清理 If Not Exists 并创建有效的更新语句。

编辑:

以下更新代码

IF EXISTS(SELECT Name, Timestamp FROM Table2)
UPDATE 
    Table2
SET 
    Name = a.Name,
    Timestamp = CONVERT(date, a.Timestamp),
    Data = AVG(a.Data)
FROM
    Table2 t
INNER JOIN
    Table1 a
ON t.Name = a.Name
WHERE t.Name = a.Name AND t.Timestamp = CONVERT(date, a.Timestamp)

ELSE
    INSERT INTO Table2 (Name, Timestamp, Data)
        SELECT 
            Name,
            CAST(Timestamp AS DATE) as Date,
            AVG(Data) as Average_Data
        FROM Table1
        GROUP BY CAST(Timestamp AS DATE), Name
END

【问题讨论】:

  • 你的逻辑是全有或全无,这不是你想要的。您需要先进行更新,无需检查行是否存在。如果他们不更新将不会更新他们。然后你想改变你的插入语句以使用一个不存在的地方。
  • 谢谢。我会试一试
  • 所以我将更新更改为先运行,看起来它仍然每次都插入值。这可能是我的时间戳的问题吗?我正在将其转换为日期,但我想知道它是否在转换之前对其进行检查?
  • 新代码在哪里? FWIW,您应该考虑不使用名为 timestamp 的列。它是 sql server 中的一种数据类型,与日期或时间无关。老实说,无论如何,这个名字对我来说太模棱两可了。这是什么意思?时间创造?时间编辑?其他相关时间? DateCreated 或类似名称是更好的命名约定。明确说出你的名字。
  • 您更新的代码仍然是全有或全无的方法。你不能在这里使用 If 语句,因为你想更新一些行并插入一些行。

标签: sql sql-server tsql stored-procedures


【解决方案1】:

我喜欢使用 MERGE 语句而不是连续的 UPDATE 和 INSERT。 由于聚合的原因,在下面的回答中,我将 CTE 与 MERGE 结合使用。

-- CREATE AND INSERT TABLES 
DROP TABLE TABLE1
CREATE TABLE 
TABLE1
   (    Name nvarchar(5)
    ,   Data int
    ,   Timestamp date
    )
INSERT INTO TABLE1  VALUES  ('Name1',5  ,'2016-11-16')
                    ,       ('Name2',10 ,'2016-11-16')
                    ,       ('Name1',7  ,'2016-11-16')
                    ,       ('Name2',8  ,'2016-11-15')
                    ,       ('Name3',8  ,'2016-11-15')
                    ,       ('Name3',10 ,'2016-11-15')
                    ,       ('Name3',9  ,'2016-11-16')
                    ,       ('Name3',11 ,'2016-11-16')


DROP TABLE TABLE2
CREATE TABLE 
TABLE2
   (    Name nvarchar(5)
    ,   Data int
    ,   Timestamp datetime
    )
INSERT INTO TABLE2 VALUES  ('Name1',0 ,'2016-11-16')
                          ,('Name2',0 ,'2016-11-16')
                          ,('Name2',0 ,'2016-11-15')

    SELECT * FROM TABLE2 -- SHOW TABLE2 BEFORE MERGE


BEGIN TRANSACTION
--  HERE IS WHERE THE CODE THAT REPLACES YOUR QUERY ACTUALLY BEGINS
   ;WITH    CTE_AVG_DATA_TABLE1
        AS (SELECT      Name 
            ,           Timestamp
            ,           AVG(Data) AS [AVG(Data)]
            FROM        TABLE1
            GROUP BY    Name 
            ,           Timestamp
            )

        MERGE   Table2              AS TARGET
        USING   CTE_AVG_DATA_TABLE1 AS SOURCE
            ON      TARGET.Name = SOURCE.Name
                AND TARGET.TIMESTAMP = SOURCE.TIMESTAMP
        WHEN MATCHED THEN 
            UPDATE      
            SET     TARGET.Name         = SOURCE.Name
            ,       TARGET.Timestamp    = CONVERT(date, SOURCE.Timestamp)
            ,       TARGET.Data         = SOURCE.[AVG(Data)]
        WHEN NOT MATCHED THEN 
            INSERT (Name, Timestamp, Data)
            VALUES (SOURCE.NAME, SOURCE.TIMESTAMP, SOURCE.[AVG(Data)])
        ;

    SELECT * FROM TABLE2 -- SHOW TABLE2 AFTER MERGE
ROLLBACK /*ALLOWS THIS TEST CODE TO BE RUN OVER AND OVER 
           ADDING ROWS TO INSERT STATEMENTS ABOVE TO SHOW THAT IT WORKS
         */

【讨论】:

  • 感谢您的回答。我会试一试。只是想知道,使用 CTE 会增加什么?
  • 个人编码风格和可读性——CTE 只是根据需要按列分组以计算平均值的一种方法——(或使用其他聚合函数)。我们可以使用内联表值函数或子查询,编译器会以彼此相同的方式解释它们,并命中源表上的任何索引(如果存在)。
【解决方案2】:

我个人更喜欢将旧式技术用于通常所说的“upsert”。 MERGE 运行良好,但调试问题确实很痛苦,因为它一次性完成所有操作。

这是我喜欢的方法类型,因为它将插入和更新分开,提供了更大的灵活性并且更容易调试问题。您还可以在插入中切换左连接以使用 NOT EXISTS 和相关子查询,但大多数时候性能差异可以忽略不计。

UPDATE t
SET 
    Name = a.Name,
    Timestamp = CONVERT(date, a.Timestamp),
    Data = AVG(a.Data)
FROM
    Table2 t
INNER JOIN
    Table1 a
ON t.Name = a.Name
WHERE t.Name = a.Name AND t.Timestamp = CONVERT(date, a.Timestamp)


INSERT INTO Table2 (Name, Timestamp, Data)
SELECT 
    Name,
    CAST(Timestamp AS DATE) as Date,
    AVG(Data) as Average_Data
FROM Table1 a
left join Table2 t on a.Name = t.Name
where t.Name is null
GROUP BY Name, CAST(Timestamp AS DATE)
    , Name

-- 编辑--

当我发布时,我什至没有注意到您更新中的汇总。您可以使用 cte 轻松规避此问题。

with cte as
(
    select Name = a.Name
        , Timestamp = CONVERT(date, a.Timestamp)
        , AverageData = AVG(a.Data)
    FROM
        Table2 t
    INNER JOIN
        Table1 a
    ON t.Name = a.Name
    WHERE t.Name = a.Name AND t.Timestamp = CONVERT(date, a.Timestamp)
    GROUP BY Name, CAST(Timestamp AS DATE)
)

update t
set Name = c.Name
    , Timestamp = c.Timestamp
    , Date = c.AverageData
from Table2 t
join cte c on c.Name = t.Name
    and c.Timestamp = t.Timestamp

【讨论】:

  • 当我删除 IF 时,它说我无法在 UPDATE 语句中设置聚合。
  • 我更新了代码。我什至没有注意到你在那里有一个聚合。 ;)
  • 正如您所发现的,在使用 FROM 时不能直接在更新中使用聚合。由于我们现在从 cte 获得标量值,因此即使标量值来自聚合,我们也可以规避该问题。
  • 我认为我们在使用聚合函数时需要一个 GROUP BY 子句。
  • @WayTooSerious 是的,事实上我们这样做了。谢谢。我编辑了我的答案。当您在稀薄的空气中编码时会发生这种情况。 :D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-03-02
  • 1970-01-01
  • 2017-03-01
  • 2015-02-05
  • 2012-12-17
  • 2015-06-16
  • 2014-05-21
相关资源
最近更新 更多