包含的代码是我们情况的简化版本;相当于#MyExample
的生产表有20个字段,所有这些字段都需要中位数计算,因此脚本的第二部分变得非常长 - 这不是一个巨大的难题,但有更紧凑的解决方案吗?
我没有使用APPLY
或自定义FUNCTION
的经验,但是我们应该为中位数创建FUNCTION
,然后使用APPLY
I' m猜不是应用于每一行吗?
/*
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
答案 0 :(得分: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,否则服务器将尝试将最终结果变为“不同”记录,在这种情况下不需要。 (期间无论如何都会让它变得独一无二!)
答案 1 :(得分:0)
基于我之前的回复,我得到了这个,这更容易(和更短)扩展列数,甚至运行更快(在20+列的情况下可能更快!)。但是,它会水平返回结果而不是垂直返回结果。这可以使用UNPIVOT再次“解决”。 我使用中间的#result表完成了两部分操作;但您可以使用子查询或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
然后
SELECT [Period], [Amount]
FROM #result as x
UNPIVOT ( [Amount] FOR Period IN (amountPeriodA, amountPeriodB, amountPeriodC)) As unpvt