Nominal dağıtım hesaplama

oy
0

Bazı arka plan hikayesi: A firması bir sorun kazananlara kuponları verir. Şu anda ihtiyaçları yazıyorum SQL değerine toplamlar bir kişiye verilen gerektiriyordu kupon mezhep karar vermek. Ben kuponları için kullanılabilir depolar mezhepler, ülke ve para birimine bağlı olduğunu bir tablo var.

Aşağıdaki örnekte, bir kişinin kuponları € 80 değerinde ile verilir.

belirli bir ülke için geçerli kupon mezhepleri için bir arama tablosu görüntüler sonuçlarının altında sorgusu.

SELECT * FROM tblDenominationScheme WHERE CountryCode IN ('AT', 'US')

Sonuç:

No. | CountryCode  |   VoucherName | VoucherValue
-------------------------------------------------
1   | AT           |   €50 Shop A  |     50
2   | AT           |   €25 Shop A  |     25
3   | AT           |   €15 Shop A  |     15
4   | AT           |   €10 Shop A  |     10
5   | US           |   $50 Store B |     50
6   | US           |   $10 Store B |     10
7   | US           |   $5 Store B  |      5

Benim şu anki SQL € 80 kuponu için gerekli kupon mezhepleri belirlemek için aşağıda gibidir:

   DECLARE @CountryCode1 VARCHAR(2) = 'AT'
   DECLARE @ChallengerID INT = 1172
   DECLARE @RoundedAmount1 INT = 80
   DECLARE @Vouchers INT
   DECLARE @AmountAwarded INT = 0

   SET @AmountAwarded = @RoundedAmount1

   DROP TABLE IF EXISTS #tempVoucher

   CREATE TABLE #tempVoucher
   (
          CountryCode VARCHAR(2),
          ChallengerID INT,
          AmountAwarded INT,
          Vouchers INT,
   )

   WHILE (@RoundedAmount1 > 0)
   BEGIN

          SET @Vouchers = 0

          SELECT TOP 1 @Vouchers = VoucherValue FROM tblDenominationScheme WHERE CountryCode = @CountryCode1 AND VoucherValue <= @RoundedAmount1 ORDER BY VoucherValue DESC

          IF (@Vouchers > 0)
          BEGIN
                 SET @RoundedAmount1 = @RoundedAmount1 - @Vouchers
          END
          ELSE
          BEGIN
                 SELECT TOP 1 @Vouchers = VoucherValue FROM tblDenominationScheme WHERE CountryCode = @CountryCode1 ORDER BY VoucherValue
                 SET @RoundedAmount1 = @RoundedAmount1 - @RoundedAmount1
          END

          INSERT INTO #tempVoucher VALUES (@CountryCode1,@ChallengerID, @AmountAwarded, @Vouchers)
   END

   SELECT * FROM #tempVoucher

SQL yukarıdan Sonuç:

No. | CountryCode  | ChallengerID |   AmountAwarded | Vouchers
--------------------------------------------------------------
1   | AT           | 1172         |   80            |   50
2   | AT           | 1172         |   80            |   25
3   | AT           | 1172         |   80            |   10

NOT: AmountAwarded sütunundaki değer tüm 3 satırlar için aynı olacaktır. 3 satır için Fişler sütunundaki miktarı 80 özetlemek gerekir.

Eğer Kuponlar sütundaki değerleri Özetle, eğer 5 daha AmountAwarded daha uzun olduğu, Kim 85 verir, çünkü, yukarıda sonuç açıkça yanlış

Beklenen sonuç (veya en yakın en azından):

No. | CountryCode  | ChallengerID |   AmountAwarded | Vouchers
--------------------------------------------------------------
1   | AT           | 1172         |   80            |   50
2   | AT           | 1172         |   80            |   10
3   | AT           | 1172         |   80            |   10
4   | AT           | 1172         |   80            |   10

yardım edebilmek isteyen?

Oluştur 02/12/2019 saat 23:58
kaynak kullanıcı
Diğer dillerde...                            


3 cevaplar

oy
1

