![]() |
AW: [PLSQL] Gibt es ein "Select oder Insert"- Befehl?
Zitat:
Die Tabelle DBT_ZEIT macht einen kranken Eindruck. Sie hat einen eindeutigen Schlüssel (Datum, Stunde) und du gibst ihr noch einen künstlichen warum? Warum überhaupt eine Tabelle DBT_ZEIT? Trage Datum und Stunde in DBT_Traffic ein und nimm sie mit in den Primary Key auf. Warum ein Datumsfeld und ein Stundenfeld wenn die Stunde doch mit in das Datumsfeld passt? Übernehme nur das Datumsfeld in DBT_TRAFFIC (lösche ID_ZEIT) und fülle es mit ROUND(<Datum>, 'HH'). Das würde deine Inserts erheblich beschleunigen und deine SP würde nur aus dem MERGE und dem Signal senden bestehen. Ich weiß ja nichts über die Update-Häufigkeit und die Datenmenge. Mein erster Ansatz wäre erstmal jeden Traffic-Datensatz mit dem genauen Zeitpunkt zuspeichern. Verdichtung würde ich erst für die Auswertung. Für mich sieht deine Lösung etwas exotisch aus. Bring doch mal eine der anderen Stellen wo dieses Muster verwendest. Vielleicht kann man ja auch dort aufzeigen wie es einfacher, sicherer und schneller geht. |
AW: [PLSQL] Gibt es ein "Select oder Insert"- Befehl?
Zitat:
SQL-Code:
-- Die Tabelle dbt_Channel führt verschiedene IDs (alles FK) aus anderen Tabellen zusammen zu einem neuen einzigen Key.
-- Diese FKs bilden einen unique index (in anderen Varianten gab es noch zusätzliche Spalten, welche außerhalb jedweden Indexes lagen) -- der PK (Spaltenname: ID), der hier in dbt_Channel gebildet wird, wird in verschiedenen Messwerttabellen verwendet, welche mit Abstand dann auch die größten Tabellen sind. Select count(ID) into c from dbt_channel where ID_Characteristic=CharacteristicID and ID_BaseIndicator=BaseIndicatorID and ID_Indicator=IndicatorID and ID_Phase=PhaseID and ID_Unit=UnitID and ID_Interval=IntervalID and Frequency=aFrequency; if c>0 then if aDoInsert then RaiseAlreadyExist('Channel'); end if; else if not aDoInsert then RaiseNotExist('Channel'); end if; begin insert into dbt_Channel (Id_Characteristic,Id_Phase,Id_Indicator, id_Unit,Id_Interval,Frequency,Id_Baseindicator) values (CharacteristicID, PhaseID, IndicatorID, UnitID, IntervalID, aFrequency, BaseIndicatorID) returning ID into ChannelID; commit; exception when dup_val_on_index then c:=1; when others then raise; end; end if; if c>0 then Select ID into ChannelID from dbt_channel where ID_Characteristic=CharacteristicID and ID_BaseIndicator=BaseIndicatorID and ID_Indicator=IndicatorID and ID_Phase=PhaseID and ID_Unit=UnitID and ID_Interval=IntervalID and Frequency=aFrequency; end if; return(ChannelID); -- Die Reaktion auf aDoInsert ist hier nochmal gesondert zu sehen und muss nicht zwingend hier beachtet werden |
AW: [PLSQL] Gibt es ein "Select oder Insert"- Befehl?
Ok, ich habe es so gewollt :shock:.
Dann bitte aber auch noch die Beschreibung dieser Channel Tabelle und deren ungefähre Update-Häufigkeit (grob 10 pro Stunde oder tausende pro Minute oder gar Sekunde?). |
AW: [PLSQL] Gibt es ein "Select oder Insert"- Befehl?
SQL-Code:
Auf dem PK ID liegt wieder eine Sequence und ein Trigger (wie bei dbt_Zeit)
prompt
prompt Creating table DBT_CHANNEL prompt ========================== prompt create table DBT_CHANNEL ( ID NUMBER not null, ID_BASINDICATOR NUMBER not null, ID_INDICATOR NUMBER not null, ID_PHASE NUMBER not null, ID_UNIT NUMBER not null, ID_INTERVAL NUMBER not null, FREQUENCY NUMBER(*,2), ID_CHARACTERISTIC NUMBER not null ) ; alter table DBT_CHANNEL add constraint DBT_CHANNEL_PK primary key (ID); alter table DBT_CHANNEL add constraint DBT_CHANNEL_UK1 unique (ID_BASINDICATOR, ID_INDICATOR, ID_PHASE, ID_UNIT, ID_INTERVAL, FREQUENCY, ID_CHARACTERISTIC); alter table DBT_CHANNEL add constraint DBT_CHANNEL_DBT_BASEINDIC_FK1 foreign key (ID_BASINDICATOR) references DBT_BASEINDICATOR (ID) on delete cascade; alter table DBT_CHANNEL add constraint DBT_CHANNEL_DBT_CHARACTER_FK1 foreign key (ID_CHARACTERISTIC) references DBT_CHARACTERISTIC (ID); alter table DBT_CHANNEL add constraint DBT_CHANNEL_DBT_INDICATOR_FK1 foreign key (ID_INDICATOR) references DBT_INDICATOR (ID) on delete cascade; alter table DBT_CHANNEL add constraint DBT_CHANNEL_DBT_INTERVAL_FK1 foreign key (ID_INTERVAL) references DBT_INTERVAL (ID) on delete cascade; alter table DBT_CHANNEL add constraint DBT_CHANNEL_DBT_PHASE_FK1 foreign key (ID_PHASE) references DBT_PHASE (ID) on delete cascade; alter table DBT_CHANNEL add constraint DBT_CHANNEL_DBT_UNIT_FK1 foreign key (ID_UNIT) references DBT_UNIT (ID) on delete cascade; Ein neuer Eintrag kommt recht unregelmäßig. Zeit spielt hier nicht die oberste Rolle. Zitat:
Und bitte wegrationalisiere mir meine ausnormalisierte Tabelle hier nicht weg ;) Es ist durchaus möglich, dass hier noch Spalten mit Zusatzinformationen ergänz werden |
AW: [PLSQL] Gibt es ein "Select oder Insert"- Befehl?
Hallo Sirius,
hier ein Entwurf. Da mir der Context fehlt kann ich natürlich nichts testen. Was mir besser gefällt ist, das im Fall, wenn die Channel-Daten schon gespeichert wurden nur einmal die Tabelle dbt_channel abgefragt wird. Der Rest beruht eher darauf, das ich keine Sorgen bei der Verwendung von Exceptions habe, aber vielleicht fehlen mir da auch nur die schlechten Erfahrungen. :)
SQL-Code:
Da ich nicht weiß wie du ChannelID verwendest, bleibt der Verdacht, dass auch diese Tabelle überflüssig seinen könnte. Diese Tabelle scheint mir wie die DBT_ZEIT Tabelle eine Gruppierung darzustellen, die man auch aus den Daten gewinnen könnte wenn man statt der ChannelID die sieben anderen Werte abspeichern würde. Aber ich weiß natürlich nicht wie viele Datensätze anderer Tabellen von der ChannelID abhängen oder wie teuer es ist, die Gruppierung live aus den von ChannelID abhängigen Daten zu ziehen.
BEGIN
LOOP BEGIN -- ChannelID suchen select ID into ChannelID from dbt_channel where ID_Characteristic=CharacteristicID and ID_BaseIndicator=BaseIndicatorID and ID_Indicator=IndicatorID and ID_Phase=PhaseID and ID_Unit=UnitID and ID_Interval=IntervalID and Frequency=aFrequency; if aDoInsert THEN -- existiert, sollte aber nicht RaiseAlreadyExist('Channel'); end if; -- ansonsten sind wir fertig RETURN ChannelID; EXCEPTION WHEN NO_DATA_FOUND THEN -- Channel-Daten noch nicht angelegt if not aDoInsert THEN -- sollten aber angelegt sein RaiseNotExist('Channel'); end if; BEGIN insert into dbt_Channel ( Id_Characteristic, Id_Phase, Id_Indicator, id_Unit, Id_Interval, Frequency, Id_Baseindicator ) values ( CharacteristicID, PhaseID, IndicatorID, UnitID, IntervalID, aFrequency, BaseIndicatorID ) returning ID into ChannelID; commit; RETURN ChannelID; EXCEPTION when dup_val_on_index then NULL; -- den Datensatz gibt es doch schon, alles von vorne END; END; END LOOP; END; |
AW: [PLSQL] Gibt es ein "Select oder Insert"- Befehl?
Sorum habe ich das auch schon einmal aufgezogen. Dadurch wird halt im Normalfall nur ein Select gemacht, und in den meisten Fällen ist das auch ausreichend, denn der entsprechende Datensatz existiert ja nur einmal nicht, ab dann existiert er ja immer und wird auch recht häufig verlangt.
Ich hatte irgendwann dann auf die jetzige Variante umgestellt, weil...tja schon lang her. Exceptions brauchen auf jeden Fall viel Zeit, aber wie das im Vergleich zu einer vollständigen Abfrage ist, weiß ich nicht. Letztenendes spielt es hier keine wichtige Rolle, da in diesem Fall relativ selten drauf zugrgriffen wird (für Datenbankverhältnisse "selten"). Die ID_Channel wird in (aktuell) drei weiteren Datenbanktabellen verwendet. Diese Tabellen werden sehr groß. Derzeit sind dort die Spalten ID_Channel, ID_Measurement, UTCDate + TimeShift, Value (Messwert) enthalten. Man hat also einen Kosten/Nutzenfaktor von ca 1 zu 3 (wenn man mal die Größe der Spalten vernachlässigt). Wenn ich jetzt alle IDs aus dbt_Channel speichern müsste, würde dieser Faktor bedeutend schlechter (siehe auch Normalisierung von Datenbanktabellen). Hinzu kommt, dass ich zu den Channels noch Zusatzinformationen ergänzen werde (zukünftig). Aktuell würde ich sie einfach als neue Spalte in dbt_Channel legen. Diese IF-Verzweigung mit aDoInsert kannst du weglassen. Das war jetzt nur noch hier mit drinn. Edit (nochmal zu einem anderen Beitrag): Zitat:
Deswegen kann ich es nochmal auf den Punkt bringen: Alle PK-FK-Kombinationen müssen doch dieses Problem haben. Was macht man da? |
AW: [PLSQL] Gibt es ein "Select oder Insert"- Befehl?
Zitat:
DBT_CHANNEL und DBT_ZEIT bestehen nur aus dem Primarschlüssel und haben damit eigentlich keine Daseinsberechtigung. Es gibt keine Spalten in diesen Tabellen die von diesen Daten abhängen. Dies verschleierst du ein wenig in dem du für den natürlichen zusammengesetzten Schlüssel, einen weiteren künstlichen vergibst. Bei DBT_ZEIT ist das besonders deutlich zu sehen. Datum und Stunde sind der eigentliche Schlüssel der Tabelle. ID_ZEIT wird noch dazu erfunden. Datum und Stunde lassen sich mit Round(<DateTime>, 'HH') einfach in einem Date-Feld speichern. Du hast also eine Tabelle mit 2 Feldern die beide Unique sind und das eine Feld ID_Zeit wird nur gebraucht um auf den anderen Wert zu verweisen. Speicherst du den gerundeten DateTime-Wert gleich in der Tabelle DBT_TRAFFIC wird alles einfacher. statt
SQL-Code:
schreibst du dann
select * from
dbt_traffic t inner join dbt_zeit z on z.id_zeit = t.id_zeit and z.datum between date '2010-01-01' and date '2010-03-31'
SQL-Code:
und bei einem insert in die DBT_TRAFFIC Tabelle musst du DatumStd auf Round(sysdate, 'HH') setzen.
select * from
dbt_traffic t where t.DatumStd between date '2010-01-01' and date '2010-03-31 23:23:59' Du würdest doch auch nicht
SQL-Code:
in die folgenden Tabellen aufteilen
create table LEUTE
Nachname varchar(30), Vorname varchar(30) );
SQL-Code:
Oder?
create table LEUTE
Nachname varchar(30), VornameID number(10) ); create table VORNAMEN VornameID number(10), Vorname varchar(30) ); Für DBT_CHANNEL mag es praktische Gründe geben, z.B. ist es immer nervig wenn man, um zwei Tabellen zu joinen immer 7 Felder angeben musst, aber das kann ich gar nicht beurteilen, da ich nicht ersehen kann was mit der ChannelID angestellt wird. |
AW: [PLSQL] Gibt es ein "Select oder Insert"- Befehl?
Was du sagst ist ja richtig, und für dbt_Zeit im jetzigen Zustand auch i.O. Aber stell dir mal vor, wenn ich demnächst zu einer bestimmten Stunde noch einen Eintrag machen will, bspw. ein externes Ereignis, irgendetwas beschreibendes. Dies wäre bei deiner gekürzten Variante nicht möglich bzw. würde IMHO die 2NF verletzen. (das war übrigens ein kleines Projekt nur für meinen Rechner, welches mir nur kurz Informationen über bestimmte Verbindungen sammeln soll; wann welche Verbindung wie viel Traffic pro Stunde erzeugt; das Programm ist quasi im Fließen und eine Erweiterung zu gewissen Informationen je Stunde war mir schon im Kopf)
Bei dbt_Channel wäre deine Variante sehr umständlich (zumal auch wieder das Prblem der Erweiterung steht). Wie gesagt, die ID aus dbt_Channel wird als FK in derzeit 3 weiteren Tabellen verwendet. Dies sieht folgendermaßen aus
Du siehst, ich habe ca. 30 Messgeräte welche ca. 200 verschiedene Messwerte ausspucken (grob geschätzt). Die Messwertbezeichnungen (ID_Channel) können im Laufe der Projektlaufzeit (Jahre) sich immer wieder ändern. Jetzt alle IDs in die MEsswerttabellen zu legen würde ja die Datenbank sprengen. Zumal sich das Problem ja nur verschiebt, denn die dbt_Channel fasst ja wieder nur andere IDS zusammen (deren Erstellung das selbe Problem haben). Es wird dann also quasi rekursiv noch größer. Fazit: Meine Ursprungsidee für den Thread, war es herauszufinden, ob es einen einzigen Befehl für mein Problem gibt (ähnlich Merge into). Dies scheint nicht der Fall zu sein. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:18 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