【问题标题】:How to fetch records that have an alternate entry如何获取具有备用条目的记录
【发布时间】:2016-05-22 19:22:36
【问题描述】:

我需要一些帮助来获取具有与唯一值关联的备用条目集的记录(例如:user_id)

我希望输出只有 (1111,2222,3333)

场景如下: user_id 1111 从 2005-01-01 到 2006-12-31 参加了 .net 课程

他后来从 2007-01-01 到 2009-12-31 参加了 java 后来他又回到了.net

所以我想检索这些 user_id 的

user_id 4444 不应出现在输出中,因为没有替代课程。

更新:4444 从 2007 年到 2009 年再次开始了他的 Java 课程 从 2010 年到 2012 年参加 Java,后来他参加了 .net,但从未来过 回到Java,所以他必须被排除在输出之外

如果使用 Group by,它将考虑记录,而不考虑替代课程名称。 我们可以通过循环和比较替代课程名称来创建一个过程来完成此操作,但我想知道查询是否可以做到这一点?

【问题讨论】:

  • 我不明白为什么 4444 不应该出现在结果集中。您能解释一下您是如何定义“替代课程”的吗?
  • 我刚刚更新了原因。
  • 如果两个课程的时间段重叠,第二个开始在第一个结束之前呢?
  • @dnoeth 这正是迁移数据时发生的事情(发生了日期重叠),因此我们将通过从主要来源获取数据来纠正该问题。

标签: sql oracle plsql oracle11g group-by


【解决方案1】:

您可以使用两个INNER JOIN 操作:

SELECT DISTINCT user_id
FROM mytable AS t1
INNER JOIN mytable AS t2 
   ON t1.user_id = t2.user_id AND t1.id < t2.id AND t1.course_name <> t2.course_name
INNER JOIN mytable AS t3
   ON t2.user_id = t3.user_id AND t2.id < t3.id AND t1.course_name = t3.course_name

我假设id 是一个自动递增字段,它反映了行在数据库中插入的顺序。否则,您应该使用日期字段来代替它。

【讨论】:

  • 好答案。我会选择不同的 user_id,因为用户可能会来回多次反弹,而您的结果集可能会重复。
  • @StanShaw 不错。更新。非常感谢。
  • 对不起,实际上不会考虑 id,我用一些示例数据创建了我当前情况的副本。实时 id 不会采用增量格式。好逻辑谢谢。将运行此
  • @Ramaraju.d 这应该不是问题,因为您可以在其位置使用end_date
  • @Ramaraju.d 如果您有权限,我会在此表中添加一个自动递增的 ID 字段。我建议您创建的几乎每张表都有一个。我能想到的唯一例外是“连接表”,其中所有列都是外键 - 在某些情况下它也会有帮助。
【解决方案2】:

与 Girogos Betsos 的回答相同,只是使用 select distinct 来防止重复。

SELECT DISTINCT user_id
FROM mytable AS t1
INNER JOIN mytable AS t2 
   ON t1.user_id = t2.user_id AND t1.Start_Date < t2.Start_Date AND 
      t1.course_name <> t2.course_name
INNER JOIN mytable AS t3
   ON t2.user_id = t3.user_id AND t2.Start_Date < t3.Start_Date AND 
      t1.course_name = t3.course_name

编辑:使用 Start_Date,因为答案已更新且 ID 不一定是连续的。

【讨论】:

    【解决方案3】:

    这是一个使用窗口聚合函数而不是多个自连接的版本:

    SELECT DISTINCT user_id
    FROM
     (
       SELECT user_id
          ,course_name
          ,start_date
          ,RANK() -- number all courses 
           OVER (PARTITION BY user_id
                 ORDER BY start_date)
           -
           RANK() -- number each course
           OVER (PARTITION BY user_id, course_name
                 ORDER BY start_date) AS x
       FROM tab
     ) dt
    GROUP BY user_id, course_name
    HAVING MIN(x) <> MAX(x) -- same course but another inbetween
    

    如果用户在一个系列中有多次课程,x 将保持不变,如果中间有另一个课程,它将改变:

    java     1  -  1 = 0
    java     2  -  2 = 0  <--- min
    .net     3  -  1 = 2
    java     4  -  3 = 1  <--- max
    
    java     1  -  1 = 0
    java     2  -  2 = 0  
    .net     3  -  1 = 2
    .net     4  -  2 = 2
    

    【讨论】:

      【解决方案4】:

      使用单表扫描且不依赖GROUP BY

      WITH table_name ( user_id, start_date, end_date, course_name, id ) AS (
        SELECT 1111, DATE '2005-01-01', DATE '2006-12-31', '.net',  1 FROM DUAL UNION ALL
        SELECT 1111, DATE '2007-01-01', DATE '2009-12-31', 'java',  2 FROM DUAL UNION ALL
        SELECT 1111, DATE '2010-01-01', DATE '2020-12-31', '.net',  3 FROM DUAL UNION ALL
        SELECT 2222, DATE '2005-01-01', DATE '2006-12-31', 'java',  4 FROM DUAL UNION ALL
        SELECT 2222, DATE '2007-01-01', DATE '2008-12-31', '.net',  5 FROM DUAL UNION ALL
        SELECT 2222, DATE '2009-01-01', DATE '2012-12-31', '.net',  6 FROM DUAL UNION ALL
        SELECT 2222, DATE '2013-01-01', DATE '2016-12-31', 'java',  7 FROM DUAL UNION ALL
        SELECT 3333, DATE '2005-01-01', DATE '2007-12-31', 'java',  8 FROM DUAL UNION ALL
        SELECT 3333, DATE '2007-01-01', DATE '2008-12-31', '.net',  9 FROM DUAL UNION ALL
        SELECT 3333, DATE '2009-01-01', DATE '2013-12-31', 'java', 10 FROM DUAL UNION ALL
        SELECT 3333, DATE '2014-01-01', DATE '2016-12-31', '.net', 11 FROM DUAL UNION ALL
        SELECT 4444, DATE '2007-01-01', DATE '2009-12-31', 'java', 12 FROM DUAL UNION ALL
        SELECT 4444, DATE '2010-01-01', DATE '2012-12-31', 'java', 13 FROM DUAL UNION ALL
        SELECT 4444, DATE '2013-01-01', DATE '2015-12-31', '.net', 14 FROM DUAL UNION ALL
        SELECT 4444, DATE '2016-01-01', DATE '2016-12-31', '.net', 15 FROM DUAL
      )
      SELECT DISTINCT user_id
      FROM   (
        SELECT user_id,
               LEAD( course_name )
                 OVER ( PARTITION BY user_id, course_name ORDER BY start_date )
                 AS next_same_course,
               LEAD( course_name )
                 OVER ( PARTITION BY user_id ORDER BY start_date )
                 AS next_course
        FROM   table_name
      )
      WHERE  next_same_course IS NOT NULL
      AND    next_course <> next_same_course;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-05-24
        • 1970-01-01
        • 1970-01-01
        • 2023-03-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多