ResultSet gleichmäßig "portionieren" (ntile?)

Ich habe eine Tabelle “schlagwoerter” mit den Spalten “id” (INTEGER) und “label” (VARCHAR).

Ich will daraus

  • eine alphabetisch sortierte Liste (A-Z) bekommen,
  • die in ca. 100 gleichgroße “Portionen” aufgeteilt wird (NTILE?).
  • Pro “Portion” will ich die id und das label des ersten Records und des letzten Records (paarweise) bekommen. (Beispiel: Aal - Ada, Ada - Ado, …)

Ziel ist eine einfache Navigation über den Gesamtbestand erstellen zu können, wie es bei großen Lexika der Fall ist, z.B. Lexikonsuche | wissen.de

Die einfache Basisabfrage ohne Portionierung ist:
SELECT id, label FROM schlagwoerter ORDER BY label ASC;

Ist so etwas möglich? Wenn ja, wie?

siehe Betreff, ntile() existiert:

ja, das ist mir bekannt. Ich bin leider nicht erfahren genug, wie ich damit mein Problem lösen kann…
Ich vermute auch, dass das alleine nicht ausreicht.

Schön wäre eine Ergebnisliste, die so aussieht:

id | label | portion_id
==|======|=======
1 | Aal | 1
337 | Adam | 1
338 | Adama | 2
411 | Adonis | 2
412 | Adonit | 3

Das Kürzen des Labels auf 3 Stellen würde ich nicht in SQL machen wollen

hielft Dir das weiter?

edb=# create table datazuul(id int generated always as identity, data numeric);
CREATE TABLE
edb=*# insert into datazuul (data) select random()*10000 from generate_series(1,100) s;
INSERT 0 100
edb=*# with tmp as (select id, data, ntile(5) over (order by data) from datazuul) select min(id), max(id), min(data), max(data), ntile from tmp group by ntile order by ntile;
 min | max |       min        |       max        | ntile 
-----+-----+------------------+------------------+-------
   3 |  94 | 13.5967705142903 | 1750.39506472814 |     1
   1 |  95 | 1808.04476866872 | 3309.69194432022 |     2
   2 |  99 | 3330.86940976319 | 5815.47063578185 |     3
   8 | 100 | 6039.09777433454 | 7836.55108658724 |     4
  19 |  90 |  7887.2883151438 | 9975.68385924893 |     5
(5 rows)

edb=*#

Die id ist mit drin, halte ich aber für inhaltsfrei. Aber ich kenne Deine Aufgabe auch ncht so genau …

das werd ich mal versuchen in meinen use case zu übersetzen… danke erstmal :+1:

Also das hier hat funktioniert:

WITH tmp AS (SELECT ROW_NUMBER () OVER (ORDER BY label) AS num, uuid, label, ntile(100) OVER (ORDER BY label ASC) FROM headwords),
tmp2 AS (SELECT min(num) AS minNum, max(num) AS maxNum, ntile FROM tmp GROUP BY ntile ORDER BY ntile)
  SELECT num, uuid, label, ntile FROM tmp WHERE num IN (SELECT minNum FROM tmp2) OR num IN (SELECT maxNum FROM tmp2);

Ich hatte oben erwähnt, dass ich eine Spalte “id” habe. Das war nicht korrekt: ich habe pro Datensatz eine Spalte uuid, die eine zufällig generierte UUID enthält. Diese enthält also auch keinen sortierbaren bzw. mit MIN oder MAX abfragbaren Wert. Daher habe ich mit ROW_NUMBER() was numerisches on the fly erzeugt.

num	    uuid	                                label        ntile
------+---------------------------------------+-------------+--------
1	    99972da5-6a55-4bc8-844d-b2224f0862c8	à	         1
1555	dd225f89-b241-4530-b283-1b7cf0342f6c	Aëroplān	 1
1556	f9ccf99b-c6d7-4327-8f65-8ebb84a9aadc	Aëroskop	 2
3110	f20ebde3-f545-40ec-950e-6cbcd86572b2	Alkylhaloide 2
3111	81b9e0cd-2439-4095-a4d8-0ac1fa74ee6c	Alkyone	     3
4665	8f8a8842-ad19-4790-862f-2dd17204cbd8	Andaöl	     3
4666	40e0cf37-4312-4766-a154-642d5b31c230	Andchui	     4

Das obige funktioniert zwar, ich bin mir aber nicht sicher, ob es nicht mit weniger SELECTs (eleganter) geht…? Hätte dazu noch jemand einen Tipp? (die letzte where bedingung könnte man evtl nur mit einem select machen?)

nicht direkt weniger SELECTs, aber eine alternative WHERE-Abfrage, die auch funktioniert:

WHERE num IN ((SELECT minNum FROM tmp2) UNION (SELECT maxNum FROM tmp2))