Sortieren anhand einer ID

Sortieren anhand einer ID

am 22.08.2006 18:28:54 von Nico Weinreich

Hallo,

ich habe eine Tabelle mit zwei Spalten (titelid als int, titel als
varchar(50) und sortid als int). Diese möchte ich anhand der sortid
_sortieren_. Dazu habe ich ein Frontend (PHP), welches die titelid und
die Aktion (nach vorn oder hinten verschieben) übergibt. Wie kann ich
jetzt in der Tabelle die sortid ändern (die sortid muss nicht
fortlaufend sein, es können auch _Löcher_ vorhanden sein)...das heißt,
ich möchte z.b. den Titel mit der ID 54871 eine Position nach vorn
verschieben, es muss also die sortid mit der nächst kleineren sortid
getauscht werden. Geht dies komplett über MySQL (in vielleicht ein oder
zwei Statements) zu lösen, oder muss ich mir relativ umständlich erst
die kleinere ID holen, dann dem anderen zuweisen und so weiter...?

Danke, Nico

Re: Sortieren anhand einer ID

am 22.08.2006 18:57:07 von Christian Kirsch

Nico Weinreich schrieb:
> Hallo,
>
> ich habe eine Tabelle mit zwei Spalten (titelid als int, titel als
> varchar(50) und sortid als int). Diese möchte ich anhand der sortid
> _sortieren_.


Falls Du damit "dauerhaft sortieren" meinst: Vergiss es. Dieses Konzept
ergibt keinen Sinn.

> Dazu habe ich ein Frontend (PHP), welches die titelid und
> die Aktion (nach vorn oder hinten verschieben) übergibt. Wie kann ich
> jetzt in der Tabelle die sortid ändern (die sortid muss nicht
> fortlaufend sein, es können auch _Löcher_ vorhanden sein)...das heißt,
> ich möchte z.b. den Titel mit der ID 54871 eine Position nach vorn
> verschieben, es muss also die sortid mit der nächst kleineren sortid
> getauscht werden. Geht dies komplett über MySQL (in vielleicht ein oder
> zwei Statements) zu lösen, oder muss ich mir relativ umständlich erst
> die kleinere ID holen, dann dem anderen zuweisen und so weiter...?

UPDATE?

Re: Sortieren anhand einer ID

am 23.08.2006 08:09:53 von Nico Weinreich

Christian Kirsch schrieb:
> Nico Weinreich schrieb:
>> Hallo,
>>
>> ich habe eine Tabelle mit zwei Spalten (titelid als int, titel als
>> varchar(50) und sortid als int). Diese möchte ich anhand der sortid
>> _sortieren_.
>
>
> Falls Du damit "dauerhaft sortieren" meinst: Vergiss es. Dieses Konzept
> ergibt keinen Sinn.
>

Wie dann? (Neue Datensätze werden dann mit der letzten sortid um eins
erhöht eingefügt und können ggf. per Frontend an die Position verschoben
werden, nach meiner Vorstellung)

>> Dazu habe ich ein Frontend (PHP), welches die titelid und
>> die Aktion (nach vorn oder hinten verschieben) übergibt. Wie kann ich
>> jetzt in der Tabelle die sortid ändern (die sortid muss nicht
>> fortlaufend sein, es können auch _Löcher_ vorhanden sein)...das heißt,
>> ich möchte z.b. den Titel mit der ID 54871 eine Position nach vorn
>> verschieben, es muss also die sortid mit der nächst kleineren sortid
>> getauscht werden. Geht dies komplett über MySQL (in vielleicht ein oder
>> zwei Statements) zu lösen, oder muss ich mir relativ umständlich erst
>> die kleinere ID holen, dann dem anderen zuweisen und so weiter...?
>
> UPDATE?

Is schon klar, aber kann ich damit nicht immer nur einen Datensatz
(WHERE titelid=12345) bearbeiten? Sonst läuft das doch über die ganze
Tabelle, bzw. wie sollte das Update aussehen?!

