简体   繁体   English

获取T-SQL中列的运行平均值

[英]Get the running average of a column in T-SQL

Okay so I have a table and in one column I have some data and the second column the average of the data. 好的,所以我有一个表,在一列中我有一些数据,第二列是数据的平均值。 Example

id|Data|avg
1 |20  |20
2 |4   |12
3 |18  |14

How do I populate the avg column on insert with the running average of the Data column using T-SQL? 如何使用T-SQL在插入时使用数据列的运行平均值填充avg列?

EDIT: Sorry guys, this was actually a stupid mistake I made. 编辑:对不起伙计们,这实际上是我犯的一个愚蠢的错误。 I assumed I had SQL 2014 but after trying Stephan's code and getting some errors, I went back to confirm and realize I use SQL 2008. Sorry for the misinformation. 我以为我有SQL 2014,但在尝试了Stephan的代码并得到一些错误后,我回去确认并意识到我使用的是SQL 2008.对于错误的信息我很抱歉。 I have also updated the tags 我还更新了标签

On insert, assuming id is an identity and you are just putting in data : 在插入时,假设id是一个标识,并且您只是放入data

insert into table t(id, data, avg)
    select @data, @data * (1.0 / n) + avg * (n - 1.0)/n
    from (select count(*) as cnt, avg(data) as avg
          from t
         ) t;

In SQL Server 2012+, it is easy enough just to get it on output: 在SQL Server 2012+中,只需将其输出即可:

select t.*, avg(data) over (order by id) as cume_avg
from table t

Prior to SQL Server 2012, you would do this with a correlated subquery or apply : 在SQL Server 2012之前,您可以使用相关子查询执行此操作或apply

select t.*,
       (select avg(data)
        from table t2
        where t2.id <= t.id
       ) as cume_avg 
from table t;

Here performance might suffer if the table is large. 如果表很大,性能可能会受到影响。 However, an index on id, data would help. 但是, id, data的索引会有所帮助。

Gordon Linoff has it on insert. Gordon Linoff将其插入。 If you want to do it with a trigger 如果你想用触发器来做

Trigger Method 触发方法

IF OBJECT_ID('myTable') IS NOT NULL
    DROP TABLE myTable;

CREATE TABLE myTable(ID INT, Data INT,[avg] INT);
GO

CREATE TRIGGER trg_running_avg ON myTable
INSTEAD OF INSERT
AS
BEGIN
    INSERT INTO myTable
        SELECT ID,Data,AVG(Data) OVER (ORDER BY ID ROWS UNBOUNDED PRECEDING)
        FROM inserted
END

INSERT INTO myTable(ID,Data)
VALUES  (1,20),(2,4),(3,18)

SELECT *
FROM myTable

View method 查看方法

CREATE VIEW vw_average
AS
SELECT ID,Data,AVG(Data) OVER (ORDER BY ID ROWS UNBOUNDED PRECEDING)
        FROM inserted

Update Pre-Inserted Values with Self-Join 使用自联接更新预先插入的值

UPDATE myTable
SET avg = running_avg
FROM myTable A
INNER JOIN (SELECT ID,AVG(Data) OVER (ORDER BY ID ROWS UNBOUNDED PRECEDING) running_avg FROM myTable) B
ON A.ID = B.ID

Update Pre-Inserted Values with CTE 使用CTE更新预先插入的值

WITH CTE_Update
AS 
(
    SELECT  ID,
            [avg] OldAvg,
            AVG(Data) OVER (ORDER BY ID) AS NewAvg
    FROM myTable
) 
UPDATE CTE_Update SET OldAvg = NewAvg

SQL Server <=2008 doesn't have the OVER(ORDER BY ...) clause for aggregate functions. SQL Server <= 2008没有聚合函数的OVER(ORDER BY ...)子句。

CREATE TRIGGER trg_running_avg ON myTable
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
  UPDATE old
    SET avg = new_avg
  FROM myTable old
  CROSS APPLY (
    SELECT AVG(Data) AS new_avg FROM myTable WHERE ID <= old.ID
  ) new
  --Skip the full table update. Start from the lowest ID that was changed.
  WHERE id >= (SELECT MIN(id) FROM (SELECT ID FROM inserted UNION ALL SELECT ID FROM deleted) t)
END
GO

Use a view for this if you can. 如果可以,请使用视图。 It's a bad design for a change in one row to invalidate data stored in other rows. 对于一行中的更改来说,将其他行中存储的数据无效是一种糟糕的设计。 Rows should represent independent facts. 行应代表独立的事实。

I feel like this should work with a self join: 我觉得这应该适用于自我加入:

select t1.id, t1.data, sum(t2.data)/t1.id as avg
from table t1, table t2
where t1.id>=t2.id group by t1.id

join will give: 加入将给:

t1.id|t1.Data|t2.id|t2.Data
1    |  20   |  1  |   20
2    |  4    |  1  |   20
2    |  4    |  2  |   4
3    |  18   |  1  |   20
3    |  18   |  2  |   4
3    |  18   |  3  |   18

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

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