我已经搜索了这个,并试图在过去几天自己做,但我不能。我在搜索范围内最接近的是这个答案,也是Stack Overflow:EAV Select query from spreaded value tables
所以我在这里,转向互联网!
所以,我有一个使用EAV(实体 - 属性 - 值)模型的数据库。但这里有一个问题:实际的实体并没有直接连接到其他EAV表。让我更具体一点;假设有一个Person和Site表,并且他们只有主键:person_id
和site_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_id
和entity_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(例如)“组装”,以便真正使其动态化。因此,即使有人甚至可以解释我如何仅仅因为我上面的假设属性(属性)获得所有这些信息,我已经非常感激了!
感谢您的帮助!
答案 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)