Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running Multiplication in T-SQL

GTS Table

CCP months   QUART   YEARS  GTS
----  ------  -----    ----- ---
CCP1    1       1   2015    5
CCP1    2       1   2015    6
CCP1    3       1   2015    7
CCP1    4       2   2015    4
CCP1    5       2   2015    2
CCP1    6       2   2015    2
CCP1    7       3   2015    3
CCP1    8       3   2015    2
CCP1    9       3   2015    1
CCP1    10      4   2015    2
CCP1    11      4   2015    3
CCP1    12      4   2015    4
CCP1     1      1   2016    8
CCP1     2      1   2016    1
CCP1     3      1   2016    3   

Baseline table

CCP BASELINE   YEARS    QUART
----  --------   -----  -----
CCP1    5       2015    1

Expected result

CCP months  QUART    YEARS  GTS   result
----  ------  -----    ----- ---   ------

CCP1    1       1   2015    5     25   -- 5 * 5 (here 5 is the baseline)
CCP1    2       1   2015    6     30   -- 6 * 5 (here 5 is the baseline)
CCP1    3       1   2015    7     35   -- 7 * 5 (here 5 is the baseline)
CCP1    4       2   2015    4     360  -- 90 * 4(25+30+35 = 90 is the basline)
CCP1    5       2   2015    2     180  -- 90 * 2(25+30+35 = 90 is the basline)
CCP1    6       2   2015    2     180  -- 90 * 2(25+30+35 = 90 is the basline)
CCP1    7       3   2015    3     2160.00  -- 720.00 * 3(360+180+180 = 720)
CCP1    8       3   2015    2     1440.00  --   720.00 * 2(360+180+180 = 720)
CCP1    9       3   2015    1     720.00   --   720.00 * 1(360+180+180 = 720)
CCP1    10      4   2015    2     8640.00  --   4320.00
CCP1    11      4   2015    3     12960.00 --   4320.00
CCP1    12      4   2015    4     17280.00 --   4320.00
CCP1     1      1   2016    8     311040.00 --  38880.00
CCP1     2      1   2016    1     77760.00  --  38880.00
CCP1     3      1   2016    3     116640.00 --  38880.00

SQLFIDDLE

Explantion

Baseline table has single baseline value for each CCP.

The baseline value should be applied to first quarter of each CCP and for the next quarters previous quarter sum value will be the basleine.

Here is a working query in Sql Server 2008

;WITH CTE AS
(   SELECT  b.CCP,
            Baseline = CAST(b.Baseline AS DECIMAL(15,2)),
            b.Years,
            b.Quart,
            g.Months,
            g.GTS,
            Result = CAST(b.Baseline * g.GTS AS DECIMAL(15,2)),
            NextBaseline = SUM(CAST(b.Baseline * g.GTS AS DECIMAL(15, 2))) OVER(PARTITION BY g.CCP, g.years, g.quart),
            RowNumber = ROW_NUMBER() OVER(PARTITION BY g.CCP, g.years, g.quart ORDER BY g.Months)
    FROM    #GTS AS g
            INNER JOIN #Base AS b
                ON B.CCP = g.CCP
               AND b.QUART = g.QUART
               AND b.YEARS = g.YEARS
    UNION ALL
    SELECT  b.CCP,
            CAST(b.NextBaseline AS DECIMAL(15, 2)),
            b.Years,
            b.Quart + 1,
            g.Months,
            g.GTS,
            Result = CAST(b.NextBaseline * g.GTS AS DECIMAL(15,2)),
            NextBaseline = SUM(CAST(b.NextBaseline * g.GTS AS DECIMAL(15, 2))) OVER(PARTITION BY g.CCP, g.years, g.quart),
            RowNumber = ROW_NUMBER() OVER(PARTITION BY g.CCP, g.years, g.quart ORDER BY g.Months)
    FROM    #GTS AS g
            INNER JOIN CTE AS b
                ON B.CCP = g.CCP
               AND b.Quart + 1 = g.QUART
               AND b.YEARS = g.YEARS
               AND b.RowNumber = 1
)
SELECT  CCP, Months, Quart, Years, GTS, Result, Baseline
FROM    CTE;

UPDATE :

To work with more than one year