Bu pahalı bir sorgu olabilir, ama size beklenen sonucu elde etmek 7 kuponları kadar yayınlamanız için farklı seçenekler alır olabilir. Ancak bu satırları artırmak veya kuponları miktarı fazla olabilir eğer okur büyük miktarda üretecektir.

  DECLARE @CountryCode1 VARCHAR(2) = 'AT'
   DECLARE @RoundedAmount1 INT = 80;

WITH cteDenominations AS(
    SELECT No, VoucherValue 
    FROM tblDenominationScheme 
    WHERE CountryCode = @CountryCode1
    UNION ALL
    SELECT 10000, 0
),
ctePermutations AS(
    SELECT a.No             AS a_No, 
           a.VoucherValue   AS a_Value, 
           b.No             AS b_No, 
           b.VoucherValue   AS b_Value,
           c.No             AS c_No, 
           c.VoucherValue   AS c_Value,
           d.No             AS d_No, 
           d.VoucherValue   AS d_Value,
           e.No             AS e_No, 
           e.VoucherValue   AS e_Value,
           f.No             AS f_No, 
           f.VoucherValue   AS f_Value,
           g.No             AS g_No, 
           g.VoucherValue   AS g_Value,
        ROW_NUMBER() OVER(ORDER BY a.No, b.No, c.No, d.No) Permutation
    FROM cteDenominations a
    JOIN cteDenominations b ON a.VoucherValue >= b.VoucherValue
    JOIN cteDenominations c ON b.VoucherValue >= c.VoucherValue
    JOIN cteDenominations d ON c.VoucherValue >= d.VoucherValue
    JOIN cteDenominations e ON d.VoucherValue >= e.VoucherValue
    JOIN cteDenominations f ON e.VoucherValue >= f.VoucherValue
    JOIN cteDenominations g ON f.VoucherValue >= g.VoucherValue
    WHERE @RoundedAmount1 = a.VoucherValue 
                          + b.VoucherValue 
                          + c.VoucherValue 
                          + d.VoucherValue 
                          + e.VoucherValue 
                          + f.VoucherValue 
                          + g.VoucherValue 
)
SELECT Permutation,
    u.No,
    u.VoucherValue
FROM ctePermutations
CROSS APPLY (VALUES(a_No, a_Value),
                   (b_No, b_Value),
                   (c_No, c_Value),
                   (d_No, d_Value),
                   (e_No, e_Value),
                   (f_No, f_Value),
                   (g_No, g_Value))u(No, VoucherValue)
WHERE VoucherValue > 0
AND   Permutation = 1 --Remove this to get all possibilities
;
Cevap 03/12/2019 saat 01:05
kaynak kullanıcı

oy
1

Dış görünüş Bir denklemi çözmek gerekir mi:

80 = n1*v1 + k2*n2...

nerede v1,v2 ...sen veritabanında depolamak değerlerdir Ve bulmalıyız n1, n2 ...içinde olan, {0, N} SQL nasıl uygulanacağı konusu hiçbir yolu yoktur. Tüm olası değerleri üzerinde, ancak daha akıllı yol değil - Aşağıdakiler hariç.

: Ayrıca, bu bilgileri görebilirsiniz https://math.stackexchange.com/questions/431367/solving-a-first-order-diophantine-equation-with-many-terms

Cevap 03/12/2019 saat 01:08
kaynak kullanıcı

oy
0

Mantık

  1. 1 mezhebinin kuponları yapabilirsiniz (başlangıç ​​miktarına eşit veya daha az olması) büyük miktarda bulun.
  2. kalan elde etmek için bir miktar başlangıç ​​bu değeri çıkarma,
  3. (Az olduğunu ya da geri kalan kısmına eşit) büyük miktarda bul 1 kuponları bir dizi daha küçük mezhep yapabilirsiniz.
  4. Önceki geri kalan bu değeri çıkarın.
  5. 3. adımda geri dön

Özellikler:

  • Birden Kolları iyi kombinasyonlar.
  • kombinasyonların sayısının az olması aranır.
  • Benim laptop günü: 100 çalışır 3 saniye sürer

notlar

Performans çıkışını kaydederek iyileştirilebilir VoucherCombinationsbir tablo değişken ve daha sonra takip eden CTEs içinde kullanılmıştır.

