【问题标题】:How to get distinct sets of rows using SQL (Oracle)?如何使用 SQL (Oracle) 获取不同的行集?
【发布时间】:2016-01-04 17:17:17
【问题描述】:

我有一种情况,我需要在现有应用程序中处理 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)

但是根据之前是否看过每个特定行,组的成员会有不同的排名。

换句话说,是否有可能获得不同的“组”行,使用“组”列消除具有相同行数和相同值的其他行,其中不同的行数组可能不同?

编辑:

我不确定原来的例子是不是一个很好的例子。使用 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 组相同。我仍然需要保留有关其他组的所有信息,包括它们被分组的事实。

【问题讨论】:

  • 如果您的组是另一个组的子集怎么办?例如。 (3, A, 1, 7) 和 (3, A, 1, 8) (3, B, boo, 8) ?在这种情况下应该显示哪些行?
  • @Boneist -- 应该显示所有这些组。我只想删除帐户、字段和值的数量和条目完全相同的组。 group 的具体值不重要,只有相同组号的行组成一个组。

标签: sql oracle


【解决方案1】:

基于 cmets 的新答案:

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() 技巧,您可以包含您想要的任何其他列,而不用担心它们是否是不同分区的一部分。

【讨论】:

  • 前两个示例不起作用,因为它们单独处理每一行,我需要考虑成组的行。第三个示例也不起作用,因为 row_number() 不考虑分组。如果第一个分组有任何字段和值与第一个相同,它将有一个 row_number() > 1,并被删除。我需要保持整个组的完整性。
  • 不知道保持整个组完整是什么意思。您必须更严格地指定您的要求
  • 以上,我的意思是说第二个分组是否有任何帐户、字段和值与第一个相同。例如上述第 7 组和第 8 组中的 (3, A, 1)。我需要保留这两行,因为它们位于不同的组中,并且在考虑所有元素时这些组并不相等。即 (3, C, bar) 和 (3, C, baz) 不相等。
  • @A.Hill - 您只想排除所有该组的元组(帐户、字段、值)都存在于编号较低的组中的组?如果任何组的元组不存在,它的所有行都保留吗?
  • 这也行不通。虽然更接近,但使用计数来确定重复项是不可靠的,因为如果存在包含之前在其他单独组中看到的值的组,则prior_group 的值将是不同的,但不为空,并且计数将是相同,从而删除有效条目。您之前提到使用 row_number() 给了我一个想法,我已经解决了这个问题。我已经相应地更新了问题。
【解决方案2】:

如果我正确理解了这个问题,这就是你要找的。​​p>

select distinct account, field, value from table

【讨论】:

  • 这不是我要找的。这将给出不同的行。我需要不同的行组,基于组列形成的组。这将区分单行。
【解决方案3】:

也许我遗漏了一些东西,但这似乎是一个简单的 group by 和 min 聚合。假设您总是希望在存在重复帐户、字段和值时返回最小组 #。

SELECT account, field, value, min(group) as group
FROM  table
GROUP BY account, field, value

【讨论】:

  • 这行不通。我不确定我原来的例子是否足够清楚。我添加了一个更详细的示例。
【解决方案4】:

我已经使用@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);

【讨论】:

    【解决方案5】:

    类似的方法,但使用 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";
    

    【讨论】:

      猜你喜欢
      • 2020-05-23
      • 1970-01-01
      • 2018-10-30
      • 2014-12-22
      • 1970-01-01
      • 1970-01-01
      • 2015-07-31
      • 1970-01-01
      • 2021-11-17
      相关资源
      最近更新 更多