用于获取表的所有条目并且仅包含基于特定列的许多重复项中的第一个的自定义查询

Posted

技术标签:

【中文标题】用于获取表的所有条目并且仅包含基于特定列的许多重复项中的第一个的自定义查询【英文标题】:Custom query to fetch all entries of a table and that only contains first of many duplicates based on a specific column 【发布时间】:2022-01-04 14:17:20 【问题描述】:

我有一个 Location 模型,表格看起来像

id name vin ip_address created_at updated_at
0 default 0 0.0.0.0/0 2021-11-08 11:54:26.822623 2021-11-08 11:54:26.822623
1 admin 1 10.108.150.143 2021-11-08 11:54:26.82885 2021-11-08 11:54:26.82885
2 V122 122 10.108.150.122 2021-11-08 11:54:26.82885 2021-11-08 11:54:26.82885
3 V123 123 10.108.150.123 2021-11-08 11:54:26.82885 2021-11-08 11:54:26.82885
4 V124 124 10.108.150.124 2021-11-08 11:54:26.82885 2021-11-08 11:54:26.82885
5 V122 122 10.108.150.122 2021-11-08 11:54:26.82885 2021-11-08 11:54:26.82885
6 V125 122 10.108.150.125 2021-11-08 11:54:26.82885 2021-11-08 11:54:26.82885

我在 Location 模型中的方法

   def self.find_all_non_duplicate
     return self.find(:all, :conditions => "id <> 1")
   end

我想获取位置表的所有条目,除了 id = 1 的条目并且仅包含基于列 ip_address 的许多重复项中的第一个条目。

因为 id = 2 和 id = 5 的 ip_address 是重复的。我想保留 许多 重复项的第一个条目,即 id = 2

预期的结果是

id name vin ip_address created_at updated_at
0 default 0 0.0.0.0/0 2021-11-08 11:54:26.822623 2021-11-08 11:54:26.822623
2 V122 122 10.108.150.122 2021-11-08 11:54:26.82885 2021-11-08 11:54:26.82885
3 V123 123 10.108.150.123 2021-11-08 11:54:26.82885 2021-11-08 11:54:26.82885
4 V124 124 10.108.150.124 2021-11-08 11:54:26.82885 2021-11-08 11:54:26.82885
6 V125 122 10.108.150.125 2021-11-08 11:54:26.82885 2021-11-08 11:54:26.82885

id 为 1 和 5 的条目将被忽略

【问题讨论】:

使用 self.where.not(id: 1) 这样的东西不起作用?进一步如果你想排除 id=5 self.where.not(id: [1,5]) 我想这应该可以解决问题where.not(id: 1).group(:name) @Chris,谢谢你的建议。为了更清楚,我详细阐述了我的问题。 好的,这里是更新版本where.not(id: 1).where("id in (select min(id) from #self.table_name group by ip_address)") @Chris 虽然这行得通,但它最终是对distinct on 的更长更慢的模仿,distinct on 是为此目的设计和优化的标准 PostgreSQL 子句。看我的回答 【参考方案1】:

您需要的是最近向 RoR 提出的distinct on here 但 not yet merged,正如 @engineersmnky 所指出的那样。在原始 SQL 中,它将如下所示:

select distinct on (ip_address) * 
from test 
where id<>1 
order by ip_address,created_at;

这将转化为 RoR 的

self.where("id <> 1").distinct_on(:ip_address)

或者,直到新功能被接受:

self.where("id <> 1").select("distinct on (ip_address) *")

完整的数据库端测试:

drop table if exists test cascade;
create table test (
    id serial primary key,
    name text,
    vin integer,
    ip_address inet,
    created_at timestamp,
    updated_at timestamp);
insert into test 
(id,name,vin,ip_address,created_at,updated_at)
values
(0,'default', 0,'0.0.0.0/0'::inet,'2021-11-08 11:54:26.822623'::timestamp,'2021-11-08 11:54:26.822623'::timestamp),
(1,'admin',   1,'10.108.150.143'::inet,'2021-11-08 11:54:26.82885'::timestamp,'2021-11-08 11:54:26.82885'::timestamp),
(2,'V122',    122,'10.108.150.122'::inet,'2021-11-08 11:54:26.82885'::timestamp,'2021-11-08 11:54:26.82885'::timestamp),
(3,'V123',    123,'10.108.150.123'::inet,'2021-11-08 11:54:26.82885'::timestamp,'2021-11-08 11:54:26.82885'::timestamp),
(4,'V124',    124,'10.108.150.124'::inet,'2021-11-08 11:54:26.82885'::timestamp,'2021-11-08 11:54:26.82885'::timestamp),
(5,'V122',    122,'10.108.150.122'::inet,'2021-11-08 11:54:26.82885'::timestamp,'2021-11-08 11:54:26.82885'::timestamp),
(6,'V125',    122,'10.108.150.125'::inet,'2021-11-08 11:54:26.82885'::timestamp,'2021-11-08 11:54:26.82885'::timestamp);

select distinct on (ip_address) * 
from test where id<>1 
order by ip_address,created_at;
-- id |  name   | vin |   ip_address   |         created_at         |         updated_at
------+---------+-----+----------------+----------------------------+----------------------------
--  0 | default |   0 | 0.0.0.0/0      | 2021-11-08 11:54:26.822623 | 2021-11-08 11:54:26.822623
--  2 | V122    | 122 | 10.108.150.122 | 2021-11-08 11:54:26.82885  | 2021-11-08 11:54:26.82885
--  3 | V123    | 123 | 10.108.150.123 | 2021-11-08 11:54:26.82885  | 2021-11-08 11:54:26.82885
--  4 | V124    | 124 | 10.108.150.124 | 2021-11-08 11:54:26.82885  | 2021-11-08 11:54:26.82885
--  6 | V125    | 122 | 10.108.150.125 | 2021-11-08 11:54:26.82885  | 2021-11-08 11:54:26.82885
--(5 rows)

【讨论】:

distinct_on和那个finder的组合自然是不可能的。在 rails 4 中删除了指定的 finder 条件,并且 distinct_on 方法似乎没有被 rails 接受 Pull Request @engineersmnky 谢谢。我已将答案更正为使用简单的.where.select - 我最初想建议在作者已经使用的内容中添加distinct_on 或等效的select,但我最终没有验证如果组合有效。我还强调了更改仍未被接受 - 以前我听起来确实好像更改已合并。

以上是关于用于获取表的所有条目并且仅包含基于特定列的许多重复项中的第一个的自定义查询的主要内容,如果未能解决你的问题,请参考以下文章

SQL server select语句用于选择另一列的重复条目的ID

显示包含特定列的所有表 [重复]

仅包含具有特定列值的第一个条目

如何仅获取 arules 中特定列的 LHS 和 RHS 项目?

Django Query以获取特定列的所有不同值的计数[重复]

pandas删除数据行中的重复数据行基于dataframe所有列删除重复行基于特定数据列或者列的作何删除重复行删除重复行并保留重复行中的最后一行pandas删除所有重复行(不进行数据保留)