Kod:

DECLARE @Vouchers TABLE( CountryCode CHAR( 2 ), VoucherValue DECIMAL( 10, 2 ))
INSERT INTO @Vouchers VALUES( 'AT', 50 ), ( 'AT', 40 ), ( 'AT', 25 ), ( 'AT', 20 ), ( 'AT', 15 ), ( 'AT', 10 ), ( 'US', 50 ), ( 'US', 10 ), ( 'US', 5 );

-- Small number table
-- Limits maximum count of Vouchers of a given denomination.
DECLARE @Numbers TABLE( Num INT )
INSERT INTO @Numbers VALUES( 1 ), ( 2 ), ( 3 ), ( 4 ), ( 5 ), ( 6 ), ( 7 ), ( 8 ), ( 9 ), ( 10 )

DECLARE @TargetAmount DECIMAL( 10, 2 ) = 60;
DECLARE @CountryCode CHAR( 2 ) = 'AT';

;WITH VoucherCombinations
AS (
    -- Anchor
    SELECT ROW_NUMBER() OVER( ORDER BY VoucherValue DESC ) AS ParentGroupID,
        ROW_NUMBER() OVER( ORDER BY VoucherValue DESC ) AS SubGroupID,
        1 AS IterationID,
        VoucherValue, Num AS VoucherCumulativeCount,
        CAST( VoucherValue * Num AS DECIMAL( 10, 2 )) AS TotalDenominationValue,
        CAST( @TargetAmount - ( VoucherValue * Num ) AS DECIMAL( 10, 2 )) AS Remainder
    FROM @Vouchers
        -- Find the largest amount a given Voucher denomination can produce that is less than or equal to @TargetAmount
        INNER JOIN @Numbers ON ( VoucherValue * Num ) <= @TargetAmount AND @TargetAmount - ( VoucherValue * Num ) < VoucherValue
    WHERE CountryCode = @CountryCode
    UNION ALL
    -- Recursive query
    SELECT SubGroupID,
        SubGroupID * 10 + ROW_NUMBER() OVER( ORDER BY V.VoucherValue DESC ) AS SubGroupID,
        IterationID + 1,
        V.VoucherValue, VoucherCumulativeCount + N.Num AS VoucherCount,
        CAST( V.VoucherValue * N.Num AS DECIMAL( 10, 2 )) AS TotalDenominationValue,
        CAST( Remainder - ( V.VoucherValue * N.Num ) AS DECIMAL( 10, 2 )) AS Remainder
    FROM VoucherCombinations AS VP
        -- For each denomination look at the smaller denominations
        INNER JOIN @Vouchers AS V ON VP.VoucherValue > V.VoucherValue
            INNER JOIN @Numbers AS N ON V.VoucherValue * N.Num <= Remainder AND Remainder - ( V.VoucherValue * N.Num ) < V.VoucherValue
    WHERE CountryCode = @CountryCode
),
-- Discard invalid combinations i.e. remainder is not 0
VoucherPoolValid AS(
    SELECT *, DENSE_RANK() OVER( ORDER BY VoucherCumulativeCount ASC ) AS BestCombos
    FROM VoucherCombinations
    WHERE Remainder = 0
),
-- Find best combinations i.e. smallest number of Vouchers; Note: logic supports having more than 1 best combination
VoucherPoolBestCombos AS(
    SELECT *, ROW_NUMBER() OVER( ORDER BY BestCombos ASC ) AS ComboID
    FROM VoucherPoolValid
    WHERE BestCombos = 1
),
-- Return all denominations for each combination
VoucherPoolAllDetails AS(
    SELECT *
    FROM VoucherPoolBestCombos
    UNION ALL
    SELECT Parent.*, BestCombos, ComboID
    FROM VoucherPoolAllDetails AS Child
        INNER JOIN VoucherCombinations AS Parent ON Child.ParentGroupID = Parent.SubGroupID
    WHERE Child.SubGroupID <> Child.ParentGroupID
)
SELECT * FROM VoucherPoolAllDetails
ORDER BY ComboID
Cevap 03/12/2019 saat 04:20
kaynak kullanıcı

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more