SQL查询重建继承的EAV模型

时间:2016-05-12 21:32:14

标签: sql sql-server join common-table-expression entity-attribute-value

我的数据库中有5个表,表示继承的EAV模型:

CREATE TABLE AttributeNames
    ("ID" int, "Name" varchar(8))
;

INSERT INTO AttributeNames
    ("ID", "Name")
VALUES
    (1, 'Color'),
    (2, 'FuelType'),
    (3, 'Doors'),
    (4, 'Price')
;

CREATE TABLE MasterCars
    ("ID" int, "Name" varchar(10))
;

INSERT INTO MasterCars
    ("ID", "Name")
VALUES
    (5, 'BMW'),
    (6, 'Audi'),
    (7, 'Ford')
;

CREATE TABLE MasterCarAttributes
    ("ID" int, "AttributeNameId" int, "Value" varchar(10), "MasterCarId" int)
;

INSERT INTO MasterCarAttributes
    ("ID", "AttributeNameId", "Value", "MasterCarId")
VALUES
    (100, 1, 'Red', 5),
    (101, 2, 'Gas', 5),
    (102, 3, '4', 5),
    (102, 4, '$100K', 5),
    (103, 1, 'Blue', 6),
    (104, 2, 'Diesel', 6),
    (105, 3, '3', 6),
    (106, 4, '$80k', 6),
    (107, 1, 'Green', 7),
    (108, 2, 'Diesel', 7),
    (109, 3, '5', 7),
    (110, 4, '$60k', 7)
;

CREATE TABLE LocalCars
    ("ID" int, "MasterCarId" int)
;

INSERT INTO LocalCars
    ("ID", "MasterCarId")
VALUES
    (8, '5'),
    (9, '6'),
    (10, NULL)
;

CREATE TABLE LocalCarAttributes
    ("ID" int, "AttributeNameId" int, "Value" varchar(6), "LocalCarId" int)
;

INSERT INTO LocalCarAttributes
    ("ID", "AttributeNameId", "Value", "LocalCarId")
VALUES
    (43, 1, 'Yellow', 8),
    (44, 3, '6', 9),
    (45, 1, 'Red', 10),
    (46, 2, 'Gas', 10),
    (47, 3, '2', 10),
    (48, 4, '$60k', 10)
;

我可以按如下方式检索所有主车属性:

SELECT MC.ID, MCA.AttributeNameId, MCA.Value
FROM MasterCars MC
left join MasterCarAttributes MCA on MC.ID = MCA.MasterCarId
order by MC.ID;

同样,我可以检索所有本地汽车属性,如下所示:

SELECT LC.ID, LCA.AttributeNameId, LCA.Value
FROM LocalCars LC
left join LocalCarAttributes LCA on LC.ID = LCA.LocalCarId
order by LC.ID;

如果LocalCars.MasterCarId不为NULL,则该本地汽车可以继承该主汽车的属性。具有相同AttributeNameId的本地car属性将覆盖具有相同AttributeNameId的任何主属性。

因此,鉴于上述数据,我有3辆本地车,每辆车都有4个属性(颜色,燃料类型,车门,价格)。以粗体显示的继承属性值:

