4

考虑下表:

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 中做到这一点。(日期是实际日期,而不仅仅是星期几。)

4

5 回答 5

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

This gets the data in a normalized format . . . , , .

If you really need it pivoted (that is, going across in columns), you can use the pivot keyword, or do conditional aggregation sums.

于 2012-12-26T00:54:28.960 回答
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);

那么用 a 来做这件事并不太难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 小提琴。

于 2012-12-26T00:52:02.420 回答
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

于 2012-12-26T00:44:54.123 回答
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

于 2012-12-26T00:46:56.737 回答
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 要求您指定要旋转的行/列值/名称,因此必须动态生成查询。

于 2016-12-19T21:00:32.240 回答