在 2 个字段上应用 distinct 并获取每列的唯一数据

Posted

技术标签:

【中文标题】在 2 个字段上应用 distinct 并获取每列的唯一数据【英文标题】:Apply the distinct on 2 fields and also fetch the unique data for each columns 【发布时间】:2013-11-26 09:53:31 【问题描述】:

根据一些奇怪的要求,我需要选择两列中的所有输出值都应该是唯一的记录。

输入如下所示:

col1   col2
1       x
1       y
2       x
2       y
3       x
3       y
3       z

预期输出是:

col1  col2
1     x
2     y
3     z

col1  col2
1     y
2     x
3     z

我尝试在 2 个字段上应用 distinct,但返回的所有记录总体上它们在两个字段上都是不同的。我们要做的是,如果 col1 中存在任何值,那么它不能在 col2 中重复。

请让我知道这是否可行,如果可行,请告诉我如何去做。

【问题讨论】:

表中还有更多列,但它们与此无关,因此暂时忽略它们。关于我尝试过的内容:我尝试使用带有 group by 的 min、max 函数来查看我是否可以识别唯一的行,但它没有用。另外,为了使要求更清楚,这里是另一个示例 输入:col1 col2 1 x 1 y 2 x 输出:col1 col2 1 x 不确定这是否可能,因为您想要不确定的行为 您需要单个 SQL 语句还是任何解决方案,包括 PLSQL? 这里的实际“顺序”是什么?为什么2 x 不是最后一个示例的选项? 因为 x 已经被选择为 1... 他希望每个值只出现一次,无论在哪个组合中 【参考方案1】:

大问题! Armunin 在这里找到了更深层次的结构问题,这是一个递归可枚举问题描述,只能通过递归解决方案来解决 - 基本关系运算符(join/union/etc)不会让你到达那里。正如 Armunin 所引用的,一种方法是使用 PL/SQL,虽然我没有详细检查过,但我认为 PL/SQL 代码可以正常工作。但是,Oracle 还是很友好地支持递归 SQL,通过它我们可以只用 SQL 构建解决方案:

-- 注意 - 此 SQL 将生成每个解决方案 - 您需要在最后过滤 SOLUTION_NUMBER=1

with t as (
select 1 col1, 'x' col2 from dual union all
select 1 col1, 'y' col2 from dual union all
select 2 col1, 'x' col2 from dual union all
select 2 col1, 'y' col2 from dual union all
select 3 col1, 'x' col2 from dual union all
select 3 col1, 'y' col2 from dual union all
select 3 col1, 'z' col2 from dual
), 
t0 as 
    (select t.*, 
            row_number() over (order by col1) id, 
            dense_rank() over (order by col2) c2_rnk 
     from t),
-- recursive step...
t1 (c2_rnk,ids, str) as
    (-- base row
     select c2_rnk, '('||id||')' ids, '('||col1||')' str 
     from   t0 
     where  c2_rnk=1
     union all
     -- induction
     select t0.c2_rnk, ids||'('||t0.id||')' ids, str||','||'('||t0.col1||')' 
     from   t1, t0 
     where  t0.c2_rnk = t1.c2_rnk+1 
            and instr(t1.str,'('||t0.col1||')') =0
    ),
t2 as 
    (select t1.*, 
            rownum solution_number 
     from   t1 
     where  c2_rnk = (select max(c2_rnk) from t1)
    )
select  solution_number, col1, col2 
from    t0, t2 
where   instr(t2.ids,'('||t0.id||')') <> 0
order by 1,2,3


SOLUTION_NUMBER       COL1    COL2 
1                     1       x    
1                     2       y    
1                     3       z    
2                     1       y    
2                     2       x    
2                     3       z    

【讨论】:

【参考方案2】:

您可以使用完全外连接将两个编号列表合并在一起:

SELECT  col1, col2
FROM  ( SELECT col1, ROW_NUMBER() OVER ( ORDER BY col1 ) col1_num
        FROM   your_table
        GROUP BY col1 )
  FULL JOIN 
      ( SELECT col2, ROW_NUMBER() OVER ( ORDER BY col2 ) col2_num
        FROM   your_table
        GROUP BY col2 )
  ON  col1_num = col2_num

如果您需要不同的订单,请更改 ORDER BY,如果您愿意让 Oracle 决定,请使用 ORDER BY NULL。

【讨论】:

作为 OP 需要如果 col1 col2 1 x 1 y 2 x 1 xx o/p: col1 col2 1 x 它不适用于所有输入,如here 所示。尽管这不是一个可行的解决方案,但您会得到 3 x 作为输出。不确定这是否是 OP 问题的复杂输入 @KevanGelling 是的,我知道,这只是我为测试所做的一些更改。不知道为什么他们还在那里。 FULL Join 不会改变任何东西。当您比较 row_numbers 时,是 ORDERing 混淆了结果。 SQL Fiddle 用于与 FULL JOINORDER BY NULL 进行比较。这次给出了 2 x 作为可行的解决方案。 @Armunin - 您可以尝试“ORDER BY MIN(col1), col2”,但我认为 Ramblin'Man 需要更清楚地说明所需的顺序 @KevanGelling 我不确定他是否会得到他想要的结果,因为他要求的行为是不确定的,所以每次他更改任何建议的解决方案的ORDER BY 时,他都会得到不同的结果。根据他的输入数据,他会错过输出中的一个或另一个数据集。【参考方案3】:

如果再有一行会是什么结果 col1 值为 1 和 col2 值为 xx ?

在这种情况下单行更好:

SELECT DISTINCT TO_CHAR(col1) FROM your_table
UNION ALL
SELECT DISTINCT col2 FROM your_table;

【讨论】:

除非我很确定 OP 希望将其作为两列。 如果任何值已经存在于 o/p 中,那么它们应该被过滤掉。即使那样,也不应该选择该记录,因为已经在第一条记录中选择了 1。 i/p: col1 col2 1 x 1 y 2 x 1 xx o/p: col1 col2 1 x【参考方案4】:

我的建议是这样的:

begin
    EXECUTE IMMEDIATE 'CREATE global TEMPORARY TABLE tmp(col1 NUMBER, col2 VARCHAR2(50))';
end;
/
DECLARE
    cur_print sys_refcursor;
    col1 NUMBER;
    col2 VARCHAR(50);
    CURSOR cur_dist
    IS
        SELECT DISTINCT
            col1
        FROM
            ttable;
    filtered sys_refcursor;
BEGIN
    FOR rec IN cur_dist
    LOOP
        INSERT INTO tmp
        SELECT
            col1,
            col2
        FROM
            ttable t1
        WHERE
            t1.col1         = rec.col1
        AND t1.col2 NOT IN
            (
                SELECT
                    tmp.col2
                FROM
                    tmp
            )
        AND t1.col1 NOT IN
            (
                SELECT
                    tmp.col1
                FROM
                    tmp
            )
        AND ROWNUM = 1;
    END LOOP;

    FOR rec in (select col1, col2 from tmp) LOOP
        DBMS_OUTPUT.PUT_LINE('col1: ' || rec.col1 || '|| col2: ' || rec.col2);
    END LOOP;

    EXECUTE IMMEDIATE 'DROP TABLE tmp';
END;
/

可能还需要一些改进,我对ROWNUM = 1 部分特别不满意。

【讨论】:

【参考方案5】:

SQL Fiddle

Oracle 11g R2 架构设置

CREATE TABLE tbl ( col1, col2 ) AS
          SELECT 1, 'x' FROM DUAL
UNION ALL SELECT 1, 'y' FROM DUAL
UNION ALL SELECT 2, 'x' FROM DUAL
UNION ALL SELECT 2, 'y' FROM DUAL
UNION ALL SELECT 3, 'x' FROM DUAL
UNION ALL SELECT 3, 'y' FROM DUAL
UNION ALL SELECT 4, 'z' FROM DUAL;

查询 1

WITH c1 AS (
  SELECT  DISTINCT
          col1,
          DENSE_RANK() OVER (ORDER BY col1) AS rank
  FROM    tbl
),
c2 AS (
  SELECT  DISTINCT
          col2,
          DENSE_RANK() OVER (ORDER BY col2) AS rank
  FROM    tbl
)
SELECT c1.col1,
       c2.col2
FROM   c1
       FULL OUTER JOIN c2
       ON ( c1.rank = c2.rank)
ORDER BY COALESCE( c1.rank, c2.rank)

Results

| COL1 |   COL2 |
|------|--------|
|    1 |      x |
|    2 |      y |
|    3 |      z |
|    4 | (null) |

并解决附加要求:

我们要做的是,如果 col1 中存在任何值,那么它不能在 col2 中重复。

查询 2

WITH c1 AS (
  SELECT  DISTINCT
          col1,
          DENSE_RANK() OVER (ORDER BY col1) AS rank
  FROM    tbl
),
c2 AS (
  SELECT  DISTINCT
          col2,
          DENSE_RANK() OVER (ORDER BY col2) AS rank
  FROM    tbl
  WHERE   col2 NOT IN ( SELECT TO_CHAR( col1 ) FROM c1 )
)
SELECT c1.col1,
       c2.col2
FROM   c1
       FULL OUTER JOIN c2
       ON ( c1.rank = c2.rank)
ORDER BY COALESCE( c1.rank, c2.rank)

【讨论】:

首先,感谢您辛苦编写sqls。关于您的问题,如果 col2 中存在的值不能重复,那么我们可以过滤该记录,并且不应在选择查询中选择它们。 查询1将选择col1中的不同项和col2中的所有不同项;查询 2 进一步限制了这一点,使得 col1col2 中的所有不同项目都被选中,如果 col1col2 中都有一个项目,那么它只会出现在 col1 中。

以上是关于在 2 个字段上应用 distinct 并获取每列的唯一数据的主要内容,如果未能解决你的问题,请参考以下文章

Drupal 6 & Views 2 - DISTINCT 字段

如何在特定字段 MongoDB 上查找 Distinct No of Document 并根据条件打印文档数?

Cakephp 2.x 在同一字段上查找同时具有 DISTINCT 和 COUNT 的查询

spark 例子count(distinct 字段)

Mongo 查询(可视化工具)

SQLServer中如何获取没有重复的记录,记录中字段有text,或image数据类型