SQL Rounding Percentages(合計100% - 1/3を0.34,0.33,0.33とする)

私は現在、パーセント値の列で1つの値を分割しようとしています。しかし、パーセンテージの値の大部分は1/3なので、値の小数点以下2桁で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の値をサプライヤのいずれかに対してランダムに配置したいと考えています。私は一時テーブルを持つ2つのSQLセットでこれを実行しようとしてきましたが、これを行うための簡単なの方法があります。可能であれば、上記の行のいずれかのパーセント列自体に0.34をどのようにして取得できますか? 0.01は無視できる値ですが、値の列が1000000000の場合は意味があります。

3
すでに計算されたパーセンテージの上にsqlの2番目のセットを更新/使用することができますが、それは私が現在取り組んでいるものですが、同じSQLでパーセンテージを計算した単純なロジックはありませんか?
追加された 著者 Enjoy coding,
パーセントに対して小数点以下14桁がありますが、 percentage の列に 0.33333333333333 を入れてはどうでしょうか?
追加された 著者 beny23,
「あなたは単にサプライヤのSETパーセンテージを更新できます== 0.34 WHERE ...」と言えば、この質問を読むだけで実際に必要なものを理解することがどれほど難しいか理解できます。
追加された 著者 Stefan Steinegger,
Martin Fowlerの "Quantity"パターンを見てください。 martinfowler.com/eaaDev/quantity.html 特に、資金の分割に関する議論を見てください。この例の除算は、単一の値ではなく、値の配列を返します。値の配列は合計に加算されます。私は、それが得られるほど簡単だとわかった。
追加された 著者 Glenn,

4 答え

これを実行すると、あなたの問題をどのように解決できるかについてのアイデアが得られます。 わかりやすいIDで注文したテーブルを作成しました。

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 [email protected]

このコードは、基本的に最大IDでパーセンテージと注文を計算し、100からパーセンテージの合計までdiferenceを取得し、maxID(ランダムな順序)で注文に追加します。

select * from #tempOrders

1   33.33
2   33.33
3   33.34
3
追加された
SQLの2つのセットは簡単にこのタスクを行うことができ、それは私が現在やっているものです。しかし、私はTeradata上で1時間に何十億もの行を実行する必要があるため、不足しているデータをランダムなサプライヤに対して自動的に割り当てて、パフォーマンスの低下を最小限に抑えるシングルステッププロセスを探しています。
追加された 著者 Enjoy coding,

ここで何らかのタイプの「割り当て」をしているようです。これは、高精度のグラニュラリティからより低いグラニュラリティに何かを割り当てようとするときはいつでも共通の問題であり、合計値に正しく再集計できる必要があります。

より大きな分数を扱う場合、これははるかに大きな問題になります。

たとえば、$ 55.30という合計値を8で割ると、8つのバケットのそれぞれに対して$ 6.9125の小数値が得られます。 1つを$ 6.92に、残りを$ 6.91に丸めてください。もしそうすれば、私は1セントを失うだろう。 1つを$ 6.93に、他のものを$ 6.91に丸める必要があります。あなたが分割するバケツを追加すると、これは悪化します。

さらに、ラウンドを開始するときに、「33.339を33.34または33.33に丸める必要がありますか」などの問題が導入されます。

あなたのビジネスロジックが、2桁の有効数字を超えて残りのものを取っておき、それをドル値の1つに「無作為に」追加してセントを失うことがないようなものなら、@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
追加された
これは私が期待していたものとほとんど同じです。どうもありがとうございました。今私はこのロジックを適用し、Teradata上でこの作業を行うために調整する必要があります。どうもありがとうございました。
追加された 著者 Enjoy coding,
うん。しかし、私がここで示した例は、私のシナリオの正確な複製ではありません。私はすでに、より複雑な結合で分割作業の大部分を行うSQLのセットを持っています。上記の例は、理解しやすいようにすることでした。非常にN西にありがとうございます。
追加された 著者 Enjoy coding,
プライマリ・インデックスがプロダクト・キー上にある場合は、各AMPが各計算を処理する必要があるため、かなり良いパフォーマンスが得られるはずです。
追加された 著者 N West,

これは、Windowed Aggregate Functionsを使用すると簡単な作業になるはずです。おそらくそれらを customer_split の計算に使用します。

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

今、customer_splitsを合計し、合計値との差がある場合、それを1つのランダムな行に追加(または減算)します。

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
1
追加された

複数の試行とテストの後、私はより良い解決策を見つけたと思う

アイデア

  1. Get Count of all(Count(*)) based on your conditions
  2. Get Row_Number()
  3. Check if (Row_Number() value < Count(*)) Then select round(curr_percentage,2) Else Get sum of all other percentage(with round) and subtract it from 100 This steps will select current percentage every time EXCEPT Last one will be 100 - the sum of all other percentages

これは自分のコードの一部です

Select your_cols
      ,(Select count(*) from [tbl_Partner_Entity] pa_et where [E_ID] [email protected]_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] [email protected]_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] [email protected]_ID and pee.P_ID != pe.P_ID))
      end AS [partnership_partners_perc_Last]

FROM [dbo].[tbl_Partner_Entity] PE
where [E_ID] [email protected]_ID
0
追加された