1

包含的代码是我们情况的简化版本;生产表相当于#MyExample有 20 个字段,所有这些字段都需要计算中位数,因此脚本的第二部分变得很长 - 不是一个巨大的困难,但有更紧凑的解决方案吗?

我没有使用APPLY或 customFUNCTION的经验,但在这种情况下,我们应该FUNCTION为中位数创建一个然后使用APPLY我猜不是因为 apply 应用于每一行吗?

/*
DROP TABLE #MyExample
DROP TABLE #mediantable
*/

CREATE TABLE #MyExample
        (
        customer char(5),
        amountPeriodA numeric(36,8),
        amountPeriodB numeric(36,8),
        amountPeriodC numeric(36,8)
        )
INSERT INTO #MyExample
        values
        ('a',10,20,30),
        ('b',5,10,15),
        ('c',500,100,150),
        ('d',5,1,1),
        ('e',5,1,15),
        ('f',5,10,150),
        ('g',5,100,1500)




SELECT 
        [Period] = 'amountPeriodA',             
        [Median] = AVG(x.amountPeriodA)         
INTO    #mediantable
FROM (
        SELECT 
                r.customer,
                r.amountPeriodA,
                [RowASC] = ROW_NUMBER() OVER(ORDER BY r.amountPeriodA ASC, customer ASC),
                [RowDESC] = ROW_NUMBER() OVER(ORDER BY r.amountPeriodA DESC, customer DESC)
        FROM #MyExample r 
    ) x
WHERE RowASC IN (RowDESC, ROWDESC-1, ROWDESC+1)

union
SELECT 
        [Period] = 'amountPeriodB',             
        [Median] = AVG(x.amountPeriodB)         
FROM (
        SELECT 
                r.customer,
                r.amountPeriodB,
                [RowASC] = ROW_NUMBER() OVER(ORDER BY r.amountPeriodB ASC, customer ASC),
                [RowDESC] = ROW_NUMBER() OVER(ORDER BY r.amountPeriodB DESC, customer DESC)
        FROM #MyExample r 
    ) x
WHERE RowASC IN (RowDESC, ROWDESC-1, ROWDESC+1)

union
SELECT 
        [Period] = 'amountPeriodC',             
        [Median] = AVG(x.amountPeriodC)         
FROM (
        SELECT 
                r.customer,
                r.amountPeriodC,
                [RowASC] = ROW_NUMBER() OVER(ORDER BY r.amountPeriodC ASC, customer ASC),
                [RowDESC] = ROW_NUMBER() OVER(ORDER BY r.amountPeriodC DESC, customer DESC)
        FROM #MyExample r 
    ) x
WHERE RowASC IN (RowDESC, ROWDESC-1, ROWDESC+1)


SELECT * 
FROM #mediantable
4

2 回答 2

0

Building on my previous reply I arrived on this which is a lot easier (and shorter) to expand for the number of columns and even runs a bit faster (probably a lot faster in case of 20+ columns!). However, it returns the results horizontally instead of vertically. This can be 'solved' again using UNPIVOT. I've done the operation in 2 parts using an intermediate #result table; but you could easily do it in a single statement using a subquery or CTE.

DECLARE @rowcount int
DECLARE @first int
DECLARE @last int
DECLARE @divider numeric(36,8)

SELECT @rowcount = COUNT(*) FROM #MyExample

SELECT @first = (CASE WHEN @rowcount % 2 = 1 THEN (@rowcount + 1) / 2 ELSE (@rowcount / 2)     END),
       @last  = (CASE WHEN @rowcount % 2 = 1 THEN (@rowcount + 1) / 2 ELSE (@rowcount / 2) + 1 END),
       @divider = (CASE WHEN @rowcount % 2 = 1 THEN 1 ELSE 2 END)

 SELECT amountPeriodA = SUM(amountPeriodA) / @divider,
        amountPeriodB = SUM(amountPeriodB) / @divider,
        amountPeriodC = SUM(amountPeriodC) / @divider    
   INTO #result
   FROM
 (
 SELECT amountPeriodA = ((CASE WHEN ROW_NUMBER() OVER(ORDER BY amountPeriodA ASC, customer ASC) IN (@first, @last) THEN amountPeriodA ELSE 0.00 END)),
        amountPeriodB = ((CASE WHEN ROW_NUMBER() OVER(ORDER BY amountPeriodB ASC, customer ASC) IN (@first, @last) THEN amountPeriodB ELSE 0.00 END)),
        amountPeriodC = ((CASE WHEN ROW_NUMBER() OVER(ORDER BY amountPeriodC ASC, customer ASC) IN (@first, @last) THEN amountPeriodC ELSE 0.00 END)) 
   FROM #MyExample
  )t 

and then

  SELECT [Period], [Amount] 
    FROM #result as x
    UNPIVOT ( [Amount] FOR Period IN (amountPeriodA, amountPeriodB, amountPeriodC)) As unpvt
于 2012-10-10T12:44:07.527 回答
0

我在想:

DECLARE @rowcount int
DECLARE @first int
DECLARE @last int

SELECT @rowcount = COUNT(*) FROM #MyExample

SELECT @first = (CASE WHEN @rowcount % 2 = 1 THEN (@rowcount + 1) / 2 ELSE (@rowcount / 2)     END),
       @last  = (CASE WHEN @rowcount % 2 = 1 THEN (@rowcount + 1) / 2 ELSE (@rowcount / 2) + 1 END)

SELECT [Period],  
       [Median] = AVG(Amount)
  FROM (SELECT [Period] = 'amountPeriodA',
               Amount   = amountPeriodA, 
               rownbr   = ROW_NUMBER() OVER(ORDER BY amountPeriodA ASC, customer ASC)
          FROM #MyExample

         UNION ALL

        SELECT [Period] = 'amountPeriodB',
               Amount   = amountPeriodB, 
               rownbr   = ROW_NUMBER() OVER(ORDER BY amountPeriodB ASC, customer ASC)
          FROM #MyExample

        UNION ALL

        SELECT [Period] = 'amountPeriodC',
               Amount   = amountPeriodC, 
               rownbr   = ROW_NUMBER() OVER(ORDER BY amountPeriodC ASC, customer ASC)
          FROM #MyExample

          ) r
 WHERE rownbr IN (@first, @last)
 GROUP BY [Period]

这似乎工作得很好,打字少了一点,结果也快了一点……但它仍然“大”。

PS:使用 UNION ALL 而不是 UNION 否则服务器将尝试将最终结果变成“不同的”记录,在这种情况下不需要。(无论如何,时期使它独一无二!)

于 2012-10-10T12:20:37.887 回答