【问题标题】:Query: getting the last record for each member查询:获取每个成员的最后一条记录
【发布时间】:2013-01-04 21:30:31
【问题描述】:

给定一个表格(“表格”)如下(对于 CSV 样式感到抱歉,因为我不知道如何使用 Stack Overflow 编辑器使它看起来像一个表格):

id、成员、数据、开始、结束
1,001,abc,12/1/2012,12/31/2999
2,001,def,1/1/2009,11/30/2012
3,002,ghi,1/1/2009,12/31/2999
4,003,jkl,1/1/2012,10/31/2012
5,003,mno,8/1/2011,12/31/2011

如果使用 Ruby Sequel,我应该如何编写我的查询,这样我会得到以下数据集作为回报。

id、成员、数据、开始、结束
1,001,abc,12/1/2012,12/31/2999
3,002,ghi,1/1/2009,12/31/2999
4,003,jkl,1/1/2012,10/31/2012

我从原始表中获取每个(不同)成员的最新(最大结束日期值)记录。

如果我将表转换为数组,我可以得到答案,但如果可能的话,我正在寻找 SQL 或 Ruby Sequel 查询中的解决方案。谢谢。

额外的功劳:这篇文章的标题很蹩脚……但我想不出一个好标题。如果你有,请提供一个更好的标题。谢谢。

【问题讨论】:

    标签: sql ruby sequel


    【解决方案1】:

    这个的续集版本有点吓人。我能想到的最好的办法是使用子选择,因为您需要在两列上连接表和子选择,所以使用Querying in Sequel 中描述的“连接块”。这是上面 Knut 程序的修改版本:

    require 'csv'
    require 'sequel'
    
    # Create Test data     
    DB = Sequel.sqlite()
    DB.create_table(:mytable){
      field :id
      String :member
      String :data
      String :start # Treat as string to keep it simple
      String :end   # Ditto
    }
    CSV.parse(<<xx
      1,"001","abc","2012-12-01","2999-12-31"
      2,"001","def","2009-01-01","2012-11-30"
      3,"002","ghi","2009-01-01","2999-12-31"
      4,"003","jkl","2012-01-01","2012-10-31"
      5,"003","mno","2011-08-01","2011-12-31"
    xx
    ).each{|x|
      DB[:mytable].insert(*x)
    }
    
    # That was all setup, here's the query
    ds = DB[:mytable]
    result = ds.join(ds.select_group(:member).select_append{max(:end).as(:end)}, :member=>:member) do |j, lj, js|
      Sequel.expr(Sequel.qualify(j, :end) => Sequel.qualify(lj, :end))
    end
    puts result.all
    

    这给了你:

    {:id=>1, :member=>"001", :data=>"abc", :start=>"2012-12-01", :end=>"2999-12-31"}
    {:id=>3, :member=>"002", :data=>"ghi", :start=>"2009-01-01", :end=>"2999-12-31"}
    {:id=>4, :member=>"003", :data=>"jkl", :start=>"2012-01-01", :end=>"2012-10-31"}
    

    在这种情况下,用直接的 SQL 替换最后四行可能更容易。比如:

    puts DB[
      "SELECT a.* from mytable as a 
      join (SELECT member, max(end) AS end FROM mytable GROUP BY member) as b 
      on a.member = b.member and a.end=b.end"].all
    

    这会给你同样的结果。

    【讨论】:

    • 这很棒。完全满足我的需求/好奇心(一种 Sequel 查询方式)和一个额外的 SQL 语句。
    【解决方案2】:

    你的结果的标准是什么?

    如果是键 1,3 和 4,您可以使用 DB[:mytable].filter( :id =&gt; [1,3,4])(下面的完整示例)

    更多关于用sequel过滤的信息,请参考sequel documentation,尤其是Dataset Filtering

    require 'csv'
    require 'sequel'
    
    #Create Test data     
    DB = Sequel.sqlite()
    DB.create_table(:mytable){
      field :id
      field :member
      field :data
      field :start #should be date, not implemented in example
      field :end   #should be date, not implemented in example
    }
    CSV.parse(<<xx
    id,member,data,start,end
     1,001,abc,12/1/2012,12/31/2999
     2,001,def,1/1/2009,11/30/2012
     3,002,ghi,1/1/2009,12/31/2999
     4,003,jkl,1/1/2012,10/31/2012
     5,003,mno,8/1/2011,12/31/2011
    xx
     ).each{|x|
      DB[:mytable].insert(*x)
    }
    #Create Test data - end -
    
    puts DB[:mytable].filter( :id => [1,3,4]).all
    

    【讨论】:

    • 感谢您的回答;但是,您的回答并不能满足我的需要。我想要一种方法,可以只为表中的每个成员(001、002、003)提供 1 个(最新的,通过检查结束日期)记录。
    【解决方案3】:

    在我看来,您从错误的角度来处理问题。 ORM(以及 Sequel)在数据库之上代表了一个不错的 DSL 层,但在其之下,全是 SQL。因此,我会尝试以某种方式制定问题和答案,以获取 SQL 查询,该查询将返回您需要的内容,然后看看它如何翻译成 Sequel 的语言。

    您需要按成员分组并获取每个成员的最新记录,对吗?

    我会采用以下想法(大致):

    SELECT t1.*
    FROM table t1
    LEFT JOIN table t2 ON t1.member = t2.member AND t2.end > t1.end
    WHERE t2.id IS NULL
    

    现在您应该了解如何在 Sequel 中执行左连接,并且您还需要为表设置别名。应该没那么难。

    【讨论】:

    • 非常感谢。你的方法肯定是在正确的方向。我会试一试,看看能不能得到我需要的结果,如果可能的话,提供一个 Ruby Sequel 语句作为回报。
    猜你喜欢
    • 2020-04-09
    • 2020-10-01
    • 2011-01-12
    • 2022-10-14
    • 2021-10-27
    • 2013-12-31
    • 2011-12-17
    • 2017-02-19
    • 1970-01-01
    相关资源
    最近更新 更多