2009-09-17 1 views
3

다음과 같이 정의 된 테이블이 있습니다.전체 합계를 변경하지 않고 단일 SQL 요청으로 열을 어떻게 순환시킬 수 있습니까?

create table #tbFoo 
(bar float) 

그리고 총합 (정수 또는 매우 알려진 것으로 변경하지 않고 열 막대에 포함 된 모든 값을 반올림하는 방법을 찾고 있습니다. float 숫자 정밀도로 인해 정수에 가깝습니다).

모든 값을 가장 가까운 정수로 반올림하는 것은 작동하지 않습니다 (예 : 1,5; 1,5는 1, 2 또는 2로 반올림 됨).

여러 요청을 사용하여 이것을 쉽게 수행 할 수 있습니다 (예 : 원래 합계 저장, 반올림, 새로운 합계 계산 및 원래 합계로 돌아 가기 위해 필요한만큼의 행 업데이트). 그러나 이것은 매우 우아한 해결책은 아닙니다.

단일 SQL 요청을 사용하여이를 수행하는 방법이 있습니까?


SQL Server 2008을 사용하고 있으므로이 특정 공급 업체를 이용하는 솔루션을 환영합니다.


편집 : 이전 값과 새로운 값의 차이를 최소화하는 요청을 찾고 있습니다. 즉, 더 큰 값을 반올림 한 값은 반올림하지 말아야하며 반대로 반올림하면

값을 반올림하지 말아야합니다.
+0

좋은 질문입니다. 나는 그것을 블로그 포스트를위한 나의 todo 목록에 넣을 것이다. – Quassnoi

답변

3

업데이트 :

이 솔루션 내 블로그에있는 문서에 대한 자세한 설명을 참조하십시오 :


당신은 누적 오프를 유지할 필요 각 값에 대해 설정합니다

1.2 (1 + 0.0) ~ 1 1 1.2 +0.2 
1.2 (1 + 0.2) ~ 1 2 2.4 +0.4 
1.2 (1 + 0.4) ~ 1 3 3.6 +0.6 
1.2 (1 + 0.6) ~ 2 5 4.8 -0.2 
1.2 (1 - 0.2) ~ 1 6 6.0 0.0 

이 쉽게 MySQL에서 수행되지만 SQL Server 당신이 커서를 작성하거나 (덜 효율적이다) 누적 부속 선택을 사용해야합니다.

업데이트 :

쿼리는 다음의 값과 가장 가까운 작은 정수로 내림들의 합 간의 차를 선택한다.

이렇게하면 올림 할 값의 수 (N)가됩니다.

그런 다음 분수 부분 (천장에 가까운 쪽)을 값으로 정렬하고 처음에는 N을 반올림하고 나머지는 내림합니다.그런 다음

declare @Sum float, @RoundedSum float, @Cnt int 

select @Sum = sum(bar), @RoundedSum = sum(round(bar)), @Cnt = count(*) 
from #tbFoo 

:

SET NOCOUNT ON 
GO 
SELECT RAND(0.20090917) 
DECLARE @mytable TABLE (value FLOAT NOT NULL) 
DECLARE @cnt INT; 
SET @cnt = 0; 
WHILE @cnt < 100 
BEGIN 
     INSERT 
     INTO @mytable 
     VALUES (FLOOR(RAND() * 100)/10) 
     SET @cnt = @cnt + 1 
END 

INSERT 
INTO @mytable 
SELECT 600 - SUM(value) 
FROM @mytable 
+1

@Quassnoi : 답변 해 주셔서 감사합니다. 귀하의 솔루션에서 1.4/1.4/1.2는 1/1/2로 반올림되어 1.4가 1.2보다 크므로 '공정하지 않습니다.' – Brann

+0

@Quassnoi : 정확히 내가 찾고 있던 종류입니다. 감사 ! – Brann

+0

@Quassnoi : 내 현실 세계 문제에서 다른 열 'ListID'가 있는데 ListID 당 합계를 일정하게 유지하고 싶습니다. 그룹 별 절을 사용하여 요청을 조정했으며 거의 ​​작동하지만 ROW_NUMBER는 요청별로 그룹화하지 않고 전체 요청에 적용됩니다. 내가 할 수있는 일이 있습니까? – Brann

0

먼저 둥근 합과 실제 금액의 차이를 얻고, 레코드의 수 : 여기

SELECT value, 
     FLOOR(value) + CASE WHEN ROW_NUMBER() OVER (ORDER BY value - FLOOR(value) DESC) <= cs THEN 1 ELSE 0 END AS nvalue 
FROM (
     SELECT cs, value 
     FROM (
       SELECT SUM(value) - SUM(FLOOR(value)) AS cs 
       FROM @mytable 
       ) c 
     CROSS JOIN 
       @mytable 
     ) q 

는 테스트 데이터에 대한 스크립트의 차이를 반올림하기 전에 모든 값에 똑같이 분산시킵니다.

declare @Offset float 

set @Offset = (@Sum - @RoundedSum)/@Cnt 

select bar = round(bar + @Offset) 
from #tbFoo 
+1

@ guffa : 0.2/0.2/0.2/0.2/0.2, sum = 1, 반올림 = 0, offest = 0.2, finalresulst = 0/0/0/0/0, finalsum = 0. – Brann

+0

@Brann : 예, 맞습니다. 올바른 결과를주지는 못합니다. 그러나 편차를 균등하게 분산시키는 원리는 건전합니다. 그래도 좀 더 줄거야. – Guffa

1

정수 값 (+ -0.5) 내에서만 요소가 정확한 n 개의 값 목록이있는 경우 해당 요소의 합계는 누적 오류 또는 + - (n * 0.5)가됩니다. 목록에 6 개 요소가있는 경우 숫자가 합쳐져야합니다. 최악의 시나리오는 정수 값을 추가하는 경우 3을 사용하지 않는 것입니다.

합계를 만들기 위해 10.2를 11로 표시하는 방법을 찾은 경우 해당 요소의 정밀도를 +0.5에서 + -0.8로 변경했는데 정수를 볼 때 반 직관적입니까?

한 가지 가능한 해결책은 검색 단계가 아닌 디스플레이에서만 일부 형식 문자열을 사용하여 숫자를 반올림하는 것입니다. 각 숫자는 가능한 한 실제 값에 가깝지만 합계도 정확합니다.

예 : 1/3의 값을 가지며 전체 번호가 매겨진 백분율로 표시되면 33, 33 및 33을 표시해야합니다. 다른 작업을 수행하려면 + 0.5는 개별 값입니다. 이것이 가능한 가장 좋은 값이기 때문에 총계가 100 %로 표시되어야합니다 (이미 반올림 한 값의 합계를 사용하는 것과 반대)

또한 float를 사용하여 이미 제한을 도입했습니다 왜냐하면 당신은 정확하게 0.1을 표현할 방법이 없기 때문입니다. 그것에 대한 자세한 내용은 What Every Computer Scientist Should Know About Floating-Point Arithmetic

+0

@ 갈루몬 : 예, 10.2를 11로 보여주는 것은 내가 성취하려고하는 것입니다. 그리고 그것은 사용자가보고 싶어하는 것입니다. – Brann

관련 문제