我有users
locations
和一个联接表visits
。每个用户可以访问多个位置,我希望按用户最近一次访问的位置名称对用户进行排序。
CREATE TABLE users
("id" int, "name" varchar(128))
;
CREATE TABLE locations
("id" int, "name" varchar(128))
;
CREATE TABLE visits
("id" int, "user_id" int, "location_id" int, "date" timestamp)
;
INSERT INTO users
("id", "name")
VALUES
(1, 'Alpha'),
(2, 'Bravo'),
(3, 'Charlie');
INSERT INTO locations
("id", "name")
VALUES
(4, 'Delta'),
(5, 'Echo'),
(6, 'Foxtrott');
INSERT INTO visits
("id", "user_id", "location_id", "date")
VALUES
(1, 1, 4, '2000-02-03 00:00:00'),
(2, 1, 5, '2000-02-02 00:00:00'),
(3, 1, 6, '2000-02-01 00:00:00'),
(4, 2, 6, '2000-01-01 00:00:00'),
(5, 2, 5, '2000-01-01 00:00:00')
;
我尝试过
SELECT users.id, users.name, max(locations.name) as location_name, max(visits.date) as date
FROM users
LEFT JOIN visits ON users.id = visits.user_id
LEFT JOIN locations ON visits.location_id = locations.id
GROUP BY users.id, users.name
ORDER BY location_name
但是max(locations.name)不依赖于max(visits.date)
另一种尝试是
SELECT users.id, users.name, t.date, locations.name as location_name
FROM users
LEFT JOIN (
SELECT MAX(date) as date, user_id
FROM visits
GROUP BY user_id
) AS t ON users.id = t.user_id
LEFT JOIN visits on visits.date = t.date
LEFT JOIN locations on locations.id = visits.location_id
ORDER BY location_name
但是当一个用户在同一日期有两次访问时,这会产生问题(我不在乎选择哪个位置,但只能是一个)
结果看起来应该像这样,尽管不需要日期。
id name date location_name
1 Alpha 2000-02-03T00:00:00Z Delta
2 Bravo 2000-01-01T00:00:00Z Echo
3 Charlie (null) (null)
该解决方案最好是在ActiveRecord中,但纯SQL也可以
答案 0 :(得分:1)
下面的查询将产生所需的结果。
我已经在CTE中填充了您的示例数据。
也不需要分组和汇总函数,例如max。这里不需要。
with users(id,name) as ( select * from ( values (1, 'Alpha'), (2, 'Bravo'), (3, 'Charlie') ) t ), locations(id,name) as ( select * from( values (4, 'Delta'), (5, 'Echo'), (6, 'Foxtrott') ) t ), visits(id,user_id,location_id,date) as ( select * from( VALUES (1, 1, 4, '2000-02-03 00:00:00'), (2, 1, 5, '2000-02-02 00:00:00'), (3, 1, 6, '2000-02-01 00:00:00'), (4, 2, 6, '2000-01-01 00:00:00'), (5, 2, 5, '2000-01-01 00:00:00') ) t ), res as ( select distinct on(user_id) u.id as user_id, u.name, l.name as location_name, date from visits v join locations l on l.id=location_id right join users u on u.id=v.user_id order by user_id,date desc ) select * from res order by location_name
答案 1 :(得分:0)
对分组的小修正:
SELECT users.id, users.name, max(locations.name) as location_name, visits.date as date
FROM users
LEFT JOIN visits ON users.id = visits.user_id
LEFT JOIN locations ON visits.location_id = locations.id
where visits.date = (select max(date) from visits where user_id = users.id)
GROUP BY users.id, users.name,visits.date
ORDER BY location_name
您可以在此处验证其是否提供了正确的结果:http://sqlfiddle.com/#!17/24a0d/27
答案 2 :(得分:0)
干得好@Sabari:
我刚刚编写了一个类似的查询,来到这里看到您已经发布了它。这与仅使用LEFT JOIN
的查询基本相同。我还是保留它。
SELECT DISTINCT ON (user_id) u.id
,u.name
,l.name
,DATE
FROM users u
LEFT JOIN visits v ON u.id = v.user_id
LEFT JOIN locations l ON l.id = location_id
ORDER BY user_id
,DATE DESC