在Sql Server中计算

时间:2017-01-23 10:17:48

标签: sql sql-server tsql sql-server-2012

我尝试执行以下计算

示例数据:

CREATE TABLE #Table1
  (
     rno   int identity(1,1),
     ccp   varchar(50),
     [col1] INT,
     [col2] INT,
     [col3] INT,
     col4 as [col2]/100.0
  );

INSERT INTO #Table1
            (ccp,[col1],[col2],[col3])
VALUES      ('ccp1',15,10,1100),
            ('ccp1',20,10,1210),
            ('ccp1',30,10,1331),
            ('ccp2',10,15,900),
            ('ccp2',15,15,1000),
            ('ccp2',20,15,1010)

+-----+------+------+------+------+----------+
| rno | ccp  | col1 | col2 | col3 |   col4   |
+-----+------+------+------+------+----------+
|   1 | ccp1 |   15 |   10 | 1100 | 0.100000 |
|   2 | ccp1 |   20 |   10 | 1210 | 0.100000 |
|   3 | ccp1 |   30 |   10 | 1331 | 0.100000 |
|   4 | ccp2 |   10 |   15 |  900 | 0.150000 |
|   5 | ccp2 |   15 |   15 | 1000 | 0.150000 |
|   6 | ccp2 |   20 |   15 | 1010 | 0.150000 |
+-----+------+------+------+------+----------+

注意:每个ccp不仅可以3条记录N no.of记录

预期结果:

1083.500000 --1100 - (15 * (1+0.100000))
1169.850000 --1210 - ((20 * (1+0.100000)) + (15 * (1+0.100000)* (1+0.100000)) )
1253.835000 --1331 - ((30 * (1+0.100000)) + (20 * (1+0.100000)* (1+0.100000)) + (15 * (1+0.100000)* (1+0.100000) *(1+0.100000)) )
888.500000  --900 - (10 * (1+0.150000))
969.525000  --1000 - ((15 * (1+0.150000)) + (10 * (1+0.150000)* (1+0.150000)) )
951.953750  --1010 - ((20 * (1+0.150000)) + (15 * (1+0.150000)* (1+0.150000)) + (10 * (1+0.150000)* (1+0.150000) *(1+0.150000)) )

我知道我们可以使用递归CTE来做到这一点,因为我必须为超过500万条记录执行此操作,因此效率不高。

我希望实现类似于基于集合的方法

对于ccp ccp1

SELECT col3 - ( col1 * ( 1 + col4 ) )
FROM   #Table1
WHERE  rno = 1

SELECT rno,
       col3 - ( ( col1 * Power(( 1 + col4 ), 1) ) + ( Lag(col1, 1)
                                                        OVER(
                                                          ORDER BY rno ) * Power(( 1 + col4 ), 2) ) )
FROM   #Table1
WHERE  rno IN ( 1, 2 )

SELECT rno,
       col3 - ( ( col1 * Power(( 1 + col4 ), 1) ) + ( Lag(col1, 1)
                                                        OVER(
                                                          ORDER BY rno ) * Power(( 1 + col4 ), 2) ) + ( Lag(col1, 2)
                                                                                                          OVER(
                                                                                                            ORDER BY rno ) * Power(( 1 + col4 ), 3) ) )
FROM   #Table1
WHERE  rno IN ( 1, 2, 3 ) 

有没有办法在单个查询中计算?

更新:

仍然愿意接受建议。我强烈相信应该有一些使用SUM () Over(Order by)窗口聚合函数来做到这一点。

6 个答案:

答案 0 :(得分:29)

最后,我使用以下方法获得了结果

gsub

结果:

SELECT a.*,
       col3 - res AS Result
FROM   #TABLE1 a
       CROSS apply (SELECT Sum(b.col1 * Power(( 1 + b.COL2 / 100.00 ), new_rn)) AS res
                    FROM   (SELECT Row_number()
                                     OVER(
                                       partition BY ccp
                                       ORDER BY rno DESC) new_rn,*
                            FROM   #TABLE1 b
                            WHERE  a.ccp = b.ccp
                                   AND a.rno >= b.rno)b) cs

答案 1 :(得分:10)

这个答案可能令人失望,但您可能会发现迭代CLR方法与任何TSQL方法都具有竞争优势。

