开发者

Oracle - Return shortest string value in a set of rows

开发者 https://www.devze.com 2022-12-27 07:38 出处:网络
I\'m trying to write a query that returns the shortest string value in the column. For ex: if Column开发者_如何学PythonA has values ABCDE, ZXDR, ERC, the query should return \"ERC\". I\'ve written the

I'm trying to write a query that returns the shortest string value in the column. For ex: if Column开发者_如何学PythonA has values ABCDE, ZXDR, ERC, the query should return "ERC". I've written the following query, but I'm wondering if there is any better way to do this?

The query should return a single value.

select distinct ColumnA from
(
  select ColumnA, rank() over (order by length(ColumnA), ColumnA) len_rank 
    from TableA where ColumnB = 'XXX'
)
where len_rank <= 1


How about:

select ColumnA
from
(
  select ColumnA
  from tablea
  order by length(ColumnA) ASC
)
where rownum = 1


This would help you get all the strings with the minimum length in the column.

select ColumnA 
from TableA 
where length(ColumnA) = (select min(length(ColumnA)) from TableA)

Hope this helps.


The simplest way, with a single table access and without subqueries:

SQL> create table mytable (txt)
  2  as
  3  select 'ABCDE' from dual union all
  4  select 'ZXDR' from dual union all
  5  select 'ERC' from dual
  6  /

Table created.

SQL> set autotrace on explain
SQL> select min(txt) keep (dense_rank first order by length(txt)) txt
  2    from mytable
  3  /

TXT
-----
ERC

1 row selected.


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (FULL) OF 'MYTABLE'

EDIT: I adjusted the example to fit your example query even more:

SQL> create table tablea (columna,columnb)
  2  as
  3  select 'ABCDE', 'XXX' from dual union all
  4  select 'ZXDR', 'XXX' from dual union all
  5  select 'ERC', 'XXX' from dual
  6  /

Table created.

SQL> set autotrace on explain
SQL> select min(columna) keep (dense_rank first order by length(columna)) columna
  2    from tablea
  3   where columnb = 'XXX'
  4  /

COLUM
-----
ERC

1 row selected.


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (FULL) OF 'TABLEA'

Regards, Rob.


There are two parts to this question. An alternative way of determining the shortest string is the old-fashioned sub-query:

select distinct ColumnA 
from tablea 
where length(ColumnA) = ( select min(length(ColumnA)) 
                          from TableA 
                          where ColumnB = 'XXX'
                        )
/

Which is better? It depends on the indexing, data volumes, etc but I would guess your version is likely to perform better. It might also give slightly different results, unless you duplicated the where ColumnB = 'XXX' in the outer query.

Like your solution this query will return one row for each value of ColumnA which is three characters long. If you want to return a single row you can do so by restricting it with rownum. It you want to apply some criterion to determine which is the first row you need to embed it in a further outer query (using my query but a variant on yours would work too) ...

select * from (
    select ColumnA 
    from tablea 
    where length(ColumnA) = ( select min(length(ColumnA)) 
                              from TableA 
                              where ColumnB = 'XXX'
                            )
    order by ColumnA
    ) 
where rownum = 1
/


Expanding on APC's answer a little bit, I think this will be slightly better:

SELECT DISTINCT columna
  FROM tablea t1
 WHERE EXISTS ( SELECT 1 FROM tablea t2
                 WHERE LENGTH(t2.columna) = MIN(LENGTH(t1.columna)) )
   AND rownum = 1

IIRC, APC's subselect will be run once for each row in tablea. This, I believe, does not.

Mind you, if you have multiple rows with the same length string in columna, you may not get consistent results from this query run multiple times.


I know this has very long answer and very old. But I think I know another good way.

Because all answer is using sub query, it was not suitable for my situation. So I found a way which was not used sub query.

Say I have a data as following.

select  *
from    (select 'x' f  from dual union all select 'aaaaa' from dual) a

--Output

x
aaaaa

If I select min of value it returns 'aaaaa' because 'a' is smaller than 'x' in ascii order.

select  min(a.f)
from    (select 'x' f  from dual union all select 'aaaaa' from dual) a

--Output

aaaaa

But if I select min of length it returns 1(which is for 'x' value) because 1 is smaller than 5 in numeric order.

select  min(length(a.f))
from    (select 'x' f  from dual union all select 'aaaaa' from dual) a

--Output

1

And if I select min of length converted to padded value also returns '0000000001'(which is for 'x' value) because '0000000001' is smaller than '0000000005' in ascii order.

select  min(lpad(length(a.f), 10, '0'))
from    (select 'x' f  from dual union all select 'aaaaa' from dual) a

--Output

0000000001

And I can bind it with value itself.

select  lpad(length(a.f), 10, '0') || a.f
from    (select 'x' f  from dual union all select 'aaaaa' from dual) a

--Output

0000000001x
0000000005aaaaa

Now I can select min of length and value together.

select  min(lpad(length(a.f), 10, '0') || a.f)
from    (select 'x' f  from dual union all select 'aaaaa' from dual) a

--Output

0000000001x

Now I can get only value by using substr.

select  substr(min(lpad(length(a.f), 10, '0') || a.f), 11, 999)
from    (select 'x' f  from dual union all select 'aaaaa' from dual) a

--Output

x


 select city,length(city) from (select city from station ORDER BY length(city) 
 ASC, CITY ASC)where rownum=1;


 select city,length(city) from (select city from station ORDER BY length(city) 
 DESC, CITY ASC)where rownum=1;
0

精彩评论

暂无评论...
验证码 换一张
取 消