Thanks Alan,
If DiscountTypes' PK is placed on DiscountTypeID, then the last row in your
script is a dup (second 2 DiscountTypeID). I'll assume it's supposed to be
4.
If all discount rates with CalcOnBal = 1 were grouped together (with all
other discounts grouped at the beginning and/or end of the sequence of
discounts), a set based query could have been written to solve the problem
by using logarithms to calculate an aggregate product of the grouped
discount rates. However, if rates with CalcOnBal = 1 and all other discounts
might be mingled, I can't think of an easy way to avoid using a cursor:
CREATE VIEW VInv
AS
SELECT
II.InvoiceID,
II.InvoiceItemID,
II.Quantity*II.UnitPrice AS Value,
ISNULL(IID.DiscountRate, 0) AS DiscountRate,
ISNULL(DiscountAmount, 0) AS DiscountAmount,
ISNULL(DT.CalcOnBal, 0) AS CalcOnBal,
ISNULL(DT.Sequence, 0) AS Sequence
FROM InvoiceItems AS II
LEFT OUTER JOIN InvoiceItemDiscounts AS IID
ON II.InvoiceItemID = IID.InvoiceItemID
LEFT OUTER JOIN DiscountTypes AS DT
ON IID.DiscountTypeID = DT.DiscountTypeID
GO
--
SET NOCOUNT ON
CREATE TABLE #Inv
(
InvoiceID INT NOT NULL,
InvoiceItemID INT NOT NULL,
Value MONEY NOT NULL,
PRIMARY KEY (InvoiceID, InvoiceItemID)
)
DECLARE
@InvoiceID AS INT, @PrevInvoiceID AS INT,
@InvoiceItemID AS INT, @PrevInvoiceItemID AS INT,
@Value AS MONEY, @RunningValue AS MONEY,
@DiscountRate AS FLOAT,
@DiscountAmount AS MONEY,
@CalcOnBal AS BIT,
@Sequence AS INT
SELECT @PrevInvoiceID = -1, @PrevInvoiceItemID = -1
DECLARE inv_cursor CURSOR FAST_FORWARD FOR
SELECT
InvoiceID,
InvoiceItemID,
Value,
DiscountRate,
DiscountAmount,
CalcOnBal
FROM VInv
ORDER BY InvoiceID, InvoiceItemID, Sequence
OPEN inv_cursor
FETCH NEXT FROM inv_cursor
INTO @InvoiceID, @InvoiceItemID, @Value, @DiscountRate,
@DiscountAmount, @CalcOnBal
WHILE @@FETCH_STATUS = 0
BEGIN
IF @InvoiceID <> @PrevInvoiceID OR @InvoiceItemID <> @PrevInvoiceItemID
BEGIN
IF @PrevInvoiceID <> - 1
INSERT INTO #Inv
VALUES(@PrevInvoiceID, @PrevInvoiceItemID, @RunningValue)
SET @RunningValue = @value
END
SET @RunningValue = @RunningValue -
CASE
WHEN @CalcOnBal = 1 THEN @RunningValue
ELSE @Value
END * @DiscountRate - @DiscountAmount
SELECT @PrevInvoiceID = @InvoiceID, @PrevInvoiceItemID = @InvoiceItemID
FETCH NEXT FROM inv_cursor
INTO @InvoiceID, @InvoiceItemID, @Value, @DiscountRate,
@DiscountAmount, @CalcOnBal
END
IF @PrevInvoiceID <> - 1
INSERT INTO #Inv
VALUES(@PrevInvoiceID, @PrevInvoiceItemID, @RunningValue)
CLOSE inv_cursor
DEALLOCATE inv_cursor
SELECT * FROM #Inv
ORDER BY InvoiceID, InvoiceItemID
DROP TABLE #Inv
GO
InvoiceID InvoiceItemID Value
----------- ------------- ---------------------
1 1 530.0000
1 2 1400.0000
1 3 8000.0000
--
BG
SQL Server MVP
Hi-Tech College, Israel
http://sql.hi-tech.co.il
http://www.tsqlsolutions.com
http://www.sqlmag.com
"Alan Perkins" <alan.perk...@protel.com.au> wrote in message
news:OZsVce44BHA.1752@tkmsftngp02...
> Keys:
> #InvoiceItems.InvoiceItemID:
> CONSTRAINT [aaaaaInvoiceItems_PK] PRIMARY KEY NONCLUSTERED
> (
> [InvoiceItemID]
> ) WITH FILLFACTOR = 70 ON [PRIMARY]
> #InvoiceItemDiscounts:
> CONSTRAINT [InvoiceItemDiscounts_PK] PRIMARY KEY CLUSTERED
> (
> [InvoiceItemDiscountID]
> ) ON [PRIMARY] ,
> CONSTRAINT [DiscountTypes_InvoiceItemDiscounts_FK1] FOREIGN KEY
> (
> [DiscountTypeID]
> ) REFERENCES [DiscountTypes] (
> [DiscountTypeID]
> ),
> CONSTRAINT [InvoiceItems_InvoiceItemDiscounts_FK1] FOREIGN KEY
> (
> [InvoiceItemID]
> ) REFERENCES [InvoiceItems] (
> [InvoiceItemID]
> ) ON DELETE CASCADE
> #DiscountTypes:
> CONSTRAINT [DiscountTypes_PK] PRIMARY KEY CLUSTERED
> (
> [DiscountTypeID]
> ) ON [PRIMARY]
> Thus InvoiceItems has a 1 to many relationship with InvoiceItemDiscounts
> (InvoiceItems INNER JOIN InvoiceItemDiscounts ON
InvoiceItems.InvoiceItemID
> = InvoiceItemDiscounts.InvoiceItemID)
> InvoiceItemDiscounts has a 1 to many relationship with DiscountTypes
> InvoiceItemDiscounts.DiscountTypeID = DiscountTypes.DiscountTypeID
> The result I am after is the net value for each InvoiceItems.InvoiceItemID
> of (Quantity * UnitPrice) less any applicable discounts. From there I will
> apply the total to invoices as a whole, but that is beyond the scope of
the
> problem.
> Thanks again for any help.
> "Itzik Ben-Gan" <itz...@hi-tech.co.il> wrote in message
> news:#slnQW44BHA.492@tkmsftngp02...
> > Alan,
> > Thanks for the DDL and sample data.
> > Can you please also specify PKs and FKs, plus provide the desired
output?
> > Thanks,
> > --
> > BG
> > SQL Server MVP
> > Hi-Tech College, Israel
> > http://sql.hi-tech.co.il
> > http://www.tsqlsolutions.com
> > http://www.sqlmag.com
> > "Alan Perkins" <alan.perk...@protel.com.au> wrote in message
> > news:O0ay9J44BHA.984@tkmsftngp05...
> > > Simplified DDL:
> > > CREATE TABLE #InvoiceItems (
> > > InvoiceItemID int NOT NULL ,
> > > InvoiceID int NULL ,
> > > Quantity float NULL ,
> > > UnitPrice money NULL ,
> > > InventoryId int NULL
> > > )
> > > CREATE TABLE #InvoiceItemDiscounts (
> > > InvoiceItemDiscountID int NOT NULL ,
> > > InvoiceItemID int NOT NULL ,
> > > DiscountTypeID int NOT NULL ,
> > > DiscountRate float NOT NULL ,
> > > DiscountAmount money NOT NULL,
> > > )
> > > CREATE TABLE #DiscountTypes (
> > > DiscountTypeID int NOT NULL ,
> > > Description nvarchar(80),
> > > CalcOnBal bit NOT NULL ,
> > > Sequence int NULL
> > > )
> > > INSERT INTO #DiscountTypes (DiscountTypeID, Description, CalcOnBal,
> > > Sequence) VALUES (1, 'Volume Discount', 1, 2)
> > > INSERT INTO #DiscountTypes (DiscountTypeID, Description, CalcOnBal,
> > > Sequence) VALUES (2, 'Education Discount', 1, 1)
> > > INSERT INTO #DiscountTypes (DiscountTypeID, Description, CalcOnBal,
> > > Sequence) VALUES (3, 'Reseller Discount', 0, 3)
> > > INSERT INTO #DiscountTypes (DiscountTypeID, Description, CalcOnBal,
> > > Sequence) VALUES (2, 'Amount', 1, 4)
> > > INSERT INTO #InvoiceItems (InvoiceItemID, InvoiceID, Quantity,
> UnitPrice,
> > > InventoryId) VALUES (1, 1, 1, 1000, 1)
> > > INSERT INTO #InvoiceItems (InvoiceItemID, InvoiceID, Quantity,
> UnitPrice,
> > > InventoryId) VALUES (2, 1, 1, 2000, 3)
> > > INSERT INTO #InvoiceItems (InvoiceItemID, InvoiceID, Quantity,
> UnitPrice,
> > > InventoryId) VALUES (3, 1, 2, 4000, 6)
> > > INSERT INTO #InvoiceItemDiscounts (InvoiceItemDiscountID,
InvoiceItemID,
> > > DiscountTypeID, DiscountRate, DiscountAmount) VALUES (1, 1, 1, 0.1, 0)
> > > INSERT INTO #InvoiceItemDiscounts (InvoiceItemDiscountID,
InvoiceItemID,
> > > DiscountTypeID, DiscountRate, DiscountAmount) VALUES (2, 1, 2, 0.3, 0)
> > > INSERT INTO #InvoiceItemDiscounts (InvoiceItemDiscountID,
InvoiceItemID,
> > > DiscountTypeID, DiscountRate, DiscountAmount) VALUES (3, 1, 4, 0,
100)
> > > INSERT INTO #InvoiceItemDiscounts (InvoiceItemDiscountID,
InvoiceItemID,
> > > DiscountTypeID, DiscountRate, DiscountAmount) VALUES (4, 2, 3, 0.3, 0)
> > > The Problem:
> > > I am trying to calculate for each line on an invoice the value after
> > > applying discounts. The base calculation is InvoiceItems.Quantity *
> > > InvoiceItems.UnitPriceEach. From that starting point, I have to apply
> > > discounts in the DiscountTypes.Sequence Order - discount rate is
either
> > > calculated on the running balance (DiscountTypes.CalcOnBal = 1) or on
> the
> > > total value. Discount Amounts are also applied in the order of the
> > > DiscountType.Sequence.
> > > So If I have an invoiceline with 3 units @ 1000 each, with two
discounts
> > of
> > > 10% and 20% respectively (in that order), both calconbal = 1, then I
> want
> > to
> > > end up with 3000 - 300 = 2700 - 540 = 2160.
> > > Any help is greatly appreciated.
> > > Alan Perkins