Проценты округления SQL для получения суммы 100% - 1/3 как 0,34, 0,33, 0,33 - PullRequest
3 голосов
/ 06 февраля 2012

В настоящее время я пытаюсь разделить одно значение в процентном столбце.Но так как большинство процентных значений составляет 1/3, я не могу получить 100% с двумя десятичными знаками в значении.Например:

Product    Supplier      percentage         totalvalue        customer_split
                         decimal(15,14)   (decimal(18,2)       decimal(18,2)
--------   --------     ------------     ---------------  ---------------
Product1    Supplier1    0.33            10.00                3.33
Product1    Supplier2    0.33            10.00                3.33
Product1    Supplier3    0.33            10.00                3.33

Итак, здесь мы пропускаем 0,01 в столбце значений, и поставщики хотели бы случайным образом выставить это недостающее значение 0,01 против любого из поставщиков.Я пытался сделать это в двух наборах SQL с временными таблицами, но есть ли простой способ сделать это.Если возможно, как я могу получить 0,34 в самом процентном столбце для одной из вышеуказанных строк?0,01 - незначительное значение, но когда столбец значений равен 1000000000, он имеет значение.

Ответы [ 4 ]

3 голосов
/ 06 февраля 2012

Похоже, что вы делаете какое-то "распределение" здесь.Это обычная проблема всякий раз, когда вы пытаетесь распределить что-то от более высокой гранулярности к более низкой гранулярности, и вам нужно иметь возможность правильно повторно агрегировать в общее значение.

Это становится гораздо большей проблемойпри работе с большими дробями.

Например, если я попытаюсь разделить общую стоимость, скажем, 55,30 доллара на восемь, я получу десятичное значение в 6,9125 доллара для каждого из восьми сегментов.Должен ли я округлить один до $ 6,92, а остальные до $ 6,91?Если я это сделаю, я потеряю ни цента.Мне нужно было бы округлить один до 6,93 доллара, а остальные до 6,91 доллара.Это ухудшается, когда вы добавляете больше ведер для деления на.

Кроме того, когда вы начинаете округление, вы вводите проблемы типа «Должно ли 33,339 быть округлено до 33,34 или 33,33?»

Если ваша бизнес-логика такова, что вы просто хотите взять остатоккроме двух значащих цифр может существовать и добавлять его к одному из значений в долларах «случайным образом», чтобы вы не потеряли ни цента, @Diego находится на правильном пути.

Выполнение этого в чистом SQL являетсянемного сложнее.Начнем с того, что ваш процент не 1/3, а 0,33, что даст общее значение 9,9, а не 10. Я бы сохранил это как отношение или как десятичное поле высокой точности (.33333333333333).

P    S    PCT           Total  
--   --   ------------  ------  
P1   S1   .33333333333  10.00   
P2   S2   .33333333333  10.00   
P3   S3   .33333333333  10.00   


SELECT 
   BaseTable.P, BaseTable.S, 
   CASE WHEN BaseTable.S = TotalTable.MinS 
      THEN BaseTable.BaseAllocatedValue + TotalTable.Remainder
      ELSE BaseTable.BaseAllocatedValue
   END As AllocatedValue
FROM
(SELECT
   P, S, FLOOR((PCT * Total * 100)) / 100 as BaseAllocatedValue,
   FROM dataTable) BaseTable
INNER JOIN
(SELECT
   P, MIN(S) AS MinS,
   SUM((PCT * Total) - FLOOR((PCT * Total * 100)) / 100) as Remainder,
FROM dataTable
GROUP BY P) as TotalTable
ON (BaseTable.P = TotalTable.P)

Похоже, что ваш расчет является равным распределением, основанным на общем количестве продуктов на одного поставщика.Если это так, может быть выгодно удалить процент и вместо этого просто сохранить количество элементов на одного поставщика в таблице.

Если также возможно сохранить флаг, указывающий строку, которая должна получить значение остаткаПрименительно к нему, вы можете назначить на основе этого флага, а не случайно.

3 голосов
/ 06 февраля 2012

запустите это, это даст представление о том, как вы можете решить вашу проблему.Я создал таблицу под названием заказы только с идентификатором, чтобы было легко понять:

create table orders(
customerID int)

insert into orders values(1)
go 3

insert into orders values(2)
go 3

insert into orders values(3)
go 3

эти значения представляют 33%, которые у вас есть

1   33.33
2   33.33
3   33.33

сейчас:

create table #tempOrders(
customerID int,
percentage numeric(10,2))

declare @maxOrder int
declare @maxOrderID int
select @maxOrderID = max(customerID) from orders
declare @total numeric(10,2)
select @total =count(*) from orders
insert into #tempOrders
    select customerID, cast(100*count(*)/@total as numeric(10,2)) as Percentage
    from orders
    group by customerID

update #tempOrders set percentage = percentage + (select 100-sum(Percentage) from #tempOrders)
where customerID =@maxOrderID

этот код будет в основном вычислять процент и порядок с максимальным идентификатором, затем он получает разность от 100 до процентной суммы и добавляет его к заказу с maxID (ваш случайный порядок)

select * from #tempOrders

1   33.33
2   33.33
3   33.34
1 голос
/ 06 января 2015

Это должно быть простой задачей с использованием оконных агрегатных функций. Вы, вероятно, уже используете их для расчета customer_split:

totalvalue  / COUNT(*) OVER (PARTITION BY Product) as customer_split

Теперь суммируйте customer_splits и, если есть разница с общей стоимостью, добавьте (или вычтите) ее в одну случайную строку.

SELECT 
   Product                       
   ,Supplier                      
   ,totalvalue                    
   ,customer_split 
    + CASE
         WHEN COUNT(*) 
              OVER (PARTITION BY Product
                    ROWS UNBOUNDED PRECEDING) = 1 -- get a random row, using row_number/order you might define a specific row
         THEN totalvalue - SUM(customer_split)
                           OVER (PARTITION BY Product)
         ELSE 0
      END
FROM 
 (
   SELECT
      Product                       
      ,Supplier                      
      ,totalvalue                    
      ,totalvalue / COUNT(*) OVER (PARTITION BY Product) AS customer_split
   FROM dropme
 ) AS dt
0 голосов
/ 18 мая 2017

После нескольких проб и испытаний я думаю, что нашел лучшее решение

Идея

  1. Получить Количество всех (Количество (*)) в зависимости от ваших условий
  2. Get Row_Number ()
  3. Проверить, если (Row_Number () value

это часть моего кода

Select your_cols
      ,(Select count(*) from [tbl_Partner_Entity] pa_et where [E_ID] =@E_ID) 
       AS cnt_all
     ,(ROW_NUMBER() over ( order by pe.p_id)) as row_num
     ,Case when (
        (ROW_NUMBER() over ( order by pe.p_id)) < 
        (Select count(*)   from [tbl_Partner_Entity] pa_et where [E_ID] =@E_ID))
      then round(([partnership_partners_perc]*100),2)
      else 
         100-
    ((select sum(round(([partnership_partners_perc]*100),2))  FROM [dbo].
     [tbl_Partner_Entity] PEE where [E_ID] =@E_ID and pee.P_ID != pe.P_ID))
      end AS [partnership_partners_perc_Last]

FROM [dbo].[tbl_Partner_Entity] PE
where [E_ID] =@E_ID
...