Pergunta

The project I am working on has deep parent/child relationships that needs to enforce availability.

Imagine we are a large Worldwide Electronic Seller (Best Buy) and selling mobile phones

We have Regions (Asia,Europe,North America..), Countries within Regions, Warehouses within Countries,Stores which are working with Warehouses.

Here are the 4 levels that are having parent-child relationships.

(Level 1) (Level 2) (Level 3) (Level 4)
Regions -> Countries -> Warehouses -> Stores

We are only selling Mobile Phones, and we have 100 different models available in total. The availibility of individual phone can vary in all these levels (availability points). For example :

By default, all mobile phone models are available for all stores.

We only sell 100 phone models, - Only 80 of them are available in Asia - Out of 80 only 60 of them are available in India - Out of 60 only 50 of them are available in the Delhi Warehouse - and finally out of 50 only 45 of them are available in New Delhi Store 1

Also some of the attributes of the phones might change between these availability points.

For example: Nokia Lumia 830 might be marketed as Lumia X in India. India might offer Vodafone by default for Nokia Lumia 830 but Delhi Warehouse can choose to override this and can offer another simcard provider.

So far I have come up with 2 ways to handle:

1- Applying all available mappings in every level.


Therefore when we make 90 Nokia phones available for Asia region, we will create.

  • 90 entries at region Level
  • 90 x Number of countries entries in Level 2
  • 90 x Number of countries x Number of Warehouses entries in Level 3
  • 90 x Number of countries x Number of Warehouses x Number of Stores entries in Level 4

Therefore someone changing the availbility at store level in the Asia region can see all the products available, and filter as they wish or change the attributes. The Delhi Warehouse cannot sell 10 of them, the New Delhi store can not sell 5 of the specific available at the warehouse. Hence

We will remove - 10 mappings from the warehouse - 10 x Number of all Stores mappings that are supplied by the warehuse from above - 5 x mappings from the specific New Delhi store

This approach is easier to handle in the code. But all the operations are creating lots of data records and managing this with transactions could be problematic.

2- Just creating records for the ones are not available in selected level and accept everything is available in every level.


In effect, by default, all products are available globally, unless specific restrictions have been created at the required level

Therefore we make 90 Nokia phones available for Asia region, we will create:

  • 90 entries in Level 1

    If someone changes the availbility at store level in the Asia region, they can see all the products available and filter as they wish or change the attributes. Suppose the Delhi Warehouse doesn't stock 10 of them. The New Delhi store doesn't stock 5 of the warehouse available phones. Hence

    We will add

  • 10 mappings x Number of Stores to Level 3

  • 5 Mappings to the New Delhi store

But implementing this approach and to find the available mobile phones in stores will require reading values from parents.

Also to be able to override an attribute of a phone model in every level in a simple and maintainable way is becoming a challenge.

I have been thinking about how to solve this problem in a nice/clean way and whether using Document Database or Data Warehouse would actually be a better fit.

Foi útil?

Solução 4