Re: Sortieren anhand einer ID

am 23.08.2006 09:01:34 von Sibylle Koczian

Nico Weinreich schrieb:
> Christian Kirsch schrieb:
>=20
>> Nico Weinreich schrieb:
>>
>>> Hallo,
>>>
>>> ich habe eine Tabelle mit zwei Spalten (titelid als int, titel als
>>> varchar(50) und sortid als int). Diese möchte ich anhand der sortid=

>>> _sortieren_.=20
>>
>>
>>
>> Falls Du damit "dauerhaft sortieren" meinst: Vergiss es. Dieses Konzep=
t
>> ergibt keinen Sinn.
>>
>=20
> Wie dann? (Neue Datensätze werden dann mit der letzten sortid um eins=

> erhöht eingefügt und können ggf. per Frontend an die Position ver=
schoben
> werden, nach meiner Vorstellung)
>

Du formulierst, als würdest Du Dir eine Tabelle vorstellen, die auf dem=

Server sortiert ist und bleibt. So wie man das in dBASE tatsächlich
haben konnte (aber schon damals nicht haben musste). Das passt mit einer
relationalen Datenbank wirklich nicht zusammen.

Aber vielleicht meinst Du etwas viel Einfacheres: ein Sortierkriterium,
das sich nicht einfach aus den vorhandenen Daten ableiten lässt (also
weder Sortierung nach titelid noch alphabetisch nach titel). Ist es das?
In dem Fall würde ich bewusst springende Nummern mit ordentlichen
Abständen wählen, neue Nummern bei der Eingabe manuell in die jeweils=

passende Lücke füllen und gelegentlich die Nummerierung erneuern (per=

Frontend oder bei hinreichend neuem MySQL mit einer Stored Procedure).
Das nur als simpelste Variante, systematische Klassifikation ist ein
Kapitel für sich und da gibt es vielerlei Möglichkeiten.


--=20
Dr. Sibylle Koczian
Universitaetsbibliothek, Abt. Naturwiss.
D-86135 Augsburg
e-mail : Sibylle.Koczian@Bibliothek.Uni-Augsburg.DE

Re: Sortieren anhand einer ID

am 23.08.2006 09:09:01 von Christian Kirsch

Nico Weinreich schrieb:
> Christian Kirsch schrieb:
>> Nico Weinreich schrieb:
>>> Hallo,
>>>
>>> ich habe eine Tabelle mit zwei Spalten (titelid als int, titel als
>>> varchar(50) und sortid als int). Diese möchte ich anhand der sortid
>>> _sortieren_.
>>
>> Falls Du damit "dauerhaft sortieren" meinst: Vergiss es. Dieses Konzept
>> ergibt keinen Sinn.
>>
>
> Wie dann? (Neue Datensätze werden dann mit der letzten sortid um eins
> erhöht eingefügt und können ggf. per Frontend an die Position verschoben
> werden, nach meiner Vorstellung)
>

Es gibt hier immer wieder Leute, die hoffen, man könne eine Tabelle
dauerhaft sortieren: So eine Art magisches "SORT ONCE", und danach
könnten sie SELECT *ohne* ORDER BY aufrufen, und jedesmal kämen ihre
Daten in derselben (richtigen) Reihenfolge raus. Darauf wollte ich hinaus.

>>> Dazu habe ich ein Frontend (PHP), welches die titelid und
>>> die Aktion (nach vorn oder hinten verschieben) übergibt. Wie kann ich
>>> jetzt in der Tabelle die sortid ändern (die sortid muss nicht
>>> fortlaufend sein, es können auch _Löcher_ vorhanden sein)...das heißt,
>>> ich möchte z.b. den Titel mit der ID 54871 eine Position nach vorn
>>> verschieben, es muss also die sortid mit der nächst kleineren sortid
>>> getauscht werden. Geht dies komplett über MySQL (in vielleicht ein oder
>>> zwei Statements) zu lösen, oder muss ich mir relativ umständlich erst
>>> die kleinere ID holen, dann dem anderen zuweisen und so weiter...?
>> UPDATE?
>
> Is schon klar, aber kann ich damit nicht immer nur einen Datensatz
> (WHERE titelid=12345) bearbeiten? Sonst läuft das doch über die ganze
> Tabelle, bzw. wie sollte das Update aussehen?!

Deine WHERE-Bedingung kann natürlich beliebig komplex sein. Aber für
Deine Anforderung wirst Du kaum eine einfache Lösung in SQL finden, die
jede Eventualität abdeckt. Sibylle hat ja schon auf Stored Procedures
hingewiesen (ab MySQL 5!).

Da könntest Du - in symbolischem Code - sowas tun:

procedure umsortieren (id1, id2, sortid1, sortid2)

update bla set sortid = sortid2 where id = id1;
update bla set sortid = sortid1 where id = id2;

um zwei Elemente zu vertauschen.

Vielleicht überlegst Du aber nochmal, ob es nicht doch ein natürliches
Sortierkriterium gibt, das Du verwenden kannst.

Re: Sortieren anhand einer ID

am 23.08.2006 09:10:38 von Thomas Rachel

Nico Weinreich wrote:

>> UPDATE?
>
> Is schon klar, aber kann ich damit nicht immer nur einen Datensatz
> (WHERE titelid=12345) bearbeiten? Sonst läuft das doch über die ganze
> Tabelle, bzw. wie sollte das Update aussehen?!

Angenommen, Du willst 5 und 7 vertauschen:

UPDATE tabelle SET sortid=12-sortid WHERE sortid IN (5,7)


Thomas
--
Könnte man sich darauf einigen, "ignorieren" im Wortsinn zu interpretieren
und die Interpretation in Handlungsprinzipien für die Praxis zu gießen?
(Andreas Kilgus in dnq)

Re: Sortieren anhand einer ID

am 23.08.2006 09:14:53 von Nico Weinreich

Sibylle Koczian schrieb:
>
> Aber vielleicht meinst Du etwas viel Einfacheres: ein Sortierkriterium,
> das sich nicht einfach aus den vorhandenen Daten ableiten lässt (also
> weder Sortierung nach titelid noch alphabetisch nach titel). Ist es das?

Ich rede gern mal drum herum :) Genau das meinte ich.

> In dem Fall würde ich bewusst springende Nummern mit ordentlichen
> Abständen wählen, neue Nummern bei der Eingabe manuell in die jeweils
> passende Lücke füllen und gelegentlich die Nummerierung erneuern (per
> Frontend oder bei hinreichend neuem MySQL mit einer Stored Procedure).
> Das nur als simpelste Variante, systematische Klassifikation ist ein
> Kapitel für sich und da gibt es vielerlei Möglichkeiten.
>
>

MySQL kann mittlerweile Stored Procedures? Oh oh oh, bin ich lange raus
gewesen...
Naja, ich baue grad ein Portal auf PHP/MySQL Basis, weil ich keines
gefunden habe, das meine Anforderungen erfüllt. Das mit der manuellen
Sortierung per Eingabe einer Nummer kenne ich glaube von osCommerce für
die Sortierung der Kategorien. Da an dem Portal verschiedene Admins
mitwirken sollen, hatte ich mir gewünscht dass es eben komfortabler (und
auch schnell im Backend) geht, als jedem erklären zu müssen: du musst da
eine Nummer eintragen.

Danke trotzdem für die Anregungen.

Re: Sortieren anhand einer ID

am 23.08.2006 10:54:35 von Harald Fuchs

In article <4l2cqeFed7gcU1@news.dfncis.de>,
Sibylle Koczian writes:

