如何在实体 - 属性 - 值(EAV)模型中查询实体的所有信息?

时间:2014-02-20 13:33:18

标签: mysql sql entity-attribute-value

我已经搜索了这个,并试图在过去几天自己做,但我不能。我在搜索范围内最接近的是这个答案,也是Stack Overflow:EAV Select query from spreaded value tables

所以我在这里,转向互联网!

所以,我有一个使用EAV(实体 - 属性 - 值)模型的数据库。但这里有一个问题:实际的实体并没有直接连接到其他EAV表。让我更具体一点;假设有一个Person和Site表,并且他们只有主键:person_idsite_id

因为这些实体(即人和网站)的属性(在我的模式中称为“属性”)必须是动态的,所以它们必须全部存储在它们各自的表之外,即EAV表。以下是数据库模式的EAV部分(我不确定它是否完全正确,所以如果您有任何建议,请告诉我)。 - http://i.stack.imgur.com/EN3dy.png

架构的EAV部分基本上有以下表格:

  • property
  • property_value_varchar
  • property_value_text
  • property_value_number
  • property_value_boolean
  • property_value_datetime
  • entity_tables

好的,所以,由于实体没有“直接连接”到EAV部分,我使用entity_tables表作为对实际表的引用,因此,通过上面的例子, entity_tables表看起来像这样:

---------------------------------------
|entity_table_id | entity_table_name  |
|      1         |       person       |
|      2         |       site         |
|      .         |        .           |
|      .         |        .           |
---------------------------------------

property表实际上包含任何实体可以容纳的不同属性,例如“PERSON_FIRST_NAME”或“LOCATION_NAME”,或其他任何内容。

property_value_*的数据类型外,property_value表都完全相同。这些是保存每个Entity对象属性的实际值的属性,由entity_table_identity_object_id映射。

为清楚起见,让我给你一个可能的数据库实例:

Person table
-------------
| person_id |
|     1     |
|     2     |
-------------

Site table
-----------
| site_id |
|    1    |
|    2    |
-----------

entity_tables table
---------------------------------------
|entity_table_id | entity_table_name  |
|      1         |       person       |
|      2         |       site         |
---------------------------------------

property table
-------------------------------------
| property_id |    property_code    |
|      1      |   PERSON_FIRST_NAME |
|      2      |   PERSON_LAST_NAME  |
|      3      |   PERSON_BIRTH_DATE |
|      4      |   SITE_NAME         |
|      5      |   SITE_PHONE_NR_1   |
|      6      |   SITE_PHONE_NR_2   |
|      7      |   SITE_LATITUDE     |
|      8      |   SITE_LONGITUDE    |
|      9      |   SITE_CITY         |
|     10      |   SITE_COUNTRY      |
|     11      |   SITE_ZIP_CODE     |
-------------------------------------

property_value_varchar table
-----------------------------------------------------------------------------------------
| property_value_id | property_id | entity_table_id | entity_object_id | property_value |
|         1         |      1      |        1        |         1        |     Richard    |
|         2         |      2      |        1        |         1        |     Hammer     |
|         3         |      1      |        1        |         2        |     Bruce      |
|         4         |      2      |        1        |         2        |     Heaton     |
|         5         |      4      |        2        |         1        |     BatCave    |
|         6         |      5      |        2        |         1        |  +49123456789  |
|         7         |      4      |        2        |         2        |   BigCompany   |
|         8         |      5      |        2        |         2        |    987654321   |
|         9         |      6      |        2        |         2        |    147852369   |
|        10         |      9      |        2        |         2        |      Berlin    |
|        11         |     10      |        2        |         2        |     Germany    |
|        12         |     11      |        2        |         2        |      14167     |
-----------------------------------------------------------------------------------------

property_value_datetime table
-----------------------------------------------------------------------------------------
| property_value_id | property_id | entity_table_id | entity_object_id | property_value |
|         1         |      3      |        1        |         1        |   1985-05-31   |
-----------------------------------------------------------------------------------------

property_value_number table
-----------------------------------------------------------------------------------------
| property_value_id | property_id | entity_table_id | entity_object_id | property_value |
|         1         |      7      |        2        |         1        |    1.402636    |
|         2         |      8      |        2        |         1        |    7.273922    |
-----------------------------------------------------------------------------------------

(property_value_text and property_value_boolean tables are empty)

如您所见,并非每个实体的所有对象都必须具有相同的属性(属性)。域名真的很松散。

所以,现在和我之前的很多人一样,我不确定如何以可读的方式检索所有这些信息,即如何获取有关Person表或站点记录的所有信息表

即,我怎么能得到这样的东西:

 Person table view
----------------------------------------------------
| Person ID |     Property code   | Property value |
|     1     |   PERSON_FIRST_NAME |    Richard     |
|     1     |   PERSON_LAST_NAME  |    Hammer      |
|     1     |   PERSON_BIRTH_DATE |   1985-05-31   |
|     2     |   PERSON_FIRST_NAME |    Bruce       |
|     2     |   PERSON_LAST_NAME  |    Heaton      |
----------------------------------------------------

 Site table view
------------------------------------------------
| Site ID | Property code    |  Property value |
|    1    |  SITE_NAME       |  Batcave        |
|    1    |  SITE_PHONE_NR_1 |  +49123456789   |
|    1    |  SITE_LATITUDE   |  1.402636       |
|    1    |  SITE_LONGITUDE  |  7.273922       |
|    2    |  SITE_NAME       |  BigCompany     |
|    2    |  SITE_PHONE_NR_1 |  987654321      |
|    2    |  SITE_PHONE_NR_2 |  147852369      |
|    2    |  SITE_CITY       |  Berlin         |
|    2    |  SITE_COUNTRY    |  Germany        |
|    2    |  SITE_ZIP_CODE   |  14167          |
------------------------------------------------

或者甚至喜欢这样,如果它更容易:

Person table view
------------------------------------------------------------------------
| Person ID | PERSON_FIRST_NAME | PERSON_LAST_NAME | PERSON_BIRTH_DATE |
|     1     |      Richard      |       Hammer     |     1985-05-31    |
|     2     |       Bruce       |       Heaton     |                   |
------------------------------------------------------------------------

Site table view
----------------------------------------------------------------------------------------------------------------------------------------
| Site ID | SITE_NAME  | SITE_PHONE_NR_1 | SITE_PHONE_NR_2 | SITE_LATITUDE | SITE_LONGITUDE | SITE_CITY | SITE_COUNTRY | SITE_ZIP_CODE |
|    1    | Batcave    |   +49123456789  |                 |   1.402636    |    7.273922    |           |              |               |
|    2    | BigCompany |    987654321    |   147852369     |               |                |  Berlin   |   Germany    |     14167     |
----------------------------------------------------------------------------------------------------------------------------------------

我意识到这可能会令人困惑。请告诉我,除了更多信息或更好地解释某些部分之外,我还能帮助您如何帮助我。

我也不希望1个SQL查询(每个实体)来做这个伎俩。我意识到可能有超过1个查询,并且它/它们很可能需要由PHP(例如)“组装”,以便真正使其动态化。因此,即使有人甚至可以解释我如何仅仅因为我上面的假设属性(属性)获得所有这些信息,我已经非常感激了!

感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

这是一个有趣的问题!这可以用动态sql处理。在下面的代码中,使用临时表重新创建了模式。代码可以转换为一个存储过程,它将entity_table_id作为参数,然后选择entity_object_id作为entity_table_name +'id',然后选择每个property_value作为具有相应property_code作为标题的列。

-- load EAV tables
if object_id('tempdb..#entity_tables') is not null
    drop table #entity_tables
create table #entity_tables(entity_table_id int,entity_table_name varchar(255))
insert into #entity_tables values
    (1,'person'),
    (2,'site')
if object_id('tempdb..#property') is not null
    drop table #property
create table #property(property_id int,property_code varchar(255))
insert into #property values
    (1,'PERSON_FIRST_NAME'),
    (2,'PERSON_LAST_NAME'),
    (3,'PERSON_BIRTH_DATE'),
    (4,'SITE_NAME'),
    (5,'SITE_PHONE_NR_1'),
    (6,'SITE_PHONE_NR_2'),
    (7,'SITE_LATITUDE'),
    (8,'SITE_LONGITUDE'),
    (9,'SITE_CITY'),
    (10,'SITE_COUNTRY'),
    (11,'SITE_ZIP_CODE')
if object_id('tempdb..#property_value_varchar') is not null
    drop table #property_value_varchar
create table #property_value_varchar(property_value_id int,property_id int,entity_table_id int,entity_object_id int,property_value varchar(255))
insert into #property_value_varchar values
    (1,1,1,1,'Richard'),
    (2,2,1,1,'Hammer'),
    (3,1,1,2,'Bruce'),
    (4,2,1,2,'Heaton'),
    (5,4,2,1,'BatCave'),
    (6,5,2,1,'+49123456789'),
    (7,4,2,2,'BigCompany'),
    (8,5,2,2,'987654321'),
    (9,6,2,2,'147852369'),
    (10,9,2,2,'Berlin'),
    (11,10,2,2,'Germany'),
    (12,11,2,2,'14167')
if object_id('tempdb..#property_value_datetime') is not null
    drop table #property_value_datetime
create table #property_value_datetime(property_value_id int,property_id int,entity_table_id int,entity_object_id int,property_value datetime)
insert into #property_value_datetime values
(1,3,1,1,'1985-05-31')
if object_id('tempdb..#property_value_number') is not null
    drop table #property_value_number
create table #property_value_number(property_value_id int,property_id int,entity_table_id int,entity_object_id int,property_value float)
insert into #property_value_number values
(1,7,2,1,1.402636),
(2,8,2,1,7.273922)

-- create dynamic sql to get all data conditioned on #entity_tables.table_id value
declare @tableid int,@sql varchar(max)
set @tableid = 1 -- this could be passed as a parameter

-- get pivot code with #ColumnList# placeholders to be added below
select @sql = 'select entity_object_id ' + entity_table_name + 'id,
    #ColumnListCast#
from    (
        select
            e.entity_table_name,
            pv.entity_object_id,
            pv.property_value,
            p.property_code
        from #entity_tables e
            inner join  (
                        select entity_table_id,entity_object_id,property_id,property_value from #property_value_varchar union all
                        select entity_table_id,entity_object_id,property_id,cast(property_value as varchar(255)) from #property_value_datetime union all
                        select entity_table_id,entity_object_id,property_id,cast(property_value as varchar(255)) from #property_value_number
                        ) pv
                on pv.entity_table_id = e.entity_table_id
            inner join #property p
                on p.property_id = pv.property_id
        where e.entity_table_id = ' + cast(@tableid as varchar(5)) + '
        ) p
    pivot   (
            max(property_value)
            for property_code in    (
                                    #ColumnList#
                                    )
            ) piv' from #entity_tables where entity_table_id = @tableid

-- get column list with cast version for diffferent data types
declare @ColumnList varchar(max),
        @ColumnListCast nvarchar(max)
set @ColumnList = ''
set @ColumnListCast = ''
select  @ColumnList = @ColumnList + '[' + p.property_code + ']' + case row_number() over(order by p.property_id desc) when 1 then '' else ',' end,
        @ColumnListCast = @ColumnListCast + 'cast([' + p.property_code + '] as ' + t.CastValue + ') [' + p.property_code + ']' + case row_number() over(order by p.property_id desc) when 1 then '' else ',' end
from #property p
    inner join  (
                select property_id,'varchar(255)' CastValue from #property_value_varchar where entity_table_id = @tableid union
                select property_id,'datetime' CastValue from #property_value_datetime where entity_table_id = @tableid union
                select property_id,'float' CastValue from #property_value_number where entity_table_id = @tableid
                ) t
        on t.property_id = p.property_id
order by p.property_id

set @sql = replace(replace(@sql,'#ColumnList#',@ColumnList),'#ColumnListCast#',@ColumnListCast)

exec(@sql)