Oracle:获取多列上的前n个值的列名

时间:2014-02-18 13:32:43

标签: oracle pivot max

我的问题类似于

https://community.oracle.com/message/4418327

  

在我的查询中,我需要3个不同列的MAX值。

     

示例:列1 = 10,列2 = 20,列3 = 30>输出应该   是30.我需要这个MAX值来对它进行排序。

然而,不是实际值,而是我需要列名称,理想情况下不仅仅是最大值,而是前三个例子。

然后,所需的输出将是

ID    first    second    third
-------------------------------
1    column 3  column 2  column1

4 个答案:

答案 0 :(得分:0)

不确定是否有更好的方法可以做到这一点,但这是使用CASE语句的可能解决方案。这是我测试过的表结构:

CREATE TABLE MAX_COL(
  "ID" INT,
  COLUMN_1 INT,
  COLUMN_2 INT,
  COLUMN_3 INT
);

这是我使用的SQL:

SELECT 
  "ID",
  CASE
    WHEN COLUMN_1 > COLUMN_2 AND COLUMN_1 > COLUMN_3 THEN 'COLUMN_1'
    WHEN COLUMN_2 > COLUMN_1 AND COLUMN_2 > COLUMN_3 THEN 'COLUMN_2'
    WHEN COLUMN_3 > COLUMN_2 AND COLUMN_3 > COLUMN_1 THEN 'COLUMN_3'
    ELSE 'NONE'
  END AS "FIRST",
  CASE
    WHEN COLUMN_1 > COLUMN_2 AND COLUMN_1 < COLUMN_3 THEN 'COLUMN_1'
    WHEN COLUMN_2 > COLUMN_1 AND COLUMN_2 < COLUMN_3 THEN 'COLUMN_2'
    WHEN COLUMN_3 > COLUMN_2 AND COLUMN_3 < COLUMN_1 THEN 'COLUMN_3'
    ELSE 'NONE'
  END AS "SECOND",
  CASE
    WHEN COLUMN_1 < COLUMN_2 AND COLUMN_1 < COLUMN_3 THEN 'COLUMN_1'
    WHEN COLUMN_2 < COLUMN_1 AND COLUMN_2 < COLUMN_3 THEN 'COLUMN_2'
    WHEN COLUMN_3 < COLUMN_2 AND COLUMN_3 < COLUMN_1 THEN 'COLUMN_3'
    ELSE 'NONE'
  END AS "THIRD"
FROM 
  MAX_COL;

请注意,您需要处理一个或多个列具有相同值的情况。在这种情况下,如果发生这种情况,我只会返回'NONE',但您可能想要做其他事情。

更新

您也可以使用PLSQL函数来实现这一点,这可能更容易用于许多列。这是一个例子:

CREATE OR REPLACE
FUNCTION COLUMN_POSITION (IDVAL INT, POSITION INT) RETURN VARCHAR2 AS
  TYPE COLUMN_VALUE_TYPE IS TABLE OF NUMBER INDEX BY VARCHAR2(64);
  COLS COLUMN_VALUE_TYPE;
  COL_KEY VARCHAR2(64);
  QUERY_STR VARCHAR2(1000 CHAR) := 'SELECT COL FROM(SELECT T.*, DENSE_RANK() OVER (ORDER BY VAL DESC) AS RANK FROM (';
  COL_NAME VARCHAR2(64);
BEGIN
  COLS('COLUMN_1') := NULL;
  COLS('COLUMN_2') := NULL;
  COLS('COLUMN_3') := NULL;
  --ADD MORE COLUMN NAMES HERE...

  COL_KEY := COLS.FIRST;

  LOOP
    EXIT WHEN COL_KEY IS NULL;
    QUERY_STR := QUERY_STR || 'SELECT ' || COL_KEY || ' AS VAL, '''|| COL_KEY ||''' AS COL FROM MAX_COL WHERE ID = '|| IDVAL ||' UNION ALL ';
    COL_KEY := COLS.NEXT(COL_KEY);
  END LOOP;

  QUERY_STR := SUBSTR(QUERY_STR, 0, LENGTH(QUERY_STR) - 11);
  QUERY_STR := QUERY_STR || ') T ) WHERE RANK = ' || POSITION;

  EXECUTE IMMEDIATE QUERY_STR INTO COL_NAME; 

  RETURN COL_NAME;
END;

此示例只有3列,但您可以轻松添加更多列。然后,您可以在这样的查询中使用它:

SELECT 
  MAX_COL.*, 
  COLUMN_POSITION(ID, 1) AS "FIRST",
  COLUMN_POSITION(ID, 2) AS "SECOND", 
  COLUMN_POSITION(ID, 3) AS "THIRD" 
FROM MAX_COL

答案 1 :(得分:0)

您所询问的内容没有内置功能。因此user1578653的答案是好的,直截了当的,快速的。另一种方法是用PL / SQL编写一个函数。

如果您想使用纯SQL,但希望更容易将列添加到比较中,那么如果您只对两列(id和列名称字符串)感到满意,则可以执行以下操作:

select id, listagg(colname, ', ') within group (order by value desc) as columns
from
(
  select id, 'column1' as colname, col1 as value from mytable
  union all
  select id, 'column2' as colname, col2 as value from mytable
  union all
  select id, 'column3' as colname, col3 as value from mytable
  -- add more columns here, if you wish
)
group by id;

请注意,这比user1578653的SQL语句慢,因为该表正在被读取三次。 (我在这里也区别对待相同的值,相同的值导致随机顺序。)

答案 2 :(得分:0)

您可以使用PIVOT/UNPIVOT

Fisrt UNPIVOT你的表并使用降序排列的值分配排名。

create table sample(
  id number,
  col1 number,
  col2 number,
  col3 number
  );

insert into sample values(1,10,20,30);
insert into sample values(2,10,20,15);

select id, col_name, val,
       row_number() over (partition by id order by val desc) r
from sample
unpivot(val for col_name in (
        col1 AS 'col1', col2 AS 'col2', col3 AS 'col3')
       );

输出:

| ID | COL_NAME | VAL | R |
|----|----------|-----|---|
|  1 |     col3 |  30 | 1 |
|  1 |     col2 |  20 | 2 |
|  1 |     col1 |  10 | 3 |
|  2 |     col2 |  20 | 1 |
|  2 |     col3 |  15 | 2 |
|  2 |     col1 |  10 | 3 |

sqlfiddle

Next PIVOT基于排名列的col_name。

with x(id, col_name, val,r) as (
  select id, col_name, val,
         row_number() over (partition by id order by val desc)
  from sample
  unpivot(val for col_name in (
          col1 AS 'col1', col2 AS 'col2', col3 AS 'col3')
         )
  )
select * from (
  select id, col_name, r
  from x
  )
pivot(max(col_name) for r in (
  1 as first, 2 as second, 3 as third)
      );

输出:

| ID | FIRST | SECOND | THIRD |
|----|-------|--------|-------|
|  1 |  col3 |   col2 |  col1 |
|  2 |  col2 |   col3 |  col1 |

sqlfiddle

答案 3 :(得分:0)

选择   (案件     当A&gt; B和A&gt; C那么&#39; A&#39;     当B> A AND B&gt; C那么&#39; B&#39;     当C&gt; B和C&gt;那么&#39; C&#39;   END)AS&#34; MaxValue_Column_Name&#34;, 最大(A,B,C)AS Max_Value FROM max_search;

- 表名= max_search和column_name是A,B和C