;WITH order_cte
     AS (SELECT Dense_rank() OVER(partition BY ccp ORDER BY years, quart) d_rn,*
         FROM   #gts),
     CTE
     AS (SELECT b.CCP,
                Baseline = Cast(b.Baseline AS DECIMAL(15, 2)),
                g.Years,
                g.Quart,
                g.Months,
                g.GTS,
                d_rn,
                Result = Cast(b.Baseline * g.GTS AS DECIMAL(15, 2)),
                NextBaseline = Sum(Cast(b.Baseline * g.GTS AS DECIMAL(15, 2)))
                                 OVER(
                                   PARTITION BY g.CCP, g.years, g.quart),
                RowNumber = Row_number()
                              OVER(
                                PARTITION BY g.CCP, g.years, g.quart
                                ORDER BY g.Months)
         FROM   order_cte AS g
                INNER JOIN #Baseline AS b
                        ON B.CCP = g.CCP
                           AND b.QUART = g.QUART
                           AND b.YEARS = g.YEARS
         UNION ALL
         SELECT b.CCP,
                Cast(b.NextBaseline AS DECIMAL(15, 2)),
                g.Years,
                g.Quart,
                g.Months,
                g.GTS,
                g.d_rn,
                Result = Cast(b.NextBaseline * g.GTS AS DECIMAL(15, 2)),
                NextBaseline = Sum(Cast(b.NextBaseline * g.GTS AS DECIMAL(15, 2)))
                                 OVER(
                                   PARTITION BY g.CCP, g.years, g.quart),
                RowNumber = Row_number()
                              OVER(
                                PARTITION BY g.CCP, g.years, g.quart
                                ORDER BY g.Months)
         FROM   order_cte AS g
                INNER JOIN CTE AS b
                        ON B.CCP = g.CCP
                           AND b.d_rn + 1 = g.d_rn
                           AND b.RowNumber = 1)
SELECT CCP,
       Months,
       Quart,
       Years,
       GTS,
       Result,
       Baseline
FROM   CTE; 

Now am looking for a solution in Sql Server 2012+ which will utilize SUM OVER(ORDER BY) functionality or any better way

Tried something like this

EXP(SUM(LOG(Baseline * GTS)) OVER (PARTITION BY CCP ORDER BY Years,Quart ROWS UNBOUNDED PRECEDING))

But didnt workout

like image 611
Pரதீப் Avatar asked Sep 25 '15 17:09

Pரதீப்


People also ask

How do you perform a multiplication query in SQL?

All you need to do is use the multiplication operator (*) between the two multiplicand columns ( price * quantity ) in a simple SELECT query. You can give this result an alias with the AS keyword; in our example, we gave the multiplication column an alias of total_price .

Is there a multiply function in SQL?

The SQL multiply ( * ) operator is used to multiply two or more expressions or numbers.

How do you write a multiplication aggregate function in SQL?

SQL> with yourTable as 2 ( select 1 yourColumn from dual union all 3 select 2 from dual union all 4 select 4 from dual union all 5 select 8 from dual 6 ) 7 select EXP(SUM(LN(yourColumn))) As ColumnProduct from yourTable 8 / COLUMNPRODUCT ------------- 64 1 row selected.

Which type of SQL command is used to multiplication of data?

Multiplication (*) : It is use to perform multiplication of data items.


1 Answers

Following solution assumes there are always 3 rows per quarter (only the last quarter might be partial), single SELECT, no recursion :-)

WITH sumQuart AS
 (
   SELECT *,
      CASE
        WHEN ROW_NUMBER() -- for the 1st month in a quarter
             OVER (PARTITION BY CCP, Years, Quart
                   ORDER BY months) = 1
                  -- return the sum of all GTS of this quarter
        THEN SUM(GTS) OVER (PARTITION BY CCP, Years, Quart)
        ELSE NULL -- other months
      END AS sumGTS
   FROM gts
 )
,cte AS
 (
   SELECT 
      sq.*,
      COALESCE(b.Baseline, -- 1st quarter
               -- product of all previous quarters
               CASE
                 WHEN MIN(ABS(sumGTS)) -- any zeros?
                      OVER (PARTITION BY sq.CCP ORDER BY sq.Years, sq.Quart, sq.Months 
                            ROWS BETWEEN UNBOUNDED PRECEDING AND 3 PRECEDING) = 0 
                   THEN 0  
                 ELSE -- product
                      EXP(SUM(LOG(NULLIF(ABS(COALESCE(b.Baseline,1) * sumGTS),0)))
                          OVER (PARTITION BY sq.CCP ORDER BY sq.Years, sq.Quart, sq.Months 
                                ROWS BETWEEN UNBOUNDED PRECEDING AND 3 PRECEDING)) -- product
                      -- odd number of negative values -> negative result
                    * CASE WHEN COUNT(CASE WHEN sumGTS < 0 THEN 1 END) 
                                OVER (PARTITION BY sq.CCP ORDER BY sq.Years, sq.Quart, sq.Months 
                                      ROWS BETWEEN UNBOUNDED PRECEDING AND 3 PRECEDING) % 2 = 0 THEN 1 ELSE -1 END
               END) AS newBaseline
   FROM sumQuart AS sq
   LEFT JOIN BASELINE AS b
          ON B.CCP = sq.CCP
          AND b.Quart = sq.Quart
          AND b.Years = sq.Years
 )
SELECT 
   CCP, months, Quart, Years, GTS,
   round(newBaseline * GTS,2),
   round(newBaseline,2)
FROM cte

See Fiddle

EDIT: Added logic to handle values <= 0 Fiddle

like image 118
dnoeth Avatar answered Sep 16 '22 17:09

dnoeth