> Aber vielleicht meinst Du etwas viel Einfacheres: ein Sortierkriterium,
> das sich nicht einfach aus den vorhandenen Daten ableiten lässt (also
> weder Sortierung nach titelid noch alphabetisch nach titel). Ist es das?
> In dem Fall würde ich bewusst springende Nummern mit ordentlichen
> Abständen wählen, neue Nummern bei der Eingabe manuell in die jeweils
> passende Lücke füllen und gelegentlich die Nummerierung erneuern (per
> Frontend oder bei hinreichend neuem MySQL mit einer Stored Procedure).

Einfacher wären wohl Gleitpunktzahlen, weil es dort (innerhalb
gewisser Grenzen) immer eine Lücke gibt. Aber das gelegentliche
Umnumerieren braucht man auch damit.

Re: Sortieren anhand einer ID

am 23.08.2006 11:35:26 von Nico Weinreich

Thomas Rachel schrieb:
> Nico Weinreich wrote:
>
>>> UPDATE?
>> Is schon klar, aber kann ich damit nicht immer nur einen Datensatz
>> (WHERE titelid=12345) bearbeiten? Sonst läuft das doch über die ganze
>> Tabelle, bzw. wie sollte das Update aussehen?!
>
> Angenommen, Du willst 5 und 7 vertauschen:
>
> UPDATE tabelle SET sortid=12-sortid WHERE sortid IN (5,7)
>

So simpel und genau das, was ich gesucht habe. Ich arbeite seit einem
dreiviertel Jahr an einem riesigen Dataware House auf MSSQL-Basis mit,
aber auf so eine Idee komm ich nicht...peinlich peinlich :)

D.h. mit zwei Statements

SELECT TOP 2 sortid FROM tabelle WHERE sortid=7 ORDER BY sortid DESC

und deinem oben stehenden Statement sollte das erledigt sein...super
mehr wollte ich nicht. (Natürlich noch ne Abfrage mit PHP dazwischen, ob
zwei Ergebnisse zurückkommen, damit man nicht den ersten Eintrag noch
weiter nach vorne verschiebt).

Ich wollte gerade mal noch probieren (mit meiner realen Tabelle und
übergebener order_id=3)

UPDATE forum_topics SET order_id=(
SELECT SUM(order_id)
FROM forum_topics
WHERE order_id<=3
ORDER BY order_id DESC
LIMIT 2
)-order_id
WHERE order_id IN (
SELECT x.*
FROM (
SELECT order_id
FROM forum_topics
WHERE order_id<=3
ORDER BY order_id DESC
LIMIT 2
) AS x
)

aber MySQL 4.1.13 meckert, dass die Zieltabelle forum_topics nicht in
der FROM-Klausel verwendet werden darf. Gibt es hier noch ein
Workaround, oder muss ich dann 2 Querys draus machen?

Danke, Nico

Re: Sortieren anhand einer ID

am 23.08.2006 12:28:03 von Gregor Kofler

Nico Weinreich meinte:

> Ich wollte gerade mal noch probieren (mit meiner realen Tabelle und
> übergebener order_id=3)
>
> UPDATE forum_topics SET order_id=(
> SELECT SUM(order_id)
> FROM forum_topics
> WHERE order_id<=3

> ORDER BY order_id DESC
> LIMIT 2

Wozu das? Das SUM liefert dir genau einen Wert. Order und Limit kannst
du dir schon mal schenken.

> )-order_id

> WHERE order_id IN (

> SELECT x.*
> FROM (

Den Alias brauchst du nicht.

> SELECT order_id
> FROM forum_topics
> WHERE order_id<=3

> ORDER BY order_id DESC
> LIMIT 2
> ) AS x

s.o.

> )
>
> aber MySQL 4.1.13 meckert, dass die Zieltabelle forum_topics nicht in
> der FROM-Klausel verwendet werden darf.

Beim "flüchtigen Überlegen": Evtl. hat MySQL Probleme mit der
"Rekursion"? Du belegst order_id mit Werten die du aus order_id in einer
Subquery ermittelst.


Gruß, Gregor



--
http://www.gregorkofler.at ::: Landschafts- und Reisefotografie
http://www.licht-blick.at ::: Forum für Multivisionsvorträge
http://www.image2d.com ::: Bildagentur für den alpinen Raum

Re: Sortieren anhand einer ID

am 23.08.2006 12:39:12 von Nico Weinreich

Gregor Kofler schrieb:
> Wozu das? Das SUM liefert dir genau einen Wert. Order und Limit kannst
> du dir schon mal schenken.

Flüchtigkeitsfehler, hänge heute schon wieder zu viel an der
Arbeit...ausserdem habe ich den Teil nur von unten kopiert und da SUM
eingesetzt, erzeugt ja auch keinen Fehler...

>
>> SELECT x.*
>> FROM (
>
> Den Alias brauchst du nicht.
>

Ohne den Alias erhalte ich: Every derived table must have its own alias

> Beim "flüchtigen Überlegen": Evtl. hat MySQL Probleme mit der
> "Rekursion"? Du belegst order_id mit Werten die du aus order_id in einer
> Subquery ermittelst.

Das wird schon der Grund sein, ich suche ja auch nach einer Lösung aber
auch gugl hilft da nicht viel weiter...Egal welche Spalte verwendet
wird, MySQL meckert generell die _doppelte_ Verwendung der Tabelle an.
Dann mache ich zwei Querys draus. Wird ja relativ selten aufgerufen,
daher ist hier keine Performance ausschlaggebend.

Danke

Re: Sortieren anhand einer ID

am 23.08.2006 13:41:37 von Axel Schwenke

Nico Weinreich wrote:
> Thomas Rachel schrieb:
>>
>> Angenommen, Du willst 5 und 7 vertauschen:
>>
>> UPDATE tabelle SET sortid=12-sortid WHERE sortid IN (5,7)
>
> So simpel und genau das, was ich gesucht habe. Ich arbeite seit einem
> dreiviertel Jahr an einem riesigen Dataware House auf MSSQL-Basis mit,
> aber auf so eine Idee komm ich nicht...peinlich peinlich :)
>
> D.h. mit zwei Statements
>
> SELECT TOP 2 sortid FROM tabelle WHERE sortid=7 ORDER BY sortid DESC

Was soll *das* denn sein? Hier ist entweder das WHERE oder das ORDER BY
fehl am Platz.

> UPDATE forum_topics SET order_id=(
> SELECT SUM(order_id)
> FROM forum_topics
> WHERE order_id<=3
> ORDER BY order_id DESC
> LIMIT 2
> )-order_id
> WHERE order_id IN (
> SELECT x.*
> FROM (
> SELECT order_id
> FROM forum_topics
> WHERE order_id<=3
> ORDER BY order_id DESC
> LIMIT 2
> ) AS x
> )



Merke: es ist nicht unbedingt ein Vorteil, alles in ein einziges
SQL-Statement zu quetschen.

> aber MySQL 4.1.13 meckert, dass die Zieltabelle forum_topics nicht in
> der FROM-Klausel verwendet werden darf. Gibt es hier noch ein
> Workaround, oder muss ich dann 2 Querys draus machen?

Diese Einschränkung (Tabelle darf in einer Subquery nicht gleichzeitig
gelesen und geschrieben werden) wird uns wohl noch eine Weile erhalten
bleiben. Aber schreibs doch einfach mit mehreren Statements:

-- der zu verschiebende Datensatz
SET @a=3;

-- finde vorhergehenden Datensatz
SELECT @b:=order_id FROM table WHERE order_id<@a ORDER BY order_id DESC LIMIT 1;

-- vertauschen
UPDATE table SET order_id=(@a+@b)-order_id WHERE order_id IN (@a, @b);


Anmerkungen:

- natürlich mußt du checken, ob Query 2 überhaupt etwas zurück gibt,
z.B. hat der erste Datensatz keinen Vorgänger