After going trhough great responses on here . I have implemented (Joel Brown's answer on the link)

https://dba.stackexchange.com/questions/28845/modelling-hierarchical-attributes

without visitation numbers . I went through use cases and that approach is solving each of them also it is generic enough that allows me to override any field i would like to. Sadly i cannot mark Vlad's comment as a answer.

Outras dicas

The hierarchical model doesn't sound like a good fit. Ideally at the upper most level there is one entry.

For example, for Asia, there would be one entry: Nokia

Now if one stops selling Nokia phones in Asia, one would remove just that one record at the top level and the change would cascade down to the other levels. This is what makes a hierarchical model powerful.

If you have many exceptions, like we can sell Nokia in Asia but for this region for these 2 stores they can't sell Nokia, and for this region for these 5 stores we can't sell Nokia, then the hierarchical model falls apart.

Phone availability doesn't make sense because just because the phone is available in Asia it may not be for a region or store because it may be out of stock etc. This is because hierarchical models work from top to bottom. So, modeling availability on this will not be a good fit.

One can still model your store locations as a hierarchical. This will allow you to run hierarchical queries, like how many phones are available for this region, etc.

But I would tie availability to the Ids of the stores as opposed to an Id or a country or region in a relational way as opposed to hierarchical.

You correctly identify the problem of conflicting rules at different levels of the hierarchy and the solution,. Either : only use white or black lists. OR only map the lowest level

In my view mapping only the lowest level is a mistake. The setting will be a product of a set of business rules. You have stated that the rules are things like 'only X phones available in asia' so you are going to have to have that hierarchical rule stored somewhere and use it to calculate the lowest level setting. You might as well calculate it on the fly unless it is very intensive.

Yes it means you have to follow the tree up to check all levels, but on the plus side you have less rules to maintain.

On the negative side though the rules are more complicated to explain to sales people.

You want to enter the less redundant information and have each level overriding the values of its parent. I would create four tables, let's call them Region, Country, Warehouse and Store. And three views with one outer join on each two tables Region-Country, Country-Warehouse and Warehouse-Store. At each view the child level values, if present, override the parent level values.

This way you can read the information at Region, Country, Warehouse or Store level.

You write in the tables inserting only the required information with minimal redundancy and read from the views.

The same could be achieved with a single table joined to itself, but I think it'd be much harder to understand the inner works.

CREATE TABLE Region (
    Region VARCHAR(50) NOT NULL,
    Model VARCHAR(100) NOT NULL,
    OnSale SMALLINT NOT NULL,
    Name VARCHAR(100) NOT NULL,
    Price DECIMAL(18, 2) NULL,
    Attribute VARCHAR(100) NULL,
    CONSTRAINT PK_Region PRIMARY KEY (Region,Model)
)

CREATE TABLE Country (
    Region VARCHAR(50) NOT NULL,
    Country VARCHAR(50) NOT NULL,
    OnSale SMALLINT NOT NULL,
    Model VARCHAR(100) NOT NULL,
    Name VARCHAR(100) NULL,
    Price DECIMAL(18, 2) NULL,
    Attribute VARCHAR(100) NULL,
    CONSTRAINT PK_Country PRIMARY KEY (Region,Country,Model),
    CONSTRAINT F1_Country FOREIGN KEY (Region,Model) REFERENCES Region(Region,Model)
)

CREATE TABLE Warehouse (
    Region VARCHAR(50) NOT NULL,
    Country VARCHAR(50) NOT NULL,
    Warehouse VARCHAR(50) NOT NULL,
    OnSale SMALLINT NOT NULL,
    Model VARCHAR(100) NOT NULL,
    Name VARCHAR(100) NULL,
    Price DECIMAL(18, 2) NULL,
    Attribute VARCHAR(100) NULL,
    CONSTRAINT PK_Warehouse PRIMARY KEY (Region,Country,Warehouse,Model),
    CONSTRAINT F1_Warehouse FOREIGN KEY (Region,Country,Model) REFERENCES Country(Region,Country,Model)
)

CREATE TABLE Store (
    Region VARCHAR(50) NOT NULL,
    Country VARCHAR(50) NOT NULL,
    Warehouse VARCHAR(50) NOT NULL,
    OnSale SMALLINT NOT NULL,
    Store VARCHAR(50) NOT NULL,
    Model VARCHAR(100) NOT NULL,
    Name VARCHAR(100) NOT NULL,
    Price DECIMAL(18, 2) NULL,
    Attribute VARCHAR(100) NULL,
    CONSTRAINT PK_Store PRIMARY KEY (Region,Country,Warehouse,Store,Model),
    CONSTRAINT F1_Store FOREIGN KEY (Region,Country,Warehouse,Model) REFERENCES Country(Region,Country,Warehouse,Model)
)

CREATE VIEW CountryModels AS
SELECT R.Region,C.Country,R.Model,ISNULL(C.OnSale,R.OnSale) AS OnSale,ISNULL(C.Name,R.Name) AS Name,ISNULL(C.Price,R.Price) AS Price ,ISNULL(C.Attribute,R.Attribute) AS Attribute FROM Region R LEFT OUTER JOIN Country C ON R.Region=C.Region AND R.Model=C.Model

CREATE VIEW WarehouseModels AS
SELECT C.Region,C.Country,W.Warehouse,ISNULL(W.OnSale,C.OnSale) AS OnSale,C.Model,ISNULL(W.Name,C.Name) AS Name,ISNULL(W.Price,C.Price) AS Price ,ISNULL(W.Attribute,C.Attribute) AS Attribute FROM CountryModels C LEFT OUTER JOIN Warehouse W ON W.Region=C.Region AND W.Country=C.Country AND W.Model=C.Model

CREATE VIEW StoreModels AS
SELECT W.Region,W.Country,W.Warehouse,S.Store,ISNULL(S.OnSale,W.OnSale) AS OnSale,W.Model,ISNULL(S.Name,W.Name) AS Name,ISNULL(S.Price,W.Price) AS Price ,ISNULL(S.Attribute,W.Attribute) AS Attribute FROM WarehouseModels S LEFT OUTER JOIN Store W ON S.Region=W.Region AND S.Country=W.Country AND S.Warehouse=W.Warehouse AND S.Model=W.Model
Licenciado em: CC-BY-SA com atribuição
scroll top