calcul de la distribution de dénomination

voix
0

Une histoire de fond: La société A donne des bons aux gagnants d'un défi. Le SQL que je vous écris actuellement doit décider la dénomination de voucher requis que des sommes à la valeur attribué à une personne. J'ai une table qui stocke les coupures disponibles pour les bons, en fonction du pays et de la monnaie.

Dans l'exemple ci-dessous, une personne en particulier est accordé à 80 € la valeur de bons.

La requête ci-dessous affiche les résultats d'une table de consultation pour les dénominations bons disponibles pour un pays donné.

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

Résultat:

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

Mon SQL actuelle est comme ci-dessous pour déterminer les dénominations voucher requis pour € 80 bon:

   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

Résultat du SQL ci-dessus:

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

REMARQUE: La valeur dans la colonne AmountAwarded sera le même pour tous les 3 rangs. Le montant dans la colonne de bons de réduction pour les 3 rangées devrait résumer à 80.

Le résultat ci-dessus est évidemment incorrect, parce que si vous additionnez les valeurs dans la colonne des bons, il vous donne 85, ce qui est 5 de plus que le AmountAwarded

Résultat attendu (ou au moins le plus proche):

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

Toute personne en mesure d'aider?

Créé 02/12/2019 à 23:58
source utilisateur
Dans d'autres langues...                            


3 réponses

voix
1

Cela pourrait être une requête coûteuse, mais vous obtient différentes options pour fournir jusqu'à 7 bons pour vous obtenir le résultat attendu. Ceci, cependant, va générer une énorme quantité de lectures si les lignes augmentent ou la quantité de coupons peuvent être plus.

  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
;
Créé 03/12/2019 à 01:05
source utilisateur

voix
1

On dirait que vous devez résoudre une équation:

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

v1,v2 ...sont les valeurs que vous enregistrez dans la base de données et vous devez trouver n1, n2 ..., qui sont en {0, N} Il n'y a aucun moyen comment mettre en œuvre dans SQL. À l' exception - sur toutes les valeurs possibles, mais ce n'est pas la façon plus intelligente.

En outre, voir cette info: https://math.stackexchange.com/questions/431367/solving-a-first-order-diophantine-equation-with-many-terms

Créé 03/12/2019 à 01:08
source utilisateur

voix
0

Logique

  1. Trouvez la plus grande quantité (qui est inférieure ou égale au montant de départ) de bons 1 dénomination peuvent faire.
  2. Soustraire cette valeur de montant de départ pour obtenir reste,
  3. Trouvez la plus grande quantité (qui est inférieur ou égal à reste) un certain nombre de pièces justificatives de 1 petite dénomination peut faire.
  4. Soustraire cette valeur reste précédent.
  5. Retour à l'étape 3

Fonctionnalités:

  • Poignées plusieurs meilleures combinaisons.
  • Un petit nombre de combinaisons sont recherchées.
  • Sur mon ordinateur portable: 100 pistes prennent environ 3 secondes

Remarques

La performance peut être améliorée en enregistrant sortie VoucherCombinationsà une variable de table, puis l' utiliser dans CTEs ultérieures.

Code:

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
Créé 03/12/2019 à 04:20
source utilisateur

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