3

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!!

WeldaSudha
  • 39
  • 5
  • Your requirement is actually pretty hard to swing, but it would be much easier to handle if you had the various measurements across separate _rows_, instead of columns. In that case, you could easily just use `ROW_NUMBER` to answer your question. – Tim Biegeleisen Feb 13 '20 at 04:50
  • @TimBiegeleisen Thanks for the explanation, but I am going to use the above two values in the calculation on an automated report. So I don't have much flexibility over here. – WeldaSudha Feb 13 '20 at 04:54
  • Are you certain that all three values would always be different? – Tim Biegeleisen Feb 13 '20 at 04:56
  • @TimBiegeleisen Nope. It can be all same too or two of the same. So it will not be always for different for sure. – WeldaSudha Feb 13 '20 at 05:00

3 Answers3

1

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.

Tim Biegeleisen
  • 502,043
  • 27
  • 286
  • 360
  • Thank you so much for the quick response. I think this looks promising and I am going to try this and let you know. Unfortunately, I am not designing those tables, but I will have a chat with them. One last thing, this should work when all numbers are the same right. Again thank you so much – WeldaSudha Feb 13 '20 at 05:24
  • @WeldaSucha Yes, this answer is robust to either a two-way or three-way tie (and in fact possible ties are the reason I am using this approach). – Tim Biegeleisen Feb 13 '20 at 05:30
  • small issue. Some record its showing the same smallest number on both minvalue and secondMinvlaue even when they have a second min number. some records showing me completely different numbers. They are not even in the particular three columns. Do you know why is that? – WeldaSudha Feb 13 '20 at 17:30
  • for example, `Item_id Item_length Item_width Item_heigth MinValue SecondMinValue ' result according to above colunm orders '5650678 4 16 16 4 4` on tis one it is showing same vale, it should be 4 and 16. `4348907 8 29 56 8 8` this is also the same. `9033813 13 10 17 3 3` This one have complete different result. Do you know whay is that tho? – WeldaSudha Feb 13 '20 at 18:09
  • In Teradata you can skip right filtering on the window function results using the `QUALIFY` clause. Like `SELECT Item_Id, item, ROW_NUMBER() OVER (PARTITION BY Item_id ORDER BY item) rn FROM cte QUALIFY rn <= 2` – JNevill Feb 13 '20 at 18:27
1

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
dnoeth
  • 59,503
  • 4
  • 39
  • 56
0

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
zip
  • 3,938
  • 2
  • 11
  • 19
  • thank you for the quick response. I am sure how values function works for me since I am getting all columns from table. `SELECT MIN(Item_caract) FROM Category_item (VALUES (Item_length),(Item_width),(Item_heigth)) a(Item_caract)` g – WeldaSudha Feb 13 '20 at 05:18
  • Besides the problem that Teradata doesn't support VALUES, your result is simply returning the lowest value in both colujmn :-) – dnoeth Feb 14 '20 at 15:03
  • Thanks dnoeth, will delete post – zip Feb 14 '20 at 15:06