2012-02-06 11 views
3

現在、パーセント値の列で1つの値を分割しようとしています。しかし、パーセンテージの値の大部分は1/3なので、値の小数点以下2桁で100%を得ることはできません。たとえば:合計を100% - 1/3にするためのSQL丸めのパーセンテージ0.34、0.33、0.33

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の2つのセットでこれを実行しようとしていますが、シンプルなという方法があります。可能であれば、上記の行のいずれかのパーセント列自体に0.34をどのようにして取得できますか? 0.01は無視できる値ですが、値の列が1000000000の場合は意味があります。

+0

の一部であるあなたは、なぜ 'percentage'列に' 0.33333333333333'に入れていない、割合は14小数点以下の桁数を持っていますか? – beny23

+1

私が言いたい場合、あなたはそれはあなたが実際にちょうどこの質問を読んで必要なものを理解することがいかに難しい理解かもしれない... –

+0

は、Martin Fowler氏のを見て、「あなただけのSETサプライヤーを更新することができます... 0.34 =割合」 「数量」パターン。 http://martinfowler.com/eaaDev/quantity.html特に、資金の分割に関する議論を見てください。この例の除算は、単一の値ではなく、値の配列を返します。値の配列は合計に加算されます。私は、それが得られるほど簡単だとわかった。 – Glenn

答えて

3

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

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

例えば、$ 55.30という合計値を8で除算しようとすると、8つのバケットごとに10進数の値が$ 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) 

あなたの計算は、サプライヤあたりの製品の総数に基づいて均等に分布しているようです。そうであれば、パーセンテージを削除し、代わりにサプライヤごとのアイテムの数をテーブルに格納することが有利な場合があります。

剰余値を適用する行を示すフラグを格納することもできますが、ランダムにではなくそのフラグに基づいて割り当てることができます。

+0

これは私が期待していたものとほとんど同じです。どうもありがとうございました。今私はこのロジックを適用し、Teradataでこの作業を行うために調整する必要があります。どうもありがとうございました。 –

+0

プライマリ・インデックスがプロダクト・キーにある場合、各AMPは各計算を処理する必要があるため、かなり良いパフォーマンスを得るはずです。 –

+0

うん。しかし、私がここで示した例は、私のシナリオの正確な複製ではありません。私はすでに、より複雑な結合で分割作業の大部分を行うSQLのセットを持っています。上記の例は、理解しやすいようにすることでした。非常にN西にありがとうございます。 –

3

これを実行すると、問題の解決方法がわかります。

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] 

このコード:

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 

これらの値は、あなたが今

1 33.33 
2 33.33 
3 33.33 

持っている33%を表します。 私は理解しやすいように、単にIDと受注というテーブルを作成しました基本的には最大IDでパーセンテージと注文を計算し、100からパーセンテージの合計までdiferenceを取得し、maxID(あなたの無作為注文)で注文に追加します

select * from #tempOrders 

1 33.33 
2 33.33 
3 33.34 
+0

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

1

これは、ウィンドウ付き集計関数を使用すると簡単な作業になります。おそらくcustomer_splitの計算のために、すでにそれらを使用する:

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

は今customer_splitsをまとめると、1行のランダムに合計値ADD(またはsubstract)に違いがあるかどう。

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

つ以上の試験とテストの後、私はあなたの条件

  • に基づいて、すべてのよりよい解決策

    アイデア

    1. ゲットカウント(COUNT(*))を見つけたと思いますラウンドを選択し、ROW_NUMBER()
    2. チェックした場合(ROW_NUMBER()値< COUNT(*)) を取得(CUR最後の1を除くすべての時間が 100になりますr_percentageは、2) エルス ゲットラウンドを持つ他のすべての割合(の合計)とは、現在の割合を選択します100の この段階からそれを引く - 他のすべてのパーセンテージの合計を

    これは私のコード

    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