Alternierende Sortierung

Hallo zusammen,

mir ist heute eine etwas ungewöhnliche Frage eingefallen, die ich gerne mal mit euch diskutieren wollte.

Angenommen, ich habe eine Tabelle mit Werten wie:

select * from bereiche order by name;

name, wert
Bereich1, 100
Bereich2, 200
Bereich3, 20
Bereich4, 150
Bereich5, 300

Der Hintergrund: Wenn man diese in einem Tortendiagramm ausgeben möchte, wäre es besonders übersichtlich, wenn man immer einen größeren Bereich neben einem kleineren Bereich platzieren würde. Zwei kleinere Bereiche nebeneinander sind schlechter lesbar.

Dabei hätte ich überlegt, wie man denn mit Postgres alternierend sortieren kann. Also so:

Bereich5, 300
Bereich3, 20
Bereich2, 200
Bereich4, 150
Bereich1, 100

Ich habe allerdings keine leichte Lösung gefunden. Klar könnte man mit einer Funktion mitzählen oder modulo2-artig eine Zählspalte mit Sortiergrößen befüllen, aber mich hätte interessiert, ob jemand einen einfacheren Weg kennt, der direkt über das order by führt?

select * from bereiche order by XYZ?;

Würde mich freuen, wenn jemand eine intressante oder trickreiche Lösung kennt.

az_hf0

seltsame anforderung …

also auch wenn ich von einem tortendiagramm erwarte, dass es die slices der groese nach sortiert rundum aufzeigt … hier meine antwort zum problem

du nimmst einfach die datenmenge normal - der groese sortiert

liest das ergebnis in sowas wie ein array und fuellst das diagramm ueber eine schleife der halben anzahl der elemente (n) an durchlaeufen

dabei bei jedem durchlauf immer 2 slices gleichzeigt setzen …
einen von vorn (index) und einen von n / 2 + index

Nettes Problem…

Ich denke mal, mittels den CTE-Funktionen von 8.4 könnte man da was machen, vielleicht in dieser Art:

test=*# select * from bereiche;
   name   | wert
----------+------
 bereich1 |  100
 bereich2 |  200
 bereich3 |   20
 bereich4 |  150
 bereich5 |  300
(5 Zeilen)

Zeit: 0,204 ms
test=*# select name, wert from (select *, row_number() over (order by wert desc) from bereiche) foo order by (case when row_number % 2 = 1 then row_number else (select count(1)-row_number from bereiche)  end);
   name   | wert
----------+------
 bereich5 |  300
 bereich1 |  100
 bereich2 |  200
 bereich4 |  150
 bereich3 |   20
(5 Zeilen)

Die Abfrage:

test=*# select name, wert, row_number from (select *, row_number() over (order by wert desc) from bereiche) foo;
   name   | wert | row_number
----------+------+------------
 bereich5 |  300 |          1
 bereich2 |  200 |          2
 bereich4 |  150 |          3
 bereich1 |  100 |          4
 bereich3 |   20 |          5
(5 Zeilen)

liefert mir eine Zeilennummer, in der Sortierung ist mein Plan, je nachdem ob diese gerade oder ungerade ist in der sortierten Menge mal von oben und mal von unten zu zählen.

Ich bin mir aber nicht ganz sicher, ob dies wirklich für alle möglichen Konstellationen funktioniert.

Vielleicht fällt ja jemand noch was besseres ein…

Andreas

ich lese deine loesungen zu windowing zeuchs zwar wirklich gern aber hier …

keep it simple :wink:

Nun, brauchst ja nicht hinzu schauen. Alternativ, nenne eine bessere Lösung.

Ich biete mal noch dies:

test=*# select * from (select * from (select *, row_number() over (order by wert) as nr from bereiche) foo where nr%2=1 union all select * from (select *, row_number() over (order by wert desc) as nr from bereiche) foo where nr%2=0) foo order by nr;
   name   | wert | nr
----------+------+----
 bereich3 |   20 |  1
 bereich2 |  200 |  2
 bereich4 |  150 |  3
 bereich1 |  100 |  4
 bereich5 |  300 |  5
(5 Zeilen)

Andreas

hab ich doch … sogar 2

name | wert | nr
----------±-----±—
bereich3 | 20 | 1
bereich2 | 200 | 2
bereich4 | 150 | 3
bereich1 | 100 | 4
bereich5 | 300 | 5
(5 Zeilen)

>
> Andreas

das ist zwar nett, aber es laesst keine wirkliche "alternierende sortiereung" erkennen ... du muesstestst die ergebnismenge halbieren .. und aufsteigend nach jedem element aus der ersten (kleine) haelfte ein element aus der zweiten (grosse) haelfte einfuegen ..

daher meinte ich 1. entweder ganz lassen - weils eigentlich quark ist - oder 2. normal sortieren und algorithmisch loesen beim darstellen ..

Das stimmt natürlich, daher biete ich:

test=*# select * from foo;
 i
----
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
(10 rows)

test=*# with f1 as (select i, row_number() over (order by i)::float   as nr from foo limit (select count(*) from foo)/2), f2 as (select i, row_number() over()::float+0.5 from (select i from foooffset (select count(*) from foo)/2)x) select * from f1  union select * from f2  order by nr;
 i  | nr
----+-----
  1 |   1
  6 | 1.5
  2 |   2
  7 | 2.5
  3 |   3
  8 | 3.5
  4 |   4
  9 | 4.5
  5 |   5
 10 | 5.5
(10 rows)



daher meinte ich 1. entweder ganz lassen - weils eigentlich quark ist - oder 2. normal sortieren und algorithmisch loesen beim darstellen …

Oder 3. die Aufgabe (so sortieren mit SQL) lösen :wink:


Andreas

Du Irrer :wink: - sieht aber gut aus

Ich schick dir nächstes Jahr diese Abfrage und frach dich was die macht lacht

Aber is scho’ a bissle wie geil

(auch wenn ich so ein Tortendiagramm nicht wirklich sinnvoll finde)

Hallo,

ganz genau so hatte ich mir das auch vorgestellt. Vom Prinzip her zwei Mengen bilden, diese sortieren und dann alternierend im Resultset zurückgeben. Das WITH-SQL ist natürlich schon ziemlich abgefahren.

Ich danke euch für die interessanten Vorschläge. Wie gesagt, mir gings hier nur um eine interessante SQL-Idee - klar kann man das Problem auch clientseitig lösen.

Grüße,
az_hf0