【问题标题】:Getting single records from multiple rows by id in one column通过一列中的 id 从多行中获取单个记录
【发布时间】:2014-02-15 13:22:27
【问题描述】:

我正在尝试从 SQL Server 2008 中的查询中获取一些记录。

table的存储方式是这样的

P_Id        Activity_Id
-----------------------
1234        53
1234        510
4567        65
4567        510
3456        53
3456        540
5678        53
5678        510

现在我需要得到的是哪个 P_Id 预订了活动 53 和 510 或 65 和 510

如果我这样做,它不会给我任何记录

SELECT  P_Id FROM ActivitiesPerPerson WHERE Activity_Id = 53 AND Activity_Id = 510

如果我这样做

SELECT  P_Id FROM ActivitiesPerPerson WHERE Activity_Id IN (53, 65, 510)

它给了我一些结果,但它给了我只预订了活动 53 的人和其他人。

我需要同时获取一起预订 53 和 510 的人员 ID 以及一起预订 65 和 510 的人员 ID。如果可能,所有结果都相同。

我有一个关于这个的 sql fiddle http://sqlfiddle.com/#!3/b4c11/2

提前致谢

【问题讨论】:

    标签: sql sql-server database resultset


    【解决方案1】:

    将您的问题解释为“查找在给定集合中完成所有活动的所有人”,您可以这样做:

    SELECT P_Id, COUNT(*) 
      FROM ActivitiesPerPerson 
      WHERE Activity_Id IN (50, 510, 800)
      GROUP BY P_Id
      HAVING COUNT(*) = 3;
    

    (针对您的小提琴数据进行了调整,这假设 ActivitiesPerPerson 在每个 (PersonId, ActivityID) 对中是唯一的 - 即同一个人不能多次执行相同的活动。您还需要调整 @ 987654325@每次更改条件的数量。

    如果您需要排除相同(person, activity) 组合重复出现误报的可能性,请像这样区分它们:

    SELECT P_Id, COUNT(*)
    FROM
    (
       SELECT DISTINCT P_Id, Activity_ID
       FROM ActivitiesPerPerson 
       WHERE Activity_Id IN (50, 510, 800)
     ) X
    GROUP BY P_Id
    HAVING COUNT(*) = 3;
    

    Activities 标准集可以表示为表变量或表值参数 - 这有助于这种方法的可维护性(尤其是计数 = x 变化)。 Another Fiddle

    【讨论】:

    • 我更喜欢你的答案!
    • 这是完成问卷要求的正确方法。荣誉。
    • 我认为您误解了这个问题。但是,使用 HAVING COUNT(DISTINCT Activity_Id) = 3; 可以更轻松地解决第一个查询中的重复问题;
    • AFAIK COUNT(DISTINCT x) 不能在 sql server 的 HAVING 子句中使用?
    • 这不是“找到所有在给定集合中完成所有活动的人”。我需要找到一起预订 Activity 53 和 510 或 65 和 510 的人。我不需要只有 53 而不是 510 的案例。结果中我不需要有其他活动的人。他们不能一起预订 53、65 和 510。只能是我上面提到的两个选项,所以 IN 不起作用。
    【解决方案2】:

    这有点可怕但有效。取自你的小提琴的数字。

    SELECT *
    FROM
       (SELECT P_Id
       FROM ActivitiesPerPerson 
       WHERE Activity_Id = 510) AS x INNER JOIN
       (SELECT P_Id
       FROM ActivitiesPerPerson 
       WHERE Activity_Id = 50) AS y ON x.P_Id = y.P_Id INNER JOIN
       (SELECT P_Id
       FROM ActivitiesPerPerson 
       WHERE Activity_Id = 800) AS z ON x.P_Id = z.P_Id
    

    【讨论】:

      【解决方案3】:

      如果您总是只需要 2 个活动的交集,您可以执行以下操作。我会将它包装在一个表函数中,该函数将 2 个活动 ID 作为输入变量。

      Declare @Act1 Int
      Declare @Act2 Int
      
      Set @Act1 = 50
      Set @Act2 = 510
      
      SELECT A1.P_Id 
      FROM   
             ActivitiesPerPerson A1
      INNER JOIN
             ActivitiesPerPerson A2
      ON     A1.P_Id = A2.P_Id
      WHERE  A1.Activity_Id = @Act1
      AND    A2.Activity_Id = @Act2
      

      SQL Fiddle here

      【讨论】:

        【解决方案4】:

        您将扫描表一次并按 P_Id 分组。完成后,您需要一个聚合来告诉您该 P_Id 是否有一个 Activity_Id x。你可以简单地用一个 case 子句来总结。然后只取那些符合您条件的 P_Id:

        SELECT P_Id
        FROM ActivitiesPerPerson 
        WHERE Activity_Id IN (53, 65, 510)
        GROUP BY p_Id
        HAVING
        (
          SUM( CASE WHEN Activity_Id = 53 THEN 1 ELSE 0 END ) > 0
          AND
          SUM( CASE WHEN Activity_Id = 510 THEN 1 ELSE 0 END ) > 0
        )
        OR
        (
          SUM( CASE WHEN Activity_Id = 65 THEN 1 ELSE 0 END ) > 0
          AND
          SUM( CASE WHEN Activity_Id = 510 THEN 1 ELSE 0 END ) > 0
        );
        

        http://sqlfiddle.com/#!3/b4c11/38

        【讨论】:

        • 请注意:WHERE 子句实际上并不需要,但可能会提高性能。另一个注意事项:您的示例 (53 AND 510) OR (65 AND 510) 当然可以简化为 510 AND (53 OR 65)。
        • 谢谢!这正是我想要的!
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-14
        • 1970-01-01
        • 2012-09-29
        相关资源
        最近更新 更多