我为这个令人困惑的标题道歉,我无法弄清楚这个问题的正确措辞。相反,我只会给你背景信息和目标:
这是在一个表中,一个人可能有也可能没有多行数据,这些行可能包含activity_id
的相同值,或者可能不包含。{1}}。每行都有一个自动递增的ID。人们没有附加名称的唯一标识符,因此我们只能使用first_name / last_name来标识某个人。
我需要能够找到此表中包含多行的人,但只能找到包含多个不同activity_id
的多行的人。
以下是我们正在查看的数据示例:
unique_id | first_name | last_name | activity_id
---------------------------------------------------------------
1 | ted | stevens | 544
2 | ted | stevens | 544
3 | ted | stevens | 545
4 | ted | stevens | 546
5 | rachel | jameson | 633
6 | jennifer | tyler | 644
7 | jennifer | tyler | 655
8 | jennifer | tyler | 655
9 | jack | fillion | 544
10 | mallory | taylor | 633
11 | mallory | taylor | 633
从这个小样本中,这里是我想要返回的记录:
unique_id | first_name | last_name | activity_id
---------------------------------------------------------------
dontcare | ted | stevens | 544
dontcare | jennifer | tyler | 655
请注意,返回unique_id
的哪个值是不可靠的,只要它是属于该人的unique_id
之一,并且只要为该人返回一条记录即可。
任何人都可以弄明白如何编写这样的查询吗? 我不关心您使用的SQL版本,如果它有所不同,我可以将其转换为Oracle。
答案 0 :(得分:9)
我愿意:
SELECT first_name, last_name, COUNT(DISTINCT activity_id)
FROM <table_name>
GROUP BY first_name, last_name
HAVING COUNT(DISTINCT activity_id) > 0;
答案 1 :(得分:1)
我将与你一起构建逻辑。首先,让我们找到所有拥有多个条目的人:
名称+活动ID的唯一列表:
select first_name, last_name,activity_id, count(1)
from yourtable
group by first_name, last_name,activity_id
现在我们将其转换为子查询,并查找活动数超过1的用户
Select first_name, last_name
from
(select first_name, last_name,activity_id, count(1)
from yourtable
group by first_name, last_name,activity_id) a
group by first_name, last_name
having count(1) > 1
应该这样工作...我没有返回activity_id,将max(activity_id)添加到select语句将获得最高的一个。
答案 2 :(得分:0)
请注意,返回unique_id的哪个值是irrelvant,只要它是属于该人的unique_id之一,并且只要为该人返回一条记录。
这些查询应该可以解决问题。不需要使用不同的关键字或子查询来获取BumbleShrimp需要的结果(如果BumbleShrimp需要正确的unique_id,则需要子查询来匹配正确的值)
以下是我能想到的最简单的查询应该可行,但在大型表格上可能会很慢。
SELECT
first_name
, last_name
, activity_id
FROM
person
GROUP BY
first_name
, last_name
, activity_id
HAVING COUNT(*) >= 2
可能很慢,因为解释显示“使用索引;使用临时;使用filesort”。 使用临时表可以触发基于磁盘的临时表,因此我们使用内部自联接来消除使用临时表的需要。
SELECT
person1.first_name
, person1.last_name
, person1.activity_id
FROM
person person1
INNER JOIN
person person2
ON
person1.unique_id < person2.unique_id
AND
person1.first_name = person2.first_name
AND
person1.last_name = person2.last_name
AND
person1.activity_id = person2.activity_id
ORDER BY
activity_id asc
请参阅演示http://sqlfiddle.com/#!2/fe3ba/29
请注意,如果有三个或更多重复项,则内部联接将失败 见demo http://sqlfiddle.com/#!2/1ff33/15
新查询
SELECT
first_name
, last_name
, activity_id
FROM
person
GROUP BY
activity_id
, last_name
, first_name
HAVING COUNT(activity_id) >= 2
ORDER BY
activity_id asc
请参阅demo http://sqlfiddle.com/#!2/1e418/3修复三个或更多重复问题/订单activity_id,并且可以在大型表上使用,因为不需要关闭临时表可以减慢执行速度
答案 3 :(得分:0)
要获得名称,最简单的是:
SELECT
first_name
, last_name
FROM
person
GROUP BY
first_name
, last_name
HAVING
COUNT(DISTINCT activity_id) >= 2 ;
要为每个名称获取一行,您可以使用窗口函数(在Oracle中正常工作):
WITH cte AS
( SELECT
unique_id, first_name, last_name, activity_id
, COUNT(DISTINCT activity_id) OVER (PARTITION BY last_name, first_name)
AS cnt
, MIN(unique_id) OVER (PARTITION BY last_name, first_name)
AS min_id
FROM
person
)
SELECT
unique_id, first_name, last_name, activity_id
FROM
cte
WHERE
cnt >= 2
AND
min_id = unique_id ;
您可以使用MIN(unique_id) OVER ...
(或MIN(activity_id) OVER ...
)而不是MAX()
,而不是min_id = activity_id
。或ROW_NUMBER()
功能。由于您无论如何都需要COUNT(DISTINCT activity_id)
,请允许我添加此版本。
使用(last_name, first_name, activity_id, unique_id)
上的索引,它应该非常有效:
WITH cte AS
( SELECT
unique_id, first_name, last_name, activity_id
, COUNT(DISTINCT activity_id) OVER (PARTITION BY last_name, first_name)
AS cnt
, ROW_NUMBER() OVER (PARTITION BY last_name, first_name
ORDER BY activity_id, unique_id)
AS rown
FROM
person
)
SELECT
unique_id, first_name, last_name, activity_id
FROM
cte
WHERE
cnt >= 2
AND
rown = 1 ;
进行测试