T-SQL在引用表中找到完全相同的值

时间:2011-08-16 09:33:46

标签: sql-server tsql

假设我的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
)
  1. 表属性包含可能的一组属性,可以为业务对象设置值。
  2. 表实体包含从app。
  3. 配置的业务对象
  4. 表3包含业务对象的选定属性值。每个业务对象都可以包含自己的一组属性(即,可以为第一个对象配置“Property1”,但不为第二个对象配置)。
  5. 我的任务是找到与给定对象完全相同的业务对象(具有完全相同值的完全相同的属性集的对象)。绩效至关重要。

    有什么建议吗?

    [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] 谢谢大家。答案非常有帮助。我的任务很复杂(但这种复杂性在性能方面很有用)。请看下面的详细信息:

    • 添加了名为EntityTypes的新表
    • EntityTypeId列已添加到“实体和属性表”
    • 现在,有几种类型的实体。每个实体都有自己的一组属性。

      是否可以使用此信息提高性能?

    [BOUNTY2] 还有第二个并发症:

    • IsDeleted列已添加到Property表
    • PropertyValues表可以包含已从数据库中删除的Properties的值。具有此类属性的实体被视为无效。
    • 某些实体没有EntityType集的每个属性的值。这些实体也被视为无效。

    问题是:我如何编写一个脚本,为他们选择所有实体和附加列IsValid。

5 个答案:

答案 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