在大多数情况下,我尽量不在我的应用程序中编写自定义 SQL 查询,但我遇到了一个案例,我想知道这是否是我最好这样做的案例。
我在这个特定的应用程序中使用 PostgreSQL。我只想返回已被重复预订任务的员工,这是我的模型。
我有以下模型。
用户
has_many :user_jobs
Fields
- id
- name
- address
- phone
工作
has_many :user_jobs
has_many :users, through: :user_jobs
Fields
- id
- date
- start_time
- end_time
用户工作
belongs_to :user
belongs_to :jobs
Fields
- id
- job_id
- user_id
Userjobs 是保存该工作的工作和雇员的表,但每个工作的日期和时间都保存在 Job 表中
我想返回类似的东西
user(employee) - 日期和时间 job1 - 日期和时间 job1
编辑:添加了更多架构细节
CREATE TABLE user_jobs (
id integer NOT NULL,
job_id integer,
job_date date,
notes text,
job_rating integer,
notes text,
created_at timestamp without time zone,
updated_at timestamp without time zone,
user_id integer,
);
CREATE TABLE jobs (
id integer NOT NULL,
date date,
start_time time without time zone,
end_time time without time zone,
notes text,
);
CREATE TABLE users (
id integer NOT NULL,
email character varying(255) DEFAULT ''::character varying NOT NULL,
name character varying(255),
address character varying(255),
phone character varying(255),
picture character varying(255),
status character varying(255) DEFAULT 'active'::character varying,
);
提前致谢
最佳答案
Postgres 9.2+
这是一个粗糙的查询:
WITH alljobs AS(
SELECT * FROM jobs j INNER JOIN user_jobs uj ON uj.job_id = j.id
)
SELECT DISTINCT q1.user_id
FROM alljobs q1
JOIN alljobs q2 on
q1.user_id = q2.user_id
AND tsrange(q1.date + q1.start_time, q1.date + q1.end_time) && tsrange(q2.date + q2.start_time, q2.date + q2.end_time)
解释:
-
WITH alljobs
有效地分配变量名alljobs
到给定的查询。该查询只是所有具有开始和结束时间的工作分配的联合列表。 -
SELECT DISTINCT q1.user_id
仅返回重复预订的用户 ID。这在技术上是您所要求的,但您可能希望扩展此选择以获取更多有用的信息。我建议使用SELECT *
在调试时。 -
FROM alljobs q1 JOIN alljobs q2
这会将作业与自身相结合,这是将每个作业与其他作业进行比较所必需的。 -
q1.user_id = q2.user_id
我们只关心单个用户的碰撞。如果您想回答相关问题,例如“谁在一起工作?”,您可以更改此设置。 -
tsrange
一个postgres built-in range function从两个时间戳创建一个范围。日期和其他类型的时间戳也有类似的功能。 _这些范围类型仅在 9.2 中引入。 -
&&
一个postgres range operator用于交叉路口。
Postgres <9.2
你可以替换tsrange
和 &&
使用您自己的交集逻辑,我认为它看起来像:q1.start_time < q2.start_time && q1.finish_time > q2.start_time OR q2.start_time < q1.start_time && q2.finish_time > q1.start_time
. (同时添加 date
。)
或者,由于您指定的开始时间始终相同,而这正是您真正关心的,在这种情况下您可以做一些更简单的事情:
SELECT user_id, date + start_time, COUNT(*)
FROM user_jobs INNER JOIN jobs ON job_id = jobs.id
GROUP BY user_id, date + start_time
HAVING COUNT(*) > 2
这将为您提供所有具有重复的用户 ID。要获得相应的作业,您可以将其包装在外部查询中。
SELECT user_jobs.user_id, user_jobs.job_id, jobs.date + jobs.start_time
FROM user_jobs INNER JOIN jobs ON job_id = jobs.id INNER JOIN (
SELECT user_id, date + start_time, COUNT(*)
FROM user_jobs INNER JOIN jobs ON job_id = jobs.id
GROUP BY user_id, date + start_time
HAVING COUNT(*) > 2
) dups ON dups.user_id = user_jobs.user_id
AND dups.date + dups.start_time = job.date + jobs.start_time
架构建议
分开 date
会让您自己的生活变得更加困难和 time
列。为什么不直接制作 start_time
和 end_time
时间戳?这样您就不必总是将它们加在一起,您仍然可以通过强制转换来获取日期。
关于mysql - Rails 和查询 - 获取重复记录的自定义查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28031963/