我正在运行一个存储过程,如果它们不存在则插入值,如果存在则更新。我有一张数据来自
的表格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 If Exists中运行Select,我会看到数据,它应该将其视为存在。
我想我需要帮助清理If Not Exists并创建一个有效的Update语句。
编辑:
更新了以下代码
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
答案 0 :(得分:2)
我喜欢使用MERGE语句而不是连续的UPDATE和INSERT。 由于聚合,在下面的答案中,我使用带有MERGE的CTE。
-- 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
*/
答案 1 :(得分:1)
我个人更喜欢将旧式技术用于通常所说的“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