本地车辆识别码= 1(黄色,气体 4 $ 100K

本地车辆识别码= 2(蓝色柴油,6, $ 80k

本地车辆识别码= 3(红色,气体,2,$ 60k)

我正在尝试找到加入上述两个查询所需的必要连接,以提供一组完整的本地汽车属性,一些继承:

LocalCarId    AttributeNameId     Value
------------------------------------------
1             1                   Yellow
1             2                   Gas
1             3                   4
1             4                   $100K
2             1                   Blue
2             2                   Diesel
2             3                   6
2             4                   $80K
3             1                   Red
3             2                   Gas
3             3                   2
3             4                   $60K

或甚至可能:

LocalCarId    AttributeNameId     LocalValue         MasterValue
    -------------------------------------------------------------
    1             1                   Yellow        Red
    1             2                   NULL          Gas
    1             3                   NULL          4
    1             4                   NULL          $100K
    2             1                   NULL          Blue
    2             2                   NULL          Diesel
    2             3                   6             3
    2             4                   NULL          $80K
    3             1                   Red           NULL
    3             2                   Gas           NULL
    3             3                   2             NULL
    3             4                   $60K          NULL

2 个答案:

答案 0 :(得分:0)

<强> SQL Fiddle Demo

SELECT LC."ID" as LocalCarID,   
       COALESCE(LCA."AttributeNameId", MCA."AttributeNameId") as "AttributeNameId",
       COALESCE(LCA."Value", MCA."Value") as "Value"
FROM LocalCars LC
LEFT JOIN MasterCars MC
       ON LC."MasterCarId" = MC."ID"
LEFT JOIN MasterCarAttributes MCA
       ON MC."ID" = MCA."MasterCarId"
LEFT JOIN LocalCarAttributes LCA
        ON (    MCA."AttributeNameId" = LCA."AttributeNameId"
             OR MCA."AttributeNameId" IS NULL)
             -- This is the important part
             -- Try to join with a MasterAtribute otherwise use the Car Atribute.
       AND LC."ID" = LCA."ID"

<强>输出

| LocalCarID | AttributeNameId |  Value |
|------------|-----------------|--------|
|          1 |               1 |   Blue |
|          1 |               2 |    Gas |
|          2 |               1 |  Green |
|          2 |               2 | Diesel |

答案 1 :(得分:0)

可以通过对所有本地汽车属性和主汽车属性执行联合来解决问题。每条记录都标有[IsMasterAttribute]标志。然后,下一步是使用ROW_NUMBER()窗口函数对每个重复属性进行排名。最后一步是仅选择排名为1的属性。

    ;WITH CTE_CombinedAttributes
    AS
    (
        SELECT               1 AS IsMasterAttribute
                            ,LC.ID
                            ,MC.ID AS MasterCarId
                            ,MCA.AttributeNameId
                            ,MCA.Value
        FROM                 MasterCars MC
        LEFT OUTER JOIN     MasterCarAttributes MCA on MC.ID = MCA.MasterCarId
        INNER JOIN          LocalCars LC ON LC.MasterCarId = MC.ID
        UNION ALL
        SELECT               0 AS IsMasterAttribute
                            ,LC.ID
                            ,LC.MasterCarId
                            ,LCA.AttributeNameId
                            ,LCA.Value
        FROM                LocalCars LC
        LEFT OUTER JOIN     LocalCarAttributes LCA on LC.ID = LCA.LocalCarId
    )
    , 
    CTE_RankedAttributes
    AS
    (
        SELECT   [IsMasterAttribute]
                ,[ID]
                ,[AttributeNameId]
                ,[Value]
                ,ROW_NUMBER() OVER (PARTITION BY [ID], [AttributeNameId] ORDER BY [IsMasterAttribute]) AS [AttributeRank]
        FROM    CTE_CombinedAttributes
    )
    SELECT       [IsMasterAttribute]
                ,[ID]
                ,[AttributeNameId]
                ,[Value]
    FROM        CTE_RankedAttributes
    WHERE       [AttributeRank] = 1
    ORDER BY    [ID]

通过对最终结果执行简单的旋转,也可以进行第二次输出:

    ;WITH CTE_CombinedAttributes
    AS
    (
        SELECT               1 AS IsMasterAttribute
                            ,LC.ID
                            ,MC.ID AS MasterCarId
                            ,MCA.AttributeNameId
                            ,MCA.Value
        FROM                 MasterCars MC
        LEFT OUTER JOIN     MasterCarAttributes MCA on MC.ID = MCA.MasterCarId
        INNER JOIN          LocalCars LC ON LC.MasterCarId = MC.ID
        UNION ALL
        SELECT               0 AS IsMasterAttribute
                            ,LC.ID
                            ,LC.MasterCarId
                            ,LCA.AttributeNameId
                            ,LCA.Value
        FROM                LocalCars LC
        LEFT OUTER JOIN     LocalCarAttributes LCA on LC.ID = LCA.LocalCarId
    )
    , 
    CTE_RankedAttributes
    AS
    (
        SELECT   [IsMasterAttribute]
                ,[ID]
                ,[AttributeNameId]
                ,[Value]
                ,ROW_NUMBER() OVER (PARTITION BY [ID], [AttributeNameId] ORDER BY [IsMasterAttribute]) AS [AttributeRank]
        FROM    CTE_CombinedAttributes
    )
    SELECT       [ID]
                ,[AttributeNameId]
                ,MAX(
                    CASE [IsMasterAttribute]
                        WHEN 0 THEN [Value]
                    END
                 ) AS LocalValue
                ,MAX(
                    CASE [IsMasterAttribute]
                        WHEN 1 THEN [Value]
                    END
                 ) AS MasterValue
    FROM        CTE_RankedAttributes
    GROUP BY    [ID], [AttributeNameId]
    ORDER BY    [ID]