【问题标题】:Selecting objects that are associated with similar datasets选择与相似数据集关联的对象
【发布时间】:2016-05-19 21:07:39
【问题描述】:

我正在尝试从 [Company] 表中选择与至少一个其他公司共享的所有公司行,相同数量的员工(来自具有 CompanyId 列的 [Employee] 表),其中每组各个员工共享同一组 LocationId([Employee] 表中的一列)并且比例相同。

因此,例如,该查询将选择两家公司,每家公司有 3 名员工,每个公司的 locationId 分别为 1、2 和 2。

[Employee]

 EmployeeId |  CompanyId  | LocationId | 
========================================
  1         |  1          |  1      
  2         |  1          |  2 
  3         |  1          |  2
  4         |  2          |  1 
  5         |  2          |  2 
  6         |  2          |  2 
  7         |  3          |  3



[Company]

 CompanyId | 
============
  1   |     
  2   |    
  3   |


  Returns the CompanyIds:
  ======================
  1
  2

之所以选择公司 ID 1 和 2,是因为它们与至少一家其他公司有共同之处: 1. 员工人数(3 名员工); 2. 与这些员工关联的 LocationId 的数量/比例(1 名员工的 LocationId 为 1,2 名员工的 LocationId 为 2)。

到目前为止,我想我想使用HAVING COUNT(?) > 1 声明,但我无法弄清楚细节。有没有人有什么建议?

【问题讨论】:

  • 一些细节怎么样?这将是一个很好的起点。 spaghettidba.com/2015/04/24/…
  • 不太清楚你在这里问什么。需要一些样本数据和预期输出。
  • 抱歉没有更清楚。我添加了一些表格作为示例。
  • 好的,所以您添加了员工表。这个查询的逻辑是什么?你的规则对我来说没有多大意义。并且不确定公司表与此有什么关系。
  • 我在表格下方添加了有关预期结果的更多信息。如果这不能说明我在追求什么,请告诉我。

标签: sql sql-server tsql


【解决方案1】:

这很丑,但我能想到的唯一方法:

;with CTE as (
    select c.Id,
        (
            select e.Location, count(e.Id) [EmployeeCount]
            from Employee e
            where e.IdCompany=c.Id
            group by e.Location
            order by e.Location
            for xml auto
        ) LocationEmployeeData
    from Company c
)
select c.Id
from Company c
join (
    select x.LocationEmployeeData, count(x.Id) [CompanyCount]
    from CTE x
    group by x.LocationEmployeeData
    having count(x.Id) >= 2
) y on y.LocationEmployeeData = (select LocationEmployeeData from CTE where Id = c.Id)

见小提琴:http://www.sqlfiddle.com/#!6/6bc16/5

它通过将每个位置数据(多行)的员工人数编码为每个公司的 xml 字符串来工作。

CTE 代码本身:

select c.Id,
    (
        select e.Location, count(e.Id) [EmployeeCount]
        from Employee e
        where e.IdCompany=c.Id
        group by e.Location
        order by e.Location
        for xml auto
    ) LocationEmployeeData
from Company c

产生如下数据:

Id  LocationEmployeeData
1   <e Location="1" EmployeeCount="2"/><e Location="2" EmployeeCount="1"/>
2   <e Location="1" EmployeeCount="2"/><e Location="2" EmployeeCount="1"/>
3   <e Location="3" EmployeeCount="1"/>

然后它根据这个字符串比较公司(而不是试图确定多行是否匹配等)。

【讨论】:

  • 感谢您的建议!这会很好地扩展吗?例如,假设我有大约 10,000 家公司,每家公司约有 1000 名员工。这样的查询是否能够在大约 24 小时内在“半高性能条件”下执行?
  • 我不希望它花费将近 24 小时,但您确实需要对其进行测试,以了解它在真实数据的实际负载下的性能。 CTE 数据不会被缓存,而是在被引用时执行,因此这里可能需要改进。例如,您可以使用临时表来存储 CTE 数据。
  • 谢谢,看起来这样可以了。最后一个问题:有没有什么简单的方法可以从结果中排除那些没有员工的公司?
  • 实际上,它看起来像 it does that already! 我猜是 CTE 上的 inner join 从 Employee 中选择 - 没有 Employee,没有行。
  • 糟糕。我的疏忽。非常感谢您对此提供的所有帮助!看到这里的解决方案,我敢肯定,我不可能在任何合理的时间内自己解决这个问题。
【解决方案2】:

另一种解决方案可能如下所示。不过,它也需要提前进行性能测试(我对&lt;&gt; 类型的连接不太自信)。

with List as
(
  select
    IdCompany,
    Location,
    row_number() over (partition by IdCompany order by Location) as RowId,
    count(1) over (partition by IdCompany) as LocCount
  from
    Employee
) 
select
  A.IdCompany
from List as A
  inner join List as B on A.IdCompany <> B.IdCompany
  and A.RowID = B.RowID
  and A.LocCount = B.LocCount
group by
  A.IdCompany, A.LocCount
having
  sum(case when A.Location = B.Location then 1 else 0 end) = A.LocCount

相关小提琴:http://sqlfiddle.com/#!6/d9f2e/1

【讨论】:

  • 直观地说,我比我更喜欢你的解决方案。我希望它会更快,而且for xml 无论如何都感觉很脏。不过,我会很高兴看到大型数据集上的性能比较!
  • 快速更新:这不是正式的性能测试或任何东西,但“for xml”方法(上图)在我的数据集上执行大约需要 30 秒,我取消了这个 ' 加入' 大约 3 分钟后。我对结果有点惊讶。感谢您的帮助!
  • 我想我并不感到惊讶。如果您有 10,000 个客户,并且每个客户的位置数量没有太大差异,那么不幸的是,这会产生大量组合。
猜你喜欢
  • 2018-11-20
  • 2017-03-15
  • 1970-01-01
  • 2023-01-30
  • 1970-01-01
  • 1970-01-01
  • 2020-11-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多