我有一种情况,我需要在现有的应用程序中使用EAV数据,其中重构不是一个选项,所以我不能改变数据的结构。也就是说,我有一个返回数据的查询
| account | field | value | group |
|---------|-------|-------|-------|
| 1 | A | 1 | 1 |
| 1 | B | foo | 1 |
| 1 | A | 2 | 2 |
| 1 | B | foo | 2 |
| 1 | A | 2 | 3 |
| 1 | B | foo | 3 |
| 2 | A | 1 | 4 |
| 2 | A | 2 | 5 |
| 2 | A | 1 | 6 |
如何根据第2组和第4组分别拥有所有相同的帐户,字段和值这一事实,如何删除第3组和第6组?
我想过使用像
这样的东西select account, field, value, rank() over (partition by account, field, value order by group)
但是该组的成员将具有不同的排名,具体取决于之前是否已经看到每个特定行。
换句话说,是否可以使用“group”列来获取行的不同“集合”,从而消除具有相同行数和相同值的其他行,其中行数不同团体可能会有所不同吗?
编辑:
我不确定最初的例子是非常好的。使用distinct / unique将不起作用,因为我对不同的行组感兴趣,而不是对不同的行感兴趣。作为一个更好的例子,考虑
| account | field | value | group |
|---------|-------|-------|-------|
| 1 | A | 1 | 1 |
| 1 | B | foo | 1 |
| 1 | A | 1 | 2 |
| 1 | B | bar | 2 |
| 1 | A | 2 | 3 |
| 1 | B | foo | 3 |
| 2 | A | 1 | 4 |
| 2 | A | 2 | 5 |
| 2 | A | 1 | 6 |
| 3 | A | 1 | 7 |
| 3 | B | foo | 7 |
| 3 | C | bar | 7 |
| 3 | A | 1 | 8 |
| 3 | B | foo | 8 |
| 3 | C | baz | 8 |
| 3 | A | 1 | 9 |
| 3 | B | foo | 9 |
| 3 | C | bar | 9 |
在这种情况下,我只想删除第6组和第9组,因为它们分别与第4组和第7组相同。我仍然需要保留关于其他组的所有信息,包括它们被分组的事实。
答案 0 :(得分:2)
基于评论的新答案:
WITH Prior AS
(
-- First find matches to Prior groups
SELECT A.account, A.field, A.value, A.group, MIN(B.group) as Prior_Group
FROM TABLE A
LEFT JOIN TABLE B ON A.account=B.account
AND A.Field = B.field
AND A.value = B.value
AND A.group > B.group
GROUP BY A.account, A.field, A.value, A.group
), Counts AS
(
-- Count group members and priors
-- Using a trick that nulls for Prior_Group won't be counted
SELECT account, field, value, group,
Count(*) AS Group_Count, Count(Prior_Group) as Prior_Count
FROM Prior
GROUP BY account, field, value, group
)
SELECT account, field, value, group
FROM TABLE
WHERE (account, field, value, group) NOT IN
(SELECT account, field, value, group
FROM Counts
WHERE Group_Count = Prior_Count)
您可以使用
SELECT DISTINCT account, field, value
FROM (
-- PRIOR QUERY
) x
或
SELECT account, field, value
FROM (
-- PRIOR QUERY
) x
GROUP BY account, field, value
最后,如果你想在不同的集合中包含“最低群体”,你可以这样做
SELECT account, field, value, group
FROM (
SELECT account, field, value, group
row_number() OVER (PARTITION BY account, field, value ORDER BY group ASC) AS rn
FROM (
-- PRIOR QUERY
) x
) x2
WHERE rn = 1
附注,使用row_number()技巧,如果它们是不同分区的一部分,您可以无需担心地包含任何其他列。
答案 1 :(得分:0)
如果我正确理解了这个问题,那就是你要找的。 p>
select distinct account, field, value from table
答案 2 :(得分:0)
也许我错过了一些东西,但这似乎是一个简单的分组和最小聚合。假设您总是希望在存在重复的帐户,字段和值时返回最小组#。
SELECT account, field, value, min(group) as group
FROM table
GROUP BY account, field, value
答案 3 :(得分:0)
我已经使用@Hogan
的部分建议解决了这个问题WITH hashes AS
(SELECT group,
SUM(Ora_hash(Concat(Concat(account,field), value))) AS hash
FROM table
GROUP BY group)
SELECT account,
field,
value,
group
FROM table
WHERE group IN (SELECT group
FROM (SELECT group,
row_number() over (PARTITION BY hash ORDER BY NULL) AS rn
FROM hashes)
WHERE rn = 1);
这样做的缺点是,如果传递给ora_hash()的字段串联长度超过varchar2字段的最大字符长度,我认为会出现问题。 Ora_hash()对于LOB字段不是确定性的,因此转换为CLOB无济于事。在我的例子中,数据库中的帐户,字段和值列的长度限制将防止这种情况发生。在其他情况下,查看dbms_crypto.hash()函数可能很有用。
编辑:
对于这类问题,ora_hash()函数似乎具有不可接受的高冲突率。考虑到我已经依赖于连接不会通过varcar2字符限制这一事实,最好直接比较这些值。这只会在两个不同字段的连接产生相同结果的情况下失败,例如, ('a','bc')和('ab','c'),而不是任何值与ora_hash()冲突的可能性。
WITH group_vals AS
(SELECT group,
listagg(account || field || value, ',') AS vals
FROM table
GROUP BY group)
SELECT account,
field,
value,
group
FROM table
WHERE group IN (SELECT group
FROM (SELECT group,
row_number() over (PARTITION BY vals ORDER BY NULL) AS rn
FROM group_vals)
WHERE rn = 1);
答案 4 :(得分:0)
类似的方法,但使用LISTAGG而不是SUM和ora_hash(如果您使用的是11g R2或更高版本):
WITH lists AS
(SELECT account, field, "value", "group", LISTAGG(account||field||"value", '-')
WITHIN GROUP (ORDER BY account, field, "value") OVER (PARTITION BY "group") AS list
FROM test
)
SELECT account, field, "value", "group"
FROM lists
WHERE "group" IN
(SELECT "group"
FROM
(SELECT "group", row_number() over (partition BY list order by "group") AS rn
FROM lists)
WHERE rn = 1
)
ORDER BY "group", account, field, "value";