尝试以下操作(基于Running sums yet again: SQLCLR saves the day!

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

public partial class StoredProcedures
{
    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void StackoverflowQuestion41803909()
    {
        using (SqlConnection conn = new SqlConnection("context connection=true;"))
        {
            SqlCommand comm = new SqlCommand();
            comm.Connection = conn;
            comm.CommandText = @"
SELECT [rno],
       [ccp],
       [col1],
       [col2],
       [col3],
       [col4]
FROM   Table1
ORDER  BY ccp,
          rno 
";

            SqlMetaData[] columns = new SqlMetaData[7];
            columns[0] = new SqlMetaData("rno", SqlDbType.Int);
            columns[1] = new SqlMetaData("ccp", SqlDbType.VarChar, 50);
            columns[2] = new SqlMetaData("col1", SqlDbType.Int);
            columns[3] = new SqlMetaData("col2", SqlDbType.Int);
            columns[4] = new SqlMetaData("col3", SqlDbType.Int);
            columns[5] = new SqlMetaData("col4", SqlDbType.Decimal, 17, 6);
            columns[6] = new SqlMetaData("result", SqlDbType.Decimal, 17, 6);

            SqlDataRecord record = new SqlDataRecord(columns);

            SqlContext.Pipe.SendResultsStart(record);

            conn.Open();

            SqlDataReader reader = comm.ExecuteReader();

            string prevCcp = null;
            decimal offset = 0;

            while (reader.Read())
            {
                string ccp = (string)reader[1];
                int col1 = (int)reader[2];
                int col3 = (int)reader[4];
                decimal col4 = (decimal)reader[5];

                if (prevCcp != ccp)
                {
                    offset = 0;
                }

                offset = ((col1 + offset) * (1 + col4));
                record.SetInt32(0, (int)reader[0]);
                record.SetString(1, ccp);
                record.SetInt32(2, col1);
                record.SetInt32(3, (int)reader[3]);
                record.SetInt32(4, col3);
                record.SetDecimal(5, col4);
                record.SetDecimal(6, col3 - offset);

                SqlContext.Pipe.SendResultsRow(record);

                prevCcp = ccp;
            }

            SqlContext.Pipe.SendResultsEnd();
        }
    }
};

答案 2 :(得分:5)

另一个选择

<img id="album_cover" src="space-160x160.jpg" alt="Kalliope Image">

const albumCover = document.querySelector("#album_cover");
songPick.addEventListener("change", audioImageSource);

function audioImageSource() {
    let option = document.querySelector("select").options;
    let indexImageSong = document.querySelector("select").selectedIndex;
    audio.src = songPick.value;
    console.log(option[indexImageSong].index);
    console.log(option[indexImageSong]);
    switch (option[indexImageSong].index) {
        case 0:
            console.log(albumCover);
            albumCover.src = 'space-160x160.jpg';
            break; 
        case 1:
            albumCover.src = 'friendly-alien-planet-160x160.jpg';
            break;
        case 2:
            albumCover.src = 'Soundscape-music-160x160.jpg';
            break;
        case 3:
            albumCover.src = 'soundscape-160x160.jpg';
            break;    
    }

}

答案 3 :(得分:4)

cross apply的方法。不确定这是否比使用WITH T AS (SELECT *, ROW_NUMBER() OVER(PARTITION BY CCP ORDER BY RNO) AS RN FROM #TABLE1) SELECT T1.RNO, T1.CCP, T1.COL1, T1.COL2, T1.COL3, T1.COL3-SUM(T2.COL1*POWER(1+T1.COL2/100.0,T1.RN-T2.RN+1)) AS RES FROM T T1 JOIN T T2 ON T1.CCP=T2.CCP AND T1.RN>=T2.RN GROUP BY T1.RNO, T1.CCP, T1.COL1, T1.COL2, T1.COL3 的版本更有效。

sendSpecialChat

Sample Demo

答案 4 :(得分:3)

试试这个:

;with 
    val as (
        select 
            *, 
            (1 + col2 / 100.00) val,
            row_number() over(partition by ccp order by rno desc) rn
        from #Table1),
res as (
        select 
            v1.rno, 
            --min(v1.ccp) ccp,
            --min(v1.col1) col1, 
            --min(v1.col2) col2, 
            min(v1.col3) col3, 
            sum(v2.col1 * power(v2.val, 1 + v2.rn - v1.rn)) sum_val
        from val v1
        left join val v2 on v2.ccp = v1.ccp and v2.rno <= v1.rno
        group by v1.rno)
select *, col3 - isnull(sum_val, 0)
from res

但性能取决于索引。发布索引结构以获取详细信当您将其拆分为更多临时表时,可以实现最佳性能。

答案 5 :(得分:2)

在玩了一段时间之后,我相信对于是否可以用sum() over (order by)来完成这个问题的答案的答案是否定的。这段代码尽可能接近:

select  *, col3 - sum(col1 * power(1 + col4, row_num)) over (partition by ccp order by col1)
from    (
        select  *, row_number() over (partition by ccp order by rno asc) row_num
        from    @Table1
        ) a
order   by 1,2;

这将返回每个ccp组中第一行的正确结果。通过使用rno desc计算row_num,然后每个ccp中的最后一行将是正确的。

似乎只有通过语法建议的简单方式才能使其工作的唯一方法是:

  1. 语法支持引用聚合函数中的实际行。据我所知,这确实存在于T-SQL中。
  2. 窗口函数中窗口函数的语法支持。根据以下错误,在T-SQL中也不允许这样做:
  3.   

    窗口函数不能在另一个窗口的上下文中使用   功能或聚合。

    这是一个有趣的问题。我很好奇这个解决方案如何对大数据集执行,即使实际结果不正确。