2015-05-29 3 views
5

У меня есть таблица с данными примера, как показано ниже.SQL - получить индекс столбца с максимальным значением

col1 col2 col3 
4  6  9 
7  1  5 

Я хочу, чтобы получить индекс столбца, который имеет значение соответствует максимальному значению в этой строке и если они равны, просто игнорирует позже.

Например, результат должен быть вернуться

3 (because col3 has maximum value 9) 
1 (because col1 has maximum value 7) 

Пожалуйста, обратите внимание, что число столбцов не определено, поэтому мне нужно общее решение.

Спасибо

+0

Если они равны, что вы хотите? –

+0

Если они равны, просто игнорируйте их позже. – YukiSakura

ответ

4

A (т.е. N столбцов), чтобы это сделать, чтобы Unpivot столбцов в строки, а затем можно использовать функцию оконного вызова, чтобы получить групповой мудрый максимум для каждого набора строк столбца. Тем не менее вам понадобится какой-то ключ для каждой строки, так что максимум можно применить по-разному (чтобы разрешить сборку исходных строк). Я сделал это, добавив суррогат Guid через newId(). Обратите внимание, это возвращает столбец NAME с наивысшим значением в каждой строке:

WITH MyTableWithRowId AS 
(
    SELECT newId() AS Id, * 
    FROM MyTable 
), 
Unpivoted AS 
(
    SELECT Ndx, Id, col, ROW_NUMBER() OVER (PARTITION BY Id ORDER BY col DESC) AS Rnk 
    FROM 
    MyTableWithRowId tbl 
    UNPIVOT 
    (
     col for Ndx in(col1, col2, col3) 
    ) p 
) 
SELECT Ndx 
FROM Unpivoted 
WHERE Rnk = 1 

SqlFiddle here

Edit, повторно просто '1, 2, 3' не имя столбца (col1, col2, col3)

согласно @ комментарий Джорджи, если вы действительно хотите (один на основе) порядковый номер столбца в каждой строке, вы можете присоединиться обратно в DMV, таких как INFORMATION_SCHEMA.COLUMNS смотреть вверх порядковое, хотя это было бы ужасно хрупкой стратегии ИМО.

WITH MyTableWithRowId AS 
(
    SELECT newId() AS Id, col1, col2, col3 
    FROM MyTable 
), 
TheOrdinalPositionOfColumns AS 
(
    SELECT COLUMN_NAME, ORDINAL_POSITION 
    FROM INFORMATION_SCHEMA.COLUMNS 
    WHERE TABLE_NAME = 'MyTable' 
), 
Unpivoted AS 
(
    SELECT Ndx, Id, col, ROW_NUMBER() OVER (PARTITION BY Id ORDER BY col DESC) AS Rnk 
    FROM 
    MyTableWithRowId tbl 
    UNPIVOT 
    (
     col for Ndx in(col1, col2, col3) 
    ) p 
) 
SELECT topoc.ORDINAL_POSITION AS ColumnOrdinalPosition 
FROM Unpivoted 
JOIN TheOrdinalPositionOfColumns topoc ON Unpivoted.Ndx = topoc.COLUMN_NAME 
WHERE Rnk = 1; 

Updated Fiddle with Giorgi's Column naming

+1

Это не будет работать, если имена cols:' SomeCol OtherCol WhatCol'. http://sqlfiddle.com/#!6/4b7bc/1 –

+0

Ваш ответ выглядит хорошо. Однако мне нужен индекс (1 вместо col1). Не могли бы вы изменить свой ответ? – YukiSakura

+0

@YukiSakura У меня есть - второй запрос вернет порядковый номер столбца. Вам нужно будет защитить от изменений в DDL при создании таблицы и предотвратить модификации DDL в структуре столбцов. – StuartLC

3

Try что-то вроде этого

select 
case when col1 >= col2 and col1 >= col3 then 1 
    when col2 >= col1 and col2 >= col3 then 2 
    else 3 end as [index] 
from myquestion_table 

См DEMO HERE

+1

Я думаю, что с последним комментарием OP «Если они равны, просто игнорируйте позже», вы должны изменить '>' на '> =', чтобы выбрать первый столбец. –

+0

@GiorgiNakeuri, Спасибо, что указали это; BTW, когда OP прокомментировал ... не видел этого. – Rahul

+0

В его вопрос. –

4

Вы можете сделать это следующим образом:

select case 
      when col1 >= col2 and col1 >= col3 then 1 
      when col2 >= col1 and col2 >= col3 then 2 
      when col3 >= col1 and col3 >= col2 then 3 
     end as ColIndex 
from table 
+0

Вам не нужно иметь третий 'когда' вместо этого пишите' else' :) –

+1

Да, но так, все ответы будут одинаковыми :) Mine отличается! –

+0

Мне нравится быть другим! ;) +1 –

2

Попробуйте это:

select case 
    when col1 >= col2 and col1 >= col3 then 1 
    when col2 >= col1 and col2 >= col3 then 2 
    else 3 
    end as ind 
from mytable 
2

Это очень простой пример, но это было бы что-то вроде: более общее решение

select case when col1 > col2 and col1 > col3 then col1 
     when col2> col3 then col2 
     else col3 end as greatestColumn 
    from table 
2

Попробуйте это, без поворота.

- Вы можете добавить N количество столбцов.

CREATE TABLE Table1 
    ([id] int primary key identity(1,1),[col1] int, [col2] int, [col3] int) 
; 

INSERT INTO Table1 
    ([col1], [col2], [col3]) 
VALUES 
    (4, 6, 9), 
    (7, 1, 5) 
; 

DECLARE @tempTable as table(name varchar(50),maxValue int) 


DECLARE @maxColumn int 
SELECT @maxColumn = max(ordinal_position) 
FROM INFORMATION_SCHEMA.COLUMNS 
WHERE TABLE_NAME = N'Table1' 

DECLARE @maxRow int 
SELECT @maxRow = Count(col1) FROM Table1 


DECLARE @rowCounter int = 1 
DECLARE @colCounter int = 1 

DECLARE @columnName varchar(max) 
DECLARE @colValue varchar(max) 
DECLARE @q nvarchar(max) 


DECLARE @maxValue int 
DECLARE @ParmDefinition nvarchar(500) 
DECLARE @FinalResult table (id int, columnName nvarchar(max)) 
DECLARE @rowId int 

WHILE(@rowCounter <= @maxRow) 
BEGIN 
    WHILE (@colCounter <= @maxColumn) 
    BEGIN 

     SELECT @columnName = COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS 
     WHERE TABLE_NAME = N'Table1' and ordinal_position = @colCounter 

     --select @columnName,@rowCounter,@colCounter 

     SELECT @q = 'select @retvalOUT =' + @columnName + ' from Table1 where id = ' + cast(@rowCounter as NVARCHAR) 

     SET @ParmDefinition = N'@retvalOUT int OUTPUT'; 

     EXEC sp_executesql @q,@ParmDefinition ,@retvalOUT = @maxValue OUT 
     --select '@maxValue' + @maxValue 

     INSERT INTO @tempTable VALUES (@columnName,@maxValue) 

     SET @colCounter = @colCounter + 1 
    END 



SELECT @rowId = maxValue FROM @tempTable WHERE name LIKE 'id' -- Primary key column 

INSERT INTO @FinalResult(id,columnName) 
SELECT TOP 1 @id,name FROM @tempTable WHERE name not like 'id' ORDER BY maxvalue DESC 

DELETE FROM @tempTable 

--select * from @FinalResult 

SET @colCounter = 1 
SET @rowCounter = @rowCounter + 1 
END 

SELECT * FROM @FinalResult 
2

Вот еще один стержень решение, это немного короче, чем другие решения поворота:

DECLARE @t table 
(col1 int, col2 int, col3 int) 
INSERT @t 
SELECT 4,8,9 union all SELECT 7,1,5 

;WITH addrownumber AS 
(
    SELECT 
    rn = row_number() over (order by (select 1)), 
    * 
    FROM @t 
) 
, unpiv AS 
(
    SELECT 
    rn, 
    value, 
    colname, 
    ordinalposition = row_number() over (partition by rn order by (select 1)), 
    rn2 = row_number() over (partition by rn order by value DESC, rn) 
    FROM addrownumber as p 
    UNPIVOT 
    (value FOR colname IN   
    ([col1], [col2], [col3])) AS unpvt 
    -- since you need all columns to be mentioned in pivot, you can set up 
    -- the ordinal order here, by putting in columns in the right order. 
) 
SELECT ordinalposition, value, colname 
FROM unpiv 
WHERE rn2 = 1 

Результат:

ordinalposition value colname 
3    9  col3 
1    7  col1