![]() |
Datenbank: Ms-Sql • Version: 2012 • Zugriff über: --
Bekomme Inner Joins nicht hin
Eins vorweg: Ich habe mit Datenbanken extrem wenig Erfahrung.
Ich habe in meiner Datenbank "Items". Wenn diese verändert werden soll der alte Stand nicht gelöscht werden, sondern ein neuer hinzugefügt werden wo die Revisionsnummer höher ist. Meine Struktur sieht so aus: Item:
Item_Rev:
Dann habe ich das auf meinem PC in einer Sqlite-Datenbank umgesetzt. Folgende Query sollte mir alle Items in der neusten Revision liefern:
Code:
Gesagt, getan. In SQLite kommt genau heraus was ich mir vorgestellt habe 😎👍
SELECT
id, Daten1, Daten2, MAX(changeTimestamp) changeTimestamp, changeComment FROM Item a INNER JOIN ( SELECT id fkey, MAX(changeTimestamp) changeTimestamp, changeComment FROM Item_Rev GROUP BY fkey, changeComment ) b ON (a.rev = b.fkey) GROUP BY id Dann habe ich das auf MS-SQL umgesetzt. Die Query so nimmt er nicht an, er meint Zitat:
Code:
Daraufhin meint er
SELECT
id, Daten1, Daten2, MAX(changeTimestamp) changeTimestamp, changeComment FROM Item a INNER JOIN ( SELECT id AS fkey, MAX(changeTimestamp) changeTimestamp, changeComment FROM Item_Rev GROUP BY id, changeComment ) b ON (a.rev = b.fkey) GROUP BY id Zitat:
Code:
Das Problem: Ich erhalte hier die Items in allen ihren Revisionen. Es ist das gleiche als hätte ich geschrieben
SELECT
id, Daten1, Daten2, MAX(changeTimestamp) changeTimestamp, changeComment FROM Item a INNER JOIN ( SELECT id AS fkey, MAX(changeTimestamp) changeTimestamp, changeComment FROM Item_Rev GROUP BY id, changeComment ) b ON (a.rev = b.fkey) GROUP BY id, Daten1, Daten2, changeComment
Code:
Was läuft hier falsch? Bin ich bei der "fkey"-Sache schon falsch abgebogen? Wenn SQLite etwas kann, dann muss MS-SQL das doch schon lange können, oder?
SELECT [Spalten...] From Item JOIN Item_Rev ON (Item.rev = Item_Rev.id)
|
AW: Bekomme Inner Joins nicht hin
So ganz steige ich da noch nicht durch. Das Inner-Select lautet
Code:
Dabei ist id laut Anmerkung ein AutoInc-Feld - also eindeutig. Ein Group By über ein eindeutiges AutoInc-Feld ist aber irgendwie witzlos, da jeder Feldwert ja eh nur einmal vorkommen kann. Du kannst ja mal einfach nur diese Query ausführen und wirst vermutlich alle Datensätze zurückbekommen.
SELECT id AS fkey, MAX(changeTimestamp) changeTimestamp, changeComment
FROM Item_Rev GROUP BY id, changeComment |
AW: Bekomme Inner Joins nicht hin
Ja, ich bekomme praktisch alles. Beide Tabellen haben eine Spalte
Delphi-Quellcode:
. Ich möchte natürlich nicht nach dem AutoInc-id, sondern nach der Text-ID aus Tabelle "item" gruppieren.
id
SQLite scheint das richtig zu machen. Wenn ich in MS-SQL jetzt aus "GROUP BY id" ein "GROUP BY Item.id" mache sagt er mir Zitat:
|
AW: Bekomme Inner Joins nicht hin
SQL-Code:
?
GROUP BY a.id
|
AW: Bekomme Inner Joins nicht hin
Nope, ob mit a. oder ohne macht keinen Unterschied.
Ich verstehe das nicht. War der Sinn von SQL nicht dass man es über verschiedene Datenbank-Systeme hinweg verwenden könne? |
AW: Bekomme Inner Joins nicht hin
Code:
vielleicht so:
SELECT
id, Daten1, Daten2, MAX(changeTimestamp) changeTimestamp, changeComment FROM Item a INNER JOIN ( SELECT id AS fkey, MAX(changeTimestamp) changeTimestamp, changeComment FROM Item_Rev GROUP BY id, changeComment ) b ON (a.rev = b.fkey) GROUP BY id, Daten1, Daten2, changeComment
Code:
SELECT
a.rev, a.Daten1, a.Daten2, MAX(b.changeTimestamp) changeTimestamp, b.changeComment FROM Item a INNER JOIN Item_Rev b ON (a.rev = b.id) GROUP BY a.Daten1, a.Daten2, b.changeComment |
AW: Bekomme Inner Joins nicht hin
Höchste Revisionsnummer zum Item suchen:
SQL-Code:
SELECT id, Max(rev) from Item group by ID
Das folgende führt nicht zum gewünschten Ergebnis, da hier das höchste Änderungsdatum zu ID UND Änderungskommentar gesucht wird. Das kann nur dann zum "annähernd" gewünschten Ergebnis führen, wenn alle Kommentare gleich sind.
SQL-Code:
Eventuell so?
SELECT id AS fkey, MAX(changeTimestamp) changeTimestamp, changeComment
FROM Item_Rev GROUP BY id, changeComment
SQL-Code:
PS: Bin eher ein alter Knacker, deshalb nehme ich lieber die (für mich) leichter lesbare alte Form für Joins.
select
Item.Id, Item.Rev, Item.Daten1, Item.Daten2, Item_Rev.changeTimestamp, Item_Rev.changeComment from Item, Item_Rev, (SELECT Id, Max(Rev) As Rev from Item group by ID) Max_Rev where Item.rev = Item_Rev.id and Item.ID = Max_Rev.ID and Item.Rev = Max_Rev.Rev |
AW: Bekomme Inner Joins nicht hin
Zitat:
Zitat:
MS-SQL ist schon seltsam. Ich denke es jetzt mit einer völlig anders formulierten Query hinbekommen zu haben:
Code:
In SQLite funktioniert das umgekehrt auch. Ist das jetzt geschickt oder eine Performance-Katastrophe? Und was MS-SQL eigentlich für ein Problem mit dem ersten Statement das in SQLite so gut geht?
SELECT * FROM Auftrag JOIN Auftrag_Rev
ON (Auftrag.rev = Auftrag_Rev.id) WHERE Auftrag.rev in ( SELECT MAX(rev) rev FROM Auftrag JOIN Auftrag_Rev ON (Auftrag.rev = Auftrag_Rev.id) GROUP BY Auftrag.id ) ; Irgendwie weiß ich weshalb ich Kontakt mit relationalen Datenbanken so gut wie möglich vermeide... Update Zitat:
Vielen Dank! |
AW: Bekomme Inner Joins nicht hin
Zitat:
![]() Zitat:
|
AW: Bekomme Inner Joins nicht hin
Danke. Dann fühle ich in meiner Einstellung Bleibe Datenbanken fern solange du kannst ja bestärkt 🤣
|
AW: Bekomme Inner Joins nicht hin
Und T-SQL ist in vielen Dingen halt "speziell"
|
AW: Bekomme Inner Joins nicht hin
Zitat:
|
AW: Bekomme Inner Joins nicht hin
Jo.
|
AW: Bekomme Inner Joins nicht hin
Als ausgewiesener NichtSQLGuru habe ich eine Frage zur Datenstruktur:
Warum trennst Du die Daten in zwei Tabellen auf? Wäre es nicht einfacher, die Revisionsdaten DateTime und den Kommentar mit in die Haupttabelle zu nehmen? Gruß Michael |
AW: Bekomme Inner Joins nicht hin
"Einfacher" sicherlich, aber nicht normalisiert.
Vielleicht war es gestern Abend für mich schon zu spät, aber wenn ich es noch richtig im Kopf habe, dann ist das immerhin in dritter Normalform, denn ich habe keine transitive Abhängigkeit. Hätte ich die Felder "changeTimestamp" und "changeComment" noch mit hereingepackt hätte ich die dritte Normalform nicht, denn Daten1 & Daten2 wären abhängig davon. Wahrscheinlich noch nicht einmal die erste Normalform, denn was haben eigentlich Änderungszeitpunkt und -Kommentar mit einem "Item" an sich zu tun? ![]() ![]() |
AW: Bekomme Inner Joins nicht hin
Normalisiert wäre es aber in die andere Richtung.
Eine Revision bezieht sich auf eine Item und nicht umgekehrt. |
AW: Bekomme Inner Joins nicht hin
Auch ein Argument. Meine Motivation war eher dass es später evtl. nicht "Item_Rev" sondern nur "Rev" ist. Das eine Revision nicht nur ein Item, sondern evtl. auch Wombatze enthält.
Ich denke sobald ich den Kram halbwegs am laufen habe mache ich noch einmal ein eigenes Thema um die Struktur von euch Profis bewerten zu lassen... |
AW: Bekomme Inner Joins nicht hin
Ich finde die Struktur auch Suboptimal, aber sei es wie es sei, ist nicht einfach durch die höchste Rev_ID schon alles bestimmt?
SQL-Code:
Rom, viele Wege und so...
Select * From Items
Left Join rev_items b on b.ID=a.rev Where a.rev=(Select max(rev) From Items Where ID=a.ID) |
AW: Bekomme Inner Joins nicht hin
Ich bin ja noch nicht einmal mit der Syntax klargekommen. Dass man einfach so sagen kann
Delphi-Quellcode:
war mir z.B. auch neu.
WHERE x = (Query)
Das sieht ja schon beschämend einfach aus und scheint zu gehen. Warum grade Left Join? Das würde Sinn machen wenn man Items ohne Revision hat, aber die gibt es bei mir nicht 😎 |
AW: Bekomme Inner Joins nicht hin
Ja, Normalisierung ist wichtig und gut ... aber manchmal steht sie dem flexiblen und raschen Zugriff auf Daten auch ganz schön im Weg. :stupid:
|
AW: Bekomme Inner Joins nicht hin
Ich sehe in diesem Fall nicht das Problem der Normalisierung sondern das Design der Tabellen
|
AW: Bekomme Inner Joins nicht hin
Liste der Anhänge anzeigen (Anzahl: 1)
Ok, wir haben schon jetzt ein handfestes Problem? Dann helft mir bitte doch am besten jetzt 😓
|
AW: Bekomme Inner Joins nicht hin
Ich bin mir nicht sicher, wie diese Aussage zu verstehen ist:
Zitat:
|
AW: Bekomme Inner Joins nicht hin
Du hast Items, von welchem (1 zu n) Revisionen bestehen.
Item ID ( PK; autoinc) DESC (altes Feld ID) Daten1 Daten2 Rev ID (PK; autoinc) Item ( FK auf Tabelle Item Feld ID) Changed Comment |
AW: Bekomme Inner Joins nicht hin
Zitat:
Vorher: Item:
Item_Rev:
Nachher: Item:
Item_Rev:
Zitat:
|
AW: Bekomme Inner Joins nicht hin
Gemäß deinem vorher/nachher Beispiel kannst du die Felder der beiden Tabellen auch in eine Tabelle zusammenfassen. Es existiert zu jedem Item-Datensatz ja auch genau ein Datensatz in Item_Rev. Das macht die Trennung in zwei Tabellen eigentlich überflüssig.
Wenn du das in einer Tabelle realisierst, fällt das Item_Rev.ID Feld ja weg, da es dem Item.rev Feld entspricht. Da das Item.ID Feld aber kein AutoInc-Feld sein kann, bietet sich an, das Item.rev Feld als AutoInc anzulegen, damit die Revisionen automatisch hochgezählt werden. Damit ist gewährleistet, daß neuere Datensätze immer einen höheren rev-Wert haben. |
AW: Bekomme Inner Joins nicht hin
Möglicherweise hilft Dir noch die Info, dass es bei MS SQL einen Feldtyp Namens DATETIME gibt.
Das Feld ANLAGE_DATUM, mit dem Default GEDATE() definiert, verwende ich in quasi jeder Tabelle. Beim anlegen eines Datensatzes sorgt der Konstrukt dafür, dass Du automatisch die genau Datum und Uhrzeit zum Datensatz speicherst. |
AW: Bekomme Inner Joins nicht hin
Vielen Dank für die Geduld und Erklärungen bislang.
Ich sehe ein dass man es eigentlich auch in eine Tabelle packen könnte. Ich sehe noch nicht was ich dadurch gewonnen habe - Ich finde es so sehr sympathisch halt ein Feld "rev" zu haben, und sonst nichts als das "Item". Mit Kommentar, Zeitstempel usw. "verunreinige" ich mMn ein bisschen die Tabelle "Item". Außerdem ist die Tabelle "Item_Rev" völlig neutral gegenüber einem "Item". Eigentlich könnte ich sie sogar "Revisionen" nennen und für alles nutzen, nicht nur Items. Wenn ich eines Tages noch anfange zu Revisionen z.B. nicht nur einen Freitext-Kommentar sondern einen Benutzernamen zu speichern müsste ich dann auch nur die Tabelle "Revisionen" anpassen - Und nicht alle Tabellen wo etwas revisioniert wird. |
AW: Bekomme Inner Joins nicht hin
Beispiel solch einer Definition MS SQL Server Management Studio
Code:
CREATE TABLE [dbo].[tblName](
. . . [DatAuf] [datetime] NULL CONSTRAINT [DF_tblName_DatAuf] DEFAULT (getdate()), [UsrAuf] [varchar](124) NULL CONSTRAINT [DF_tblName_UsrAuf] DEFAULT ((((('Pc: '+host_name())+'; User: ')+suser_sname())+' - ')+app_name()), [DatAen] [datetime] NULL, [UsrAen] [varchar](124) NULL, . . usw |
AW: Bekomme Inner Joins nicht hin
Zitat:
Zitat:
Zitat:
|
AW: Bekomme Inner Joins nicht hin
Wir arbeiten hier mit einer Software mit über 1000 Tabellen, alls großteils historisch gesteuert, das ist ja sowas ähnliches wie deine Revisionen.
Die Infos dazu stehen jeweils in der Tabelle selber, in unserem Fall eine Art GültigVON und GültigBis Datum für den Datensatz. Der Vorteil ist, wie du selber schon gemerkt hast, das die Abfragen einfacher werden, wenn du wissen willst, was der gerade aktuell gültige Datensatz ist. Wenn du daher aktuelle Items mit aktuellen Foos joinen willst und sowohl für Items als auch Foos musst du die Revisionstabelle dazu joinen oder abfragen, wird es schnell unübersichtlich und evtl. auch langsam. Allerdings ist deine Intention mit der Revisionstabelle natürlich auch sinnvoll und dafür gibt es bei uns ein ChangeLog/ChangeHistory Tabellen Konstrukt, dass zwar nicht optimal (gewachsene Struktur) ist, aber man kann sehen wer hat was wann geändert (leider nicht warum). |
AW: Bekomme Inner Joins nicht hin
Grundsätzlich ist Normalsisierung (bzw. SnowFlaking) sinnvoll. Man kann ja auch einen (Join-)View erstellen.
|
AW: Bekomme Inner Joins nicht hin
Um dir noch etwas Lesestoff zu geben: Du hast da eine
![]() Es gibt verschiedene Ansätze, wenn du die Historie behalten möchtest nur Typ 2 oder 3. Bei Typ 2 kannst du die nicht mehr gültigen Daten in eine eigene Tabelle auslagern, muss aber nicht. Wichtiger wäre, dass du immer ein "Gültig bis" Datum drin speicherst, wenn das NULL ist, ist diese Zeile gültig. Wenn du den Primary Key nicht auf dieses Feld erweitern möchtest, würde ich es auf zwei Tabellen splitten - siehe Beitrag von mkinzer: Zitat:
|
AW: Bekomme Inner Joins nicht hin
Mein Senf:
Ich versteh die Trennung, die (eigene) innere Ordnung der Dinge, die man erhalten möchte. Das ist auch legitim und m.E. ebenso legitim, an dieser stelle "Insider Wissen" einzusetzen und bereits eine Trennung vorzunehmen, wenn sie faktisch in der bevorstehenden Version noch nicht benötigt wird. Normalisierung ist kein Selbstzweck, sondern hat primär einen ganz simplen und wichtigen Grund: Konsistenz Wenn man sein Datenmodell so konstruiert, dass Konstistenz (garantiert) gewahrt bleibt, hat man nichts verkehrt gemacht. Den Zusatzaufwand treibt man aber nicht aus Vergnügen, es sollte schon irgendwelche Gründe geben, die nicht nur einem selbst bekannt (und akzeptiert) sind. Bei einer Historisierung oder Revisionen hier wäre es m.E. wichtig dafür zu sorgen, das ein Basiszugriff auf aktuell gültige Daten per Default erfolgt. Das würde hier heißen, mit Views zu arbeiten, die Altdaten automatisch ausblenden. Ausnahme wäre ggF. wenn die Historie teil des Business Case ist und irgendwelche Spezialisten ständig mit verschiedenen Ständen arbeiten (müssen). Gerade erst gesehen: Ist das vielleicht nur eine Fake Frage, weil DSG hier ![]() nicht weiter kommt? |
AW: Bekomme Inner Joins nicht hin
Zitat:
Ausnahmen die es nieee geben wird sind zum :kotz: Gruß K-H |
AW: Bekomme Inner Joins nicht hin
Zitat:
Was die sichere Seite betrifft, steht man dort sehr häufig auch nur auf dünnem Eis. Analog zu Premature Optimization fällt das auch sehr leicht in die Kategorie Over-Engineering oder wie man hier landläufig sagt: unnötig kompliziert. |
AW: Bekomme Inner Joins nicht hin
apropos 1:1
wie sind die Beispieldaten zu verstehen, die Werte für REV>Id passen m.E. nicht, Tippfehler oder Verständnisfehler bei mir? ![]() |
AW: Bekomme Inner Joins nicht hin
Ich würde ja ganz stumpf eine "Master" Tabelle (item) machen, in welcher die aktuellen Items drin stehen. Und zwar immer in der neuesten Revision - zur Not auch noch mit Kommentar, warum diese Revision nötig war, dem Benutzernamen und einem Zeitstempel.
Daneben eine "History" Tabelle (item_history), welche prinzipiell gleich aufgebaut ist. Bei jeder Änderung in der Master Tabelle wird der alte Record in die History Tabelle übertragen und fertig. Ein select auf die Master Tabelle liefert die aktuellen items. Wenn die verschiedenen Revisionen gebraucht werden, kommt ein UNION mit der History Tabelle. |
AW: Bekomme Inner Joins nicht hin
Zitat:
|
AW: Bekomme Inner Joins nicht hin
Hier sind 3 Varianten ohne Anspruch auf Vollständigkeit usw:
Code:
-- nur max(id) wenn garantiert(!) ist, dass sie eindeutig, aufsteigend ist (faule Lösung)
-- geht "immer" (Syntax) select a.*, r.* from item a join (select max(id) fkey from Item_Rev) b on a.rev = b.fkey join item_rev r on a.rev = r.id; -- erst max(timestamp), darüber die zugehörige ID holen-Reihenfolge nun egal- ("ordentliche" Lösung) -- ordentlich halt*, Syntax geht auch immer select a.*, r.* from item a join (select id from item_rev ri join (select max(changetimestamp) x from Item_Rev) rx on ri.changetimestamp = rx.x) b on a.rev = b.id join item_rev r on a.rev = r.id; -- top n, logisch sauber, vermeidet Group ganz, trendy -- bin mir nicht ganz sicher, aber es wäre eine typische MS Lösung, die relativ früh (als erste?) die TOP Funktion eingeführt hatten -- ich würde mal sagen, eine gute Lösung, m.E. ohne Group sozusagen straight forward, aber syntaktisch nicht sehr kompatibel -- (ersetze "top n" durch "limit n" o.ä. für andere Systeme) select a.*, r.* from Item a join (select top 1 id from Item_Rev order by changetimestamp desc) b on a.rev = b.id join item_rev r on a.rev = r.id; zu * Alles ist relativ. Diese Lösung vermeidet das vielleicht etwas theoretische ID Problem (aufsteigend=chronologische ID= höchste Revision). Aber was ist, wenn changetimestamp sich später (per Trigger) ändert, weil z.B. rein Rechtschreibfehler korrigiert wurde? Die anfangs von Dir gefundene SQLite Variante hat mich mehr als überrascht und stark an das mySQL Desaster erinnert. Ich war bis gerade der Meinung, mySQL seien die Einzigen, die sowas gefährliches "anbieten". Deren Group By Syntax war angetreten, ~ohne weiteres~ eine richtige Antwort zu liefern, es kommt dort aber meist Schrott raus, wenn man sich nicht an die Spielregeln hält. SQLite ist dann wohl Nr. 2 in dieser Heldenliste. Ein Zitat aus deren Hilfe Zitat:
Es geht noch weiter in der Hilfe. Dort wird beschrieben, dass nur bei min und max etwas besonderes gemacht wird, was dann tatsächlich Dein Ergebnis erklärt. Eine sehr bequeme "Abkürzung" für den Entwickler von etwas SQL Text, aber tatsächlich weit weg vom Standard. Ich find besonders den ersten Teil gefährlich. Würde ich mich nicht dran gewöhnen. Vielleicht hast Du selbst diese Doku gelesen und warst der Meinung, so gehört sich das .. Und wo ich dabei bin. was das Thema "kompatibel" angeht: Es gibt die verschiendenen ANSI SQL Versionen von 86 bis 2016. Daran hält sich leider niemand vollständig (Was m.E. schlimmer klingt, als es ist, da man bei sowas offenbar schon die wichtigsten Dinge angeht) Den Wunsch nach Kompatibilität versteh ich gut, aber es ist wie bei verschiedenen Pascal Dialekten, eben nicht alles kompatibel. SQLite ist m.E. ein Exot, weil es ganz klar anders positioniert ist, als MSSQL oder andere große. Auf der Ebene am ehesten vergleichbar mit dem embedded Part von Firebird, aber eben nur mit dem. Ebenso exotisch der lockere Umgang mit Typen, low concurrency Ansatz, .. MSSQL ist da gefühlt eher Standard, aber fett und reich genug, eigene Features vor den Standard zu stellen. Das gilt ähnlich für die anderen Kommerzanbieter. Gewollt sind am ehesten die open source Systeme bemüht, Standards einzuhalten, hier fehlen dann leider oft die Ressourcen. Postgres legt allerdings sehr großen Wert auf die ANSI-SQL Normen und kriegt das auch ganz gut hin. In der Praxis geschieht es häufig, dass man sich für eine Syntax entlang der Doku des verwendeten Systems entscheidet und dabei nicht Standard gemäß formuliert, obwohl die DB es verstehen würde. Die Mohrrübe ist dabei oft auch eine kleine Extraportion Komfort oder Funktionalität. "Alte Hasen" haben da den kleinen Vorteil, dass sie aus Gewohnheit "gut abgehangenes" SQL verwenden. (Was nicht immer wirklich ein Vorteil sein muss, weil sie die Mohrrübe gar nicht sehen). Zum "Problem" Für Group By gibt es eine Faustregel, mit der man gut fährt: Alle nicht aggregierten Felder der Select Liste müssen im Group By genannt werden. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:51 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz