2010-04-20 7 views
8

Sto provando a scrivere una query che restituisce il valore di stringa più breve nella colonna. Ad esempio: se ColumnA ha valori ABCDE, ZXDR, ERC, la query deve restituire "ERC". Ho scritto la seguente domanda, ma mi chiedo se c'è un modo migliore per farlo?Oracle: restituisce il valore di stringa più breve in un set di righe

La query deve restituire un singolo valore.

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

risposta

7

ne dite di:

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

E, per migliorare le prestazioni di questa query, è possibile aggiungere un indice basato su funzioni su 'LENGTH (ColumnA)'. –

+0

Penso che dovresti rimuovere la parola chiave "DESC" qui. –

+0

Sono d'accordo: il post è stato modificato da Jeffrey in modo errato; Correggerò – IMHO

0

Ci sono due parti a questa domanda. Un altro modo per determinare la stringa più breve è la sottoquery obsoleta:

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

Quale è meglio? Dipende dall'indicizzazione, dai volumi di dati, ecc., Ma suppongo che la tua versione abbia prestazioni migliori. Potrebbe anche dare risultati leggermente diversi, a meno che non si sia duplicato lo where ColumnB = 'XXX' nella query esterna.

Come la soluzione, questa query restituirà una riga per ogni valore di ColumnA di tre caratteri. Se si desidera restituire una singola riga, è possibile limitarla con rownum. E si desidera applicare un criterio per determinare quale è la prima riga è necessario incorporare in una ulteriore interrogazione esterna (utilizzando mia domanda, ma una variante sul vostro avrebbe funzionato troppo) ...

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

Non credo che è necessario nido la query nel secondo esempio di limitare a una singola riga. –

+0

@mmark - Fair Point, ho rielaborato il testo e l'esempio, quindi entrambi sono d'accordo – APC

0

Ampliando la risposta di APC un po ', credo che questo sarà un po' meglio:

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, subselect di APC verrà eseguita una volta per ogni riga in TableA. Questo, credo, non lo è.

Attenzione, se si dispone di più righe con la stessa stringa di lunghezza in colonna, è possibile che non si ottengano risultati coerenti da questa query eseguiti più volte.

2

Ciò consente di ottenere tutte le stringhe con la lunghezza minima nella colonna.

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

Spero che questo aiuti.

1

Il modo più semplice, con un unico accesso tavolo e senza sottoquery:

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: ho regolato l'esempio per soddisfare la vostra richiesta esempio ancora più:

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' 

Saluti, Rob.

+0

Bella soluzione semplice con un test case completo e ben formulato con dati campione. – Benjamin

0

So che questo ha una risposta molto lunga e molto vecchia. Ma penso di conoscere un altro buon modo.

Poiché tutta la risposta utilizza la query secondaria, non era adatta alla mia situazione. Quindi ho trovato un modo che non è stato usato sotto query.

Dire che ho un dato come segue.

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

--output

x 
aaaaa 

Se seleziono min di valore che restituisce 'aaaaa' perche 'a' è minore di 'x' in ordine ASCII.

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

--output

aaaaa 

Ma se seleziono min di lunghezza restituisce 1 (che è per valore 'x') perché 1 è minore di 5 in ordine numerico.

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

--output

1 

E se seleziono min di lunghezza convertito in valore imbottita ritorna anche '0000000001' (che è per il valore 'x') perche '0000000001' è più piccolo rispetto '0000000005' in ordine ASCII.

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

--output

0000000001 

E posso legarlo con un valore in sé.

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 

Ora posso selezionare min di lunghezza e il valore insieme.

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 

Ora posso ottenere solo valore utilizzando 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 
0
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; 
+2

Per favore aggiungi una spiegazione di testo alla tua risposta –

Problemi correlati