- aus praktischen Erwägungen heraus [1] würde man ein UNIQUE Constraint
auf order_id haben wollen. Allerdings schlägt dann das UPDATE fehl,
weil für einen Augenblick beide Datensätze die gleiche order_id haben.
Das könnte man umgehen, indem man zwei UPDATES macht. Leider ist das
ganze dann nicht mehr atomar und man braucht entweder Transaktionen
oder riskiert race conditions.

- wahrscheinlich will man order_id auch NOT NULL deklarieren. Falls NULL
erlaubt ist (z.B. für neu eingefügte Datensätze), muß man diesen Fall
dann auch entsprechend abfangen [2].


[1] stell dir vor, du hast (1, 2, 2, 3) und willst 3 um eins nach vorn
schieben. Dann bekommst du (1, 2, 3, 3).

[2] du hast (NULL, 1, 2) und willst 1 nach vorn schieben. Dann bekommst
du (NULL, NULL, 2).

XL

Re: Sortieren anhand einer ID

am 24.08.2006 08:18:48 von Nico Weinreich

Axel Schwenke schrieb:
> Nico Weinreich wrote:
>
> Was soll *das* denn sein? Hier ist entweder das WHERE oder das ORDER BY
> fehl am Platz.
>
>> UPDATE forum_topics SET order_id=(
>> SELECT SUM(order_id)
>> FROM forum_topics
>> WHERE order_id<=3
>> ORDER BY order_id DESC
>> LIMIT 2

Wie sicher zu merken war, wurde das lange nachfolgende Statement bereits
korrigiert um WHERE order_id<=3...LIMIT 2...

>
> Diese Einschränkung (Tabelle darf in einer Subquery nicht gleichzeitig
> gelesen und geschrieben werden) wird uns wohl noch eine Weile erhalten
> bleiben. Aber schreibs doch einfach mit mehreren Statements:
>

Siehe mein letzter Kommentar, zu dieser Erkenntnis bin ich mittlerweile
auch gelangt...

> - natürlich mußt du checken, ob Query 2 überhaupt etwas zurück gibt,
> z.B. hat der erste Datensatz keinen Vorgänger

....siehe den Kommentar auf den du geantwortet hast...

>
> - aus praktischen Erwägungen heraus [1] würde man ein UNIQUE Constraint
> auf order_id haben wollen. Allerdings schlägt dann das UPDATE fehl,
> weil für einen Augenblick beide Datensätze die gleiche order_id haben.
> Das könnte man umgehen, indem man zwei UPDATES macht. Leider ist das
> ganze dann nicht mehr atomar und man braucht entweder Transaktionen
> oder riskiert race conditions.

*das* ist mal ein Beitrag, da ich aber letztendlich über eine topic_id
auf die Zeilen zugreife, welche eindeutig ist, sind keine gravierenden
Inkonsistenzen bei gleichen order_id zu erwarten, sollte es wider
Erwarten passieren, dass zwei Zeilen die gleiche order_id haben sollten
die sich im schlimmsten Fall eben nicht verschieben lassen. Schließlich
kann man ja auch einmal im Monat die order_id komplett neu zuteilen
lassen, so dass diese fortlaufend vergeben ist. Da aber bei neuem
Einfügen einer Zeile automatisch MAX(order_id)+1 vergeben wird, sollte
es hier wohl zu keinem Zwischenfall kommen.

>
> - wahrscheinlich will man order_id auch NOT NULL deklarieren. Falls NULL
> erlaubt ist (z.B. für neu eingefügte Datensätze), muß man diesen Fall
> dann auch entsprechend abfangen [2].
>
>

wird sicher nicht eintreten, da so nicht angedacht

> [1] stell dir vor, du hast (1, 2, 2, 3) und willst 3 um eins nach vorn
> schieben. Dann bekommst du (1, 2, 3, 3).
>
> [2] du hast (NULL, 1, 2) und willst 1 nach vorn schieben. Dann bekommst
> du (NULL, NULL, 2).
>
> XL

cu