假设我的Sql Serer 2008数据库中有3个表:
CREATE TABLE [dbo].[Properties](
[PropertyId] [int] NOT NULL,
[PropertyName] [nvarchar](50) NOT NULL
)
CREATE TABLE [dbo].[Entities](
[EntityId] [int] NOT NULL,
[EntityName] [nvarchar](50) NOT NULL
)
CREATE TABLE [dbo].[PropertyValues](
[EntityId] [int] NOT NULL,
[PropertyId] [int] NOT NULL,
[PropertyValue] [int] NOT NULL
)
我的任务是找到与给定对象完全相同的业务对象(具有完全相同值的完全相同的属性集的对象)。绩效至关重要。
有什么建议吗?
[ADDED] 例如,Entities表中有一个EntityId = 1的条目。在PropertyValues表中有3行与此条目相关:
EntityId PropertyId PropertyValue
1 4 Val4
1 5 Val5
1 6 Val6
要求是在Entity表中找到PropertyValues表中有3个相关行的其他条目,这些行包含与EntityId = 1的行相同的数据(除了EntityId列)
[ADDED] 请看我的新问题:Best approach to store data which attributes can vary
[BOUNTY1] 谢谢大家。答案非常有帮助。我的任务很复杂(但这种复杂性在性能方面很有用)。请看下面的详细信息:
现在,有几种类型的实体。每个实体都有自己的一组属性。
是否可以使用此信息提高性能?
[BOUNTY2] 还有第二个并发症:
问题是:我如何编写一个脚本,为他们选择所有实体和附加列IsValid。
答案 0 :(得分:5)
;with cteSource as
(
select PropertyId,
PropertyValue
from PropertyValues
where EntityId = @EntityID
)
select PV.EntityId
from PropertyValues as PV
inner join cteSource as S
on PV.PropertyId = S.PropertyId and
PV.PropertyValue = S.PropertyValue and
PV.EntityId <> @EntityID
group by PV.EntityId
having count(*) = (select count(*)
from cteSource) and
count(*) = (select count(*)
from PropertyValues as PV1
where PV1.EntityId = PV.EntityId)
您可以添加以下条款:
where -- exlude entities with deleted properties
PV.EntityID not in (select PV2.EntityID
from Properties as P
inner join PropertyValues as PV2
on P.PropertyID = PV2.PropertyID
where P.IsDeleted = 1)
-- exclude entities with missing EntityType
and PV.EntityID not in (select E.EntityID
from Entities as E
where E.EntityType is null)
修改强>
如果要针对某些示例数据测试查询,可以在此处执行此操作: http://data.stackexchange.com/stackoverflow/q/110243/matching-properties
答案 1 :(得分:1)
My task is to find business objects which are exactly same as given object (ones which have exactly same set of properties with exactly same values). Performance is critical.
方法可能会有所不同,具体取决于对象通常具有的平均数量,几个与几十个。
假设对象具有不同数量的属性:
我会从二元组(PropertyValues.PropertyId,PropertyValues.PropertyValue)上的复合非唯一索引开始,以获得性能。
然后,给定一个实体ID,我会将其propertyid,propertyvalue对选择到游标中。
[编辑: 不确定(entityid,propertyid)在您的系统中是唯一的,还是您允许实体使用相同属性id的多个实例,例如FavoriteColors:
entityid propertyid property value
1 17 blue
1 17 dark blue
1 17 sky blue
1 17 ultramarine
您还需要monad(PropertyValues.entityid)上的非唯一索引或(PropertyValues.entityid,PropertyValues.propertyid)上的复合索引;如果您想要防止同一属性与实体多次关联,那么复合索引将是唯一的。
如果属性可以多次出现,则应该在Properties表中有一个CanBeMultivalued标志。如果你想阻止它,你应该在三元组(entityid,propertyid,propertyvalue)上有一个唯一索引:
entityid propertyid property value
1 17 blue
1 17 blue
如果您将此三元组编入索引,则不需要(entityid)索引或PropertyValues表中的(entityid,propertyid)复合索引。
[/编辑]
然后我会创建一个临时表来存储匹配的实体ID。
然后我会迭代上面的光标来抓取给定实体的propertyid,propertyvalue对,一次一对,并在每次迭代时发出一个select语句:
insert into temp
select entityid from PropertyValues
where propertyid = mycursor.propertyid and propertyvalue = mycursor.propertyvalue
在循环结束时,临时表中有一组非独特的实体,用于至少有一个与给定对象相同的属性的所有实体。但是你想要的那些必须具有所有属性。
由于您知道给定对象具有多少属性,因此您可以执行以下操作以仅获取具有与给定对象相同的所有属性的实体:
select entityid from temp
group by entityid having count(entityid) = {the number of properties in the given object}
附录:
在使用给定对象的第一个属性 - 值对选择所有可能匹配之后,您的临时表将不会缺少任何可能的匹配;相反,它将包含不完美匹配的实体,必须以某种方式将其丢弃,或者通过忽略(通过具有...子句的组)或者从临时表中明确删除。
此外,在循环的第一次迭代之后,您可以探索临时表和PropertyValues表之间的内部联接可能提供一些性能增益的可能性:
select entityid from propertvalues
>> inner join temp on temp.entityid = propertyvalues.entityid <<
where propertyid = mycursor.propertyid and propertyvalue = mycursor.propertyvalue
你也可以尝试在第一次迭代后从temp中删除entityids:
delete from temp
where not exists
(
select entityid from propertyvalues
inner join temp on temp.entityid = propertyvalues.entityid
where propertyid = mycursor.propertyid and propertyvalue = mycursor.propertyvalue
)
或者,如果您存储了一些有关property-frequency的元数据,则可以进一步优化此循环方法。最理想的情况是,在查找给定实体的匹配项时,您需要从最不常出现的属性 - 值对开始。您可以按升序频率对给定对象的属性 - 值对进行排序,这样在循环中您将首先查找最稀有的对象。这将在循环的第一次迭代中将潜在匹配的集合减少到其最小可能的大小。
当然,如果在给定对象的第一个属性 - 值对用于查找匹配项后,temp在任何时候都是空的,那么您将知道您的给定对象没有匹配项,因为您找到了属性值没有其他实体拥有,你可以退出循环并返回一个空集。
答案 2 :(得分:1)
一种看待这种情况的方法是,如果我拥有所有的基础球牌,那么我们就没有相同的棒球卡。但如果你还拥有我拥有的所有棒球卡,那么我们拥有完全相同的棒球卡。我们正在按团队观察,这有点复杂。团队可以计算匹配计数,我的计数和你的计数,并比较这3个计数,但这是3个连接。这个解决方案是2个连接,我认为它会比3个连接选项更快。
对我来说,奖金问题没有意义。作为表的更改但该表名称与任何表都不匹配。需要这些奖金问题的完整表格描述。
以下是2个加入选项:
select [m1].[IDa] as [EntityId1], [m1].[IDb] as [EntityId2]
from
( select [PV1].[EntityId] as [IDa], [PV2].[EntityId] as [IDb]
from [PropertyValue] as [PV1]
left outer join [PropertyValue] as [PV2]
on [PV2].[EntityId] <> [PV1].[EntityId]
and [PV2].[PropertyId] = [PV1].[PropertyId]
and [PV2].[PropertyValue] = [PV1].[PropertyValue]
group by [PV1].[EntityId], [PV2].[EntityId]
having count(*) = count([PV2].[EntityId])
) as [m1]
join
( select [PV1].[EntityId] as [IDa], [PV2].[EntityId] as [IDb]
from [PropertyValue] as [PV1]
right outer join [PropertyValue] as [PV2]
on [PV2].[EntityId] <> [PV1].[EntityId]
and [PV2].[PropertyId] = [PV1].[PropertyId]
and [PV2].[PropertyValue] = [PV1].[PropertyValue]
group by [PV1].[EntityId], [PV2].[EntityId]
having count(*) = count([PV1].[EntityId]))
) as [m2]
on [m1].[IDa] = [m2].[IDa] and [m1].[IDb] = [m2].[IDb]
以下是基于3连接数的选项:
select [m1].[IDa] as [EntityId1], [m1].[IDb] as [EntityId2]
from
( select [PV1].[EntityId] as [IDa], [PV2].[EntityId] as [IDb], COUNT(*) as [count]
from [PropertyValue] as [PV1]
join [PropertyValue] as [PV2]
on [PV2].[EntityId] <> [PV1].[EntityId]
and [PV2].[PropertyId] = [PV1].[PropertyId]
and [PV2].[PropertyValue] = [PV1].[PropertyValue]
group by [PV1].[EntityId], [PV2].[EntityId]
) as [m1]
join
( select [PV1].[EntityId] as [IDa], COUNT(*) as [count]
from [PropertyValue] as [PV1]
group by [PV1].[EntityId]
having count(*) = count([PV1].[sID]))
) as [m2]
on [m1].[IDa] = [m2].[IDa] and [m1].[count] = [m2].[count]
join
( select [PV2].[EntityId] as [IDb], COUNT(*) as [count]
from [PropertyValue] as [PV2]
group by [PV2].[EntityId]
) as [m3]
on [m1].[IDb] = [m3].[IDb] and [m1].[count] = [m3].[count]
答案 3 :(得分:1)
我的任务是找到与给定完全相同的业务对象 对象(具有完全相同的属性集的对象) 相同的价值观。)
如果“给定的目标”被描述为例如#PropertyValues,所以查询将是:
create table #PropertyValues(
[PropertyId] [int] NOT NULL,
[PropertyValue] [int] NOT NULL
)
insert #PropertyValues
select
3, 3 -- e.g.
declare
@cnt int
select @cnt = count(*) from #PropertyValues
select
EntityId
from
PropertyValues pv
left join #PropertyValues t on t.PropertyId = pv.PropertyId and t.PropertyValue = pv.PropertyValue
group by
EntityId
having
count(t.PropertyId) = @cnt
and count(pv.PropertyId) = @cnt
drop table #PropertyValues
但是,如果性能如此重要,您可以在表实体上创建特殊的索引字段,例如EntityIndex varchar(8000),它将由PropertyValues表上的触发器填充为convert(char(10),PropertyId)+ convert(char(10),PropertyValue)(对于实体的所有属性,已排序!)。因此,可以通过该领域进行快速搜索。
答案 4 :(得分:0)
我认为这只是一个简单的自我加入:
select P2.EntityID,E.EntityName
from PropertyValues P1
inner join PropertyValues P2
on P1.PropertyID = P2.PropertyID
and P1.PropertyValue = P2.PropertyValue
inner join Entity E
on P2.EntityID = E.EntityID
where P1.EntityId = 1
and P2.EntityId <> 1
group by P2.EntityID, E.EntityName