1

含まれているコードは、私たちの状況を簡略化したものです。に相当する本番テーブルに#MyExampleは20のフィールドがあり、そのすべてに中央値の計算が必要です。したがって、スクリプトの2番目の部分は非常に長くなります。大きな困難ではありませんが、よりコンパクトなソリューションはありますか?

私はAPPLYまたはカスタムの経験がありませんが、これは中央値FUNCTIONのを作成してから使用する必要がある状況ですか?適用が各行に適用されるのではないと思いますか?FUNCTIONAPPLY

/*
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

私は次のように考えていました:

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ではなくUNION ALLを使用してください。そうしないと、サーバーは最終結果を「個別の」レコードにしようとしますが、この場合は必要ありません。(期間はとにかくそれをユニークにします!)

于 2012-10-10T12:20:37.887 に答える
0

以前の返信に基づいて、これに到達しました。これは、列数を拡張するのがはるかに簡単(かつ短く)であり、実行速度も少し速くなります(20列以上の場合はおそらくはるかに高速です!)。ただし、結果は垂直方向ではなく水平方向に返されます。これは、UNPIVOTを使用して再度「解決」できます。中間の#resultテーブルを使用して2つの部分で操作を実行しました。ただし、サブクエリまたはCTEを使用して、1つのステートメントで簡単に実行できます。

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 

その後

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