2010-04-20 9 views
8

Ich versuche, eine Abfrage schreiben, die den kürzesten String-Wert in der Spalte zurückgibt. Für Beispiel: Wenn SpalteA die Werte ABCDE, ZXDR, ERC hat, sollte die Abfrage "ERC" zurückgeben. Ich habe die folgende Abfrage geschrieben, aber ich frage mich, ob es einen besseren Weg gibt, dies zu tun?Oracle - Return kürzeste Zeichenfolge Wert in einer Reihe von Zeilen

Die Abfrage sollte einen einzelnen Wert zurückgeben.

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

Antwort

7

Wie sei es:

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

Und um die Leistung dieser Abfrage zu verbessern, könnten Sie einen funktionsbasierten Index für 'LENGTH (ColumnA)' hinzufügen. –

+0

Ich denke, dass Sie das Schlüsselwort "DESC" hier entfernen sollten. –

+0

Ich stimme zu - der Beitrag wurde von Jeffrey falsch bearbeitet; Ich werde korrigieren – IMHO

0

Es gibt zwei Teile zu dieser Frage. Eine alternative Möglichkeit zur Ermittlung der kürzesten Zeichenkette ist die altmodische Unterabfrage:

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

Was ist besser? Es hängt von der Indexierung, den Datenmengen usw. ab, aber ich würde annehmen, dass Ihre Version wahrscheinlich bessere Ergebnisse erzielt. Es kann auch leicht andere Ergebnisse geben, wenn Sie die where ColumnB = 'XXX' in der äußeren Abfrage dupliziert.

Wie Ihre Lösung gibt diese Abfrage eine Zeile für jeden Wert von ColumnA zurück, der drei Zeichen lang ist. Wenn Sie eine einzelne Zeile zurückgeben möchten, können Sie dies tun, indem Sie sie auf rownum beschränken. Es wollen Sie einige Kriterien anwenden, um zu bestimmen, welche die erste Zeile, die Sie in einer weiteren äußeren Abfrage einbinden müssen, ist (meine Abfrage verwenden, aber eine Variante würde Sie arbeitet auch) ...

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

Ich glaube nicht, Sie nisten müssen die Abfrage im zweiten Beispiel auf eine einzige Zeile zu begrenzen. –

+0

@Mmark - Punkt, ich habe Text und Beispiel überarbeitet, also stimmen beide überein – APC

0

Erweiterung auf APC Antwort ein wenig, ich denke, das etwas besser sein wird:

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 APC wird einmal für jede Zeile in TableA ausgeführt werden. Das glaube ich nicht.

Wohlgemerkt, wenn Sie mehrere Zeilen mit der gleichen Länge Zeichenfolge in Spalte haben, erhalten Sie möglicherweise nicht konsistente Ergebnisse aus dieser Abfrage ausführen mehrere Male.

2

Dies würde Ihnen helfen, alle Zeichenfolgen mit der minimalen Länge in der Spalte zu erhalten.

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

Hoffe, das hilft.

1

Die einfachste Art und Weise, mit einem einzigen Tabellenzugriff und ohne 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: bereinigtes ich das Beispiel Ihr Beispiel Abfrage noch mehr passen:

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' 

Grüße, Rob.

+0

Schöne einfache Lösung mit vollständigen und gut formatierten Testfall einschließlich Beispieldaten. – Benjamin

0

Ich weiß, das hat sehr lange Antwort und sehr alt. Aber ich denke, ich kenne einen anderen guten Weg.

Da alle Antwort Sub-Abfrage verwendet, war es nicht für meine Situation geeignet. So habe ich einen Weg gefunden, die Unterabfrage nicht verwendet wurde.

Angenommen, ich habe Daten wie folgt.

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

--output

x 
aaaaa 

Wenn ich min Wert auswählen es gibt 'aaaaa' da 'a' kleiner als 'x' in ascii bestellen.

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

--output

aaaaa 

Aber wenn ich min Länge wählen es gibt 1 (was für 'x' Wert ist), weil 1 kleiner als 5 in numerischer Reihenfolge ist.

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

--output

1 

Und wenn ich min von Länge zu gepolstertem Wert umgewandelt wählen kehrt auch '0000000001' (die 'x' Wert ist), weil '0000000001' kleiner als '0000000005' in ASCII-Reihenfolge.

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

--output

0000000001 

Und ich kann es mit dem Wert selbst binden.

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 

Jetzt kann ich wählen min Länge und zusammen Wert.

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 

Jetzt kann ich nur Wert erhalten, indem substr verwenden.

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

Bitte fügen Sie eine Text Erklärung zu Ihrer Antwort hinzu –

Verwandte Themen