Database Design Brainstorming: Sale Prices

2019-04-11 18:36发布

问题:

I need to create a database solution to provide product discounting.

Current tables:

Products
Columns: ProductId, ProductTypeId, ReleaseDate

ProductPrices
Columns: ProductPriceId, ProductPriceTypeId (one product may have n prices), ProductId, Price

We want to be able to discount by ProductId and/or ProductTypeId and/or ProductPriceTypeId and/or ReleaseDate.

Example sales:

  1. Discount a single ProductId.
  2. Discount all products of a specified ProductTypeId and ProductPriceTypeId.
  3. Discount all products of a specified ProductTypeId with a ReleaseDate within the last month.

The challenging aspect of #2 is not the literal example, but considering long term scalability in the event new fields are added in the future.

I am stumped how to handle #3 because of the ReleaseDate.

Below, is what I mentally thought out before I realized I needed to come to Stackoverflow. You can see that the rigid structure will not allow for good scalability because of the explicitly included columns - if we added new criteria in the future then those columns would need to be added to the table - and not to mention it does not even handle the ReleaseDate requirement.

New table:

ProductPriceDiscounts
Columns: ProductPriceDiscountId, ProductPriceId, ProductTypeId, ProductPriceTypeId, Discount, DiscountTypeId (1 for percentage, 2 for fixed)

Then I could use something like this to get the pricing:

from p in Products
join pp in ProductPrices on p.ProductId equals pp.ProductId
let ppd = (
    from ppd in ProductPriceDiscounts
        .WhereIf(ppd.ProductPriceId != null, ppd.ProductPriceId == pp.ProductPriceId)
        .WhereIf(ppd.ProductTypeId != null, ppd.ProductTypeId == pp.ProductTypeId )
        .WhereIf(ppd.ProductPriceTypeId != null, ppd.ProductPriceTypeId == pp.ProductPriceId)
    select ppd).FirstOrDefault()
where p.ProductId = productId
select new 
{
    ...,
    Price = pp.Price,
    Discount = pp.Discount,
    DiscountedPrice = 
        (ppd.DiscountTypeId == 1) ? 
            (pp.Price - (pp.Price * pp.Discount)) :
            (pp.Price - pp.Discount) :
}

I only included this bad example I came up with to show the end result of how I need to be able to use this new product discounting functionality. Can anyone offer advice for a good way to handle this situation? Thanks.

回答1:

I'd say you need a separate DiscountDetail table. Something like

   DiscountDetailID INT NOT NULL
   DiscountTypeID INT NOT NULL --(FK On Discount Types - maybe not necessary)
   DiscountProductTypeID INT NULL --(FK ON ProductType)
   DiscountProductID INT NULL --(FK ON Product)
   DiscountAmount INT NULL --(Some value related to %age reduction perhaps?)
   DiscountDateStart DATETIME NOT NULL
   DiscountDateEnd DATETIME NULL

The with some lovely left joins and funky calculations you should be able to get a list of all products/types and the discounted prices at any particular time...



回答2:

I ended up going with my original design, and when I query the database using linq to sql, I get the "DiscountedPrice" using a method. The method runs its own query and performs conditional logic on it before returning the results. So this gives me the option of having my code do additional work that my query wouldn't be able to. I also made an Enum class for the special ProductPriceDiscountIds so they can be easily identified. I hope this helps anyone else who finds themselves in a similar situation - it's working very well for me so far.