简体   繁体   中英

What is the simplest way to get the least value and second least value from multiple columns (MIN function does not work this)?

Below table is from Teradata database

SELECT
  sku.Item_id,
  sku.Item_length,
  sku.Item_width,
  sku.Item_heigth,
FROM  Category_item sku

Item_id | Item_length | Item_width |Item_heigth
-------------------------------------------
104174        8            6           1
9482763       8            8           8
8434610       8            9           1
2109145       54           34          2
567106        41           41          2
2028731       3            4           3

The final result that I want is,

 Item_id | Item_length | Item_width |Item_heigth | MinValue | SecondMinVale
    ---------------------------------------------------------------------------
    104174        8            6           1          1            6
    9482763       8            8           8          8            8
    8434610       8            9           1          1            8
    2109145       54           34          2          2            34
    567106        41           41          2          2            41
    2028731       3            4           3          3            3

So I did a little research online, most of them used MIN function with a subquery or raw over by partition. Min function does not work for me, because I have three separate columns. I want to get minimum or least two values from the above three columns.

I tried to do subquery or CTE using Least function, but I am stuck with getting the second least value. I am not sure how to use raw over partition for this.

Any help regarding this will be highly appreciated.

Thank you so much!!

You can do it this way:

this below:

SELECT MIN(Item_caract)
      FROM (VALUES (Item_length),(Item_width),(Item_heigth)) a(Item_caract)

give you the minimum of all columns

and this:

(SELECT MIN(Item_caract)
      FROM (VALUES (Item_length),(Item_width),(Item_heigth)) a(Item_caract) where Item_caract >=
      (SELECT MIN(Item_caract)
      FROM (VALUES (Item_length),(Item_width),(Item_heigth)) a(Item_caract))

gives you the minimum that is bigger than the minimum above

Below is the full query:

select * ,

(SELECT MIN(Item_caract)
      FROM (VALUES (Item_length),(Item_width),(Item_heigth)) a(Item_caract))
   AS MinValue ,

(SELECT MIN(Item_caract)
      FROM (VALUES (Item_length),(Item_width),(Item_heigth)) a(Item_caract) where Item_caract >=
      (SELECT MIN(Item_caract)
      FROM (VALUES (Item_length),(Item_width),(Item_heigth)) a(Item_caract))
      )
   AS SecondMinVale 
from cte

Output:

104174  8   6   1   1   1
567106  41  41  2   2   2
2028731 3   4   3   3   3
2109145 54  34  2   2   2
8434610 8   9   1   1   1
9482763 8   8   8   8   8

Without set based analytic functions, your requirement is difficult. I might suggest unpivoting your data:

WITH cte AS (
    SELECT Item_id, Item_length AS item FROM Category_item UNION ALL
    SELECT Item_id, Item_width FROM Category_item UNION ALL
    SELECT Item_id, Item_height FROM Category_item
),
cte2 AS (
    SELECT Item_id, item,
        ROW_NUMBER() OVER (PARTITION BY Item_id ORDER BY item) rn
    FROM cte
)

SELECT
    t1.Item_id,
    t1.Item_length,
    t1.Item_width,
    t1.Item_height,
    MAX(CASE WHEN rn = 1 THEN t2.item END) AS MinValue,
    MAX(CASE WHEN rn = 2 THEN t2.item END) AS SecondMinValue
FROM Category_item t1
INNER JOIN cte2 t2
    ON t1.Item_id = t2.Item_id
WHERE
    t2.rn <= 2
GROUP BY
    t1.Item_id,
    t1.Item_length,
    t1.Item_width,
    t1.Item_height;

Note that the need to use such unpivoting operations might seriously imply that your table should be redesigned with the various measurements being stored per item across rows , rather than across columns.

If you actually got three columns you can simply apply this brute-force logic:

SELECT sku.*,
   Least(Item_length,Item_width,Item_height) AS MinValue,
   CASE
     WHEN Item_length BETWEEN Least(Item_width, Item_height) AND 
                           Greatest(Item_width, Item_height) 
     THEN Item_length
     WHEN Item_width BETWEEN Least(Item_length, Item_height) AND 
                           Greatest(Item_length, Item_height) 
     THEN Item_width
     WHEN Item_height BETWEEN Least(Item_width, Item_length) AND 
                           Greatest(Item_width, Item_length)
     THEN Item_height
   END AS SecondMinValue
FROM  Category_item sku

For more than three columns you need Tim's approach, this is a slight variation (if your TD release supports UNPIVOT) avoiding the join-back:

WITH cte AS 
 (
   SELECT up.*
     ,Row_Number() Over (PARTITION BY item_id ORDER BY val) AS rn
   FROM Category_item
   UNPIVOT
    ( val 
      FOR measure
      IN (Item_length AS 'l'
         ,Item_width  AS 'w'
         ,Item_height AS 'h') 
    ) AS up
 )  
SELECT
    Item_id,
    Max(CASE WHEN measure = 'l' THEN val END) AS item_length,
    Max(CASE WHEN measure = 'w' THEN val END) AS item_width,
    Max(CASE WHEN measure = 'h' THEN val END) AS item_height,
    Max(CASE WHEN rn = 1 THEN val END) AS MINVALUE,
    Max(CASE WHEN rn = 2 THEN val END) AS SecondMinValue
FROM cte
GROUP BY
    Item_id

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM