sql中的指数衰减

时间:2012-12-25 23:40:40

标签: sql tsql

考虑下表:

Person | 1/1/13 | 1/2/13  | 1/3/13 | 1/4/13 | 1/5/13
Bill   | 4      | 2       | 1      | .5     | .25
Jane   | 0      | 0       | 2      | 1      | .5
Mary   | 0      | 8       | 4      | 2      | 1
-------------------------------------------------
Total  | 4      | 10      | 7      | 3.5    | 1.75

这来自下表:

Bill | 1/1/13 | 4
Jane | 1/3/13 | 2
Mary | 1/2/13 | 8

基本上,我们知道第一天,然后我们假设每隔一天减少一半的价值。我想从第一张表中获得“总计”行。

有没有办法在(T-)SQL中执行此操作?我已经在R中创建了它,但我完全不知道如何在SQL中实现它。 (日期是实际日期,而不仅仅是星期几。)

5 个答案:

答案 0 :(得分:1)

如果您的表格如下:

CREATE TABLE t (person VARCHAR(7), day_of_week_name VARCHAR(7), value NUMERIC);
INSERT INTO t VALUES ('Bill', 'Monday', 4);
INSERT INTO t values ('Jane', 'Weds', 2);
INSERT INTO t VALUES ('Mary', 'Tuesday', 8);

你有一些day_of_week表格,其中包含天的相对位置:

CREATE TABLE day_of_week (name VARCHAR(7), position INT);
INSERT INTO day_of_week VALUES ('Monday', 1);
INSERT INTO day_of_week values ('Tuesday', 2);
INSERT INTO day_of_week VALUES ('Weds', 3);
INSERT INTO day_of_week VALUES ('Thurs', 4);
INSERT INTO day_of_week VALUES ('Friday', 5);

然后使用PIVOT执行此操作 丑陋:

SELECT Monday, Tuesday, Weds, Thurs, Friday
  FROM ( SELECT dow2.name AS day_of_week_name,
                t.value / power(2, dow2.position - dow1.position) AS decayed_value
           FROM t
           JOIN day_of_week AS dow1
             ON t.day_of_week_name = dow1.name
           JOIN day_of_week AS dow2
             ON dow1.position <= dow2.position
       ) AS b
 PIVOT ( SUM(decayed_value)
           FOR day_of_week_name
             IN (Monday, Tuesday, Weds, Thurs, Friday)
       ) AS pvt
;

SQL Fiddle here.

答案 1 :(得分:1)

with dow as (
     select 'Monday' as dow, 1 as num union all
     select 'Tuesday', 2 union all
     select 'Wedneday', 3 union all
     select 'Thursday', 4 union all
     select 'Friday', 5 union all
     select 'Saturday', 6 union all
     select 'Sunday', 7
    )
select dow.dow,
       t.num * power(cast(0.5 as float), dow.dow - t.dow))
from (select t.*, dow.dow as dow
      from t join
           dow
           on t.dow = dow.dow
     ) t join
     dow
     on t.dow >= dow.dow
group by t.name, dow.dow

这将以标准化格式获取数据。 。 。 ,,,。

如果你真的需要它进行透视(即,在列中进行),你可以使用pivot关键字,或者进行条件聚合和。

答案 2 :(得分:0)

我创造了一个怪物,但它有效。

这是mysql,但我确信它很容易转换为tsql

select sum(mon)
     , sum(tue)
     , sum(wed)
     , sum(thur)
     , sum(fri)
  from (
            select person
                 , if (1 < field(day, 'mon', 'tue', 'wed', 'thur', 'fri'), 0, val / pow(2, 1 - field(day, 'mon', 'tue', 'wed', 'thur', 'fri'))) as mon
                 , if (2 < field(day, 'mon', 'tue', 'wed', 'thur', 'fri'), 0, val / pow(2, 2 - field(day, 'mon', 'tue', 'wed', 'thur', 'fri'))) as tue
                 , if (3 < field(day, 'mon', 'tue', 'wed', 'thur', 'fri'), 0, val / pow(2, 3 - field(day, 'mon', 'tue', 'wed', 'thur', 'fri'))) as wed
                 , if (4 < field(day, 'mon', 'tue', 'wed', 'thur', 'fri'), 0, val / pow(2, 4 - field(day, 'mon', 'tue', 'wed', 'thur', 'fri'))) as thur
                 , if (5 < field(day, 'mon', 'tue', 'wed', 'thur', 'fri'), 0, val / pow(2, 5 - field(day, 'mon', 'tue', 'wed', 'thur', 'fri'))) as fri
            from (
                (select 'bill' as person, 'mon' as day, 4 as val from dual)
                union
                (select 'jane', 'wed', 2 from dual)
                union
                (select 'mary', 'tue', 8 from dual)
            ) t

) tbl

http://sqlfiddle.com/#!2/d41d8/5249

答案 3 :(得分:0)

这样应该在sql server中工作:

with data 
     as (select ppl.name, 
                case 
                  when ppl.day_of_week = 'Monday' then val 
                  when 1 < day_val then 0 
                end monday, 
                case 
                  when ppl.day_of_week = 'Tuesday' then val 
                  when 2 < day_val then 0 
                  else val * Power(Cast(0.5 as FLOAT), ( 2 - day_val )) 
                end Tuesday, 
                case 
                  when ppl.day_of_week = 'Weds' then val 
                  when 3 < day_val then 0 
                  else val * Power(Cast(0.5 as FLOAT), ( 3 - day_val )) 
                end Weds, 
                case 
                  when ppl.day_of_week = 'Thurs' then val 
                  when 4 < day_val then 0 
                  else val * Power(Cast(0.5 as FLOAT), ( 4 - day_val )) 
                end thurs, 
                case 
                  when ppl.day_of_week = 'Friday' then val 
                  when 5 < day_val then 0 
                  else val * Power(Cast(0.5 as FLOAT), ( 5 - day_val )) 
                end friday 
           from (select name, 
                        day_of_week, 
                        val, 
                        case day_of_week 
                          when 'Monday' then 1 
                          when 'Tuesday' then 2 
                          when 'Weds' then 3 
                          when 'Thurs' then 4 
                          when 'Friday' then 5 
                        end day_val 
                   from person) ppl) 
select coalesce(name, 'Total') as "Name", 
       Sum(monday) as "Monday", 
       Sum(tuesday) as "Tuesday", 
       Sum(weds) as "Weds", 
       Sum(thurs) as "Thursday", 
       Sum(friday) as "Friday" 
  from data 
 group by name with rollup 

http://sqlfiddle.com/#!6/c4c6e/1

答案 4 :(得分:0)

以下查询假定存在此表变量:

DECLARE @data TABLE (
    Person varchar(50),
    Date date,
    Value int
);

INSERT @data VALUES 
('Bill', '2013-01-01', 4),
('Jane', '2013-01-03', 2),
('Mary', '2013-01-02', 8);

这是获取原始衰变数据的递归CTE方法:

WITH decay AS (
    SELECT Person, Date, CAST(Value as float) AS Value
    FROM @data
    UNION ALL
    SELECT Person, DATEADD(day, 1, Date) AS Date, Value / 2 AS Value
    FROM decay
    WHERE Value / 2 >= 0.25
)
SELECT Person, Date, SUM(Value) AS Value
FROM decay
GROUP BY Person, Date;

我想这可能会解决大多数人的需求,但是如果你需要它,你可以指定列的日期:

WITH decay AS (
    SELECT Person, Date, CAST(Value as float) AS Value
    FROM @data
    UNION ALL
    SELECT Person, DATEADD(day, 1, Date) AS Date, Value / 2 AS Value
    FROM decay
    WHERE Value / 2 >= 0.25
)
SELECT pvt.Person, 
    ISNULL(pvt.[2013-01-01], 0) AS [2013-01-01], 
    ISNULL(pvt.[2013-01-02], 0) AS [2013-01-02], 
    ISNULL(pvt.[2013-01-03], 0) AS [2013-01-03], 
    ISNULL(pvt.[2013-01-04], 0) AS [2013-01-04], 
    ISNULL(pvt.[2013-01-05], 0) AS [2013-01-05]
FROM (
    SELECT Person, Date, SUM(Value) AS Value
    FROM decay
    GROUP BY Person, Date
) AS q
PIVOT (
    SUM(Value)
    FOR Date
    IN ([2013-01-01], [2013-01-02], [2013-01-03], [2013-01-04], [2013-01-05])
) AS pvt;

这为您提供了结果集:

Person  2013-01-01  2013-01-02  2013-01-03  2013-01-04  2013-01-05
------------------------------------------------------------------
Bill    4           2           1           0.5         0.25
Jane    0           0           2           1           0.5
Mary    0           8           4           2           1

不幸的是,PIVOT要求您指定要旋转的行/列值/名称,以便必须动态生成查询。