![]() |
Datenbank: MS-SQL-Server • Version: 2008 R2 • Zugriff über: stored procedure
Array in SQL
Hinweis auf Cross-Posting:
![]() Ja, ich weiß, dass es in SQL keine Arrays gibt. Mein Problem ist folgendes: Ich benötige eine Stored-Procedure, die ca. eine halbe Million Datensätze verarbeitet. Darin enthalten sind verschiedenste String-Operationen, die auf zwei Tabellen (weniger als 50 Datensätze) zurückgreifen, um z.B. Zeichen- und Wort-Ersetzungen durchzuführen. Dies ist für jeden Datensatz der großen Tabelle erforderlich und muss jeden Tag einmal durchgeführt werden (Datenabgleich zwischen mehreren, unabhängigen Systemen). Damit ich auf die kleinen Tabellen zurückgreifen kann, habe ich 2 Cursor mit Scroll-Option definiert. Diese beiden Cursor sind aber mein Flaschenhals und ich würde hier gerne optimieren. Ohne diese Cursor läuft die SP weniger als 30 Sekunden, mit den Cursor fast 10 Minuten. Alle anderen Operationen in der SP habe ich zum Test rausgenommen. Ein Versuch, innerhalb der SP eine Tabelle zu deklarieren und diese als Basis zu nehmen, hat keinen Geschwindigkeitsvorteil gebracht. Ich vermute, das die Datenbank intern eine temporäre Tabelle dafür aufbaut und somit genauso arbeitet, als würde ich die echten Tabellen nutzen. Theoretisch könnte ich mir eine Menge Variablen deklarieren und das ohne Tabellen machen, aber dann müsste ich bei jeder Änderung der Tabellen die SP anpassen. Hat jemand eine Idee, wie das ganze optimiert werden kann? |
AW: Array in SQL
Moin Jasocul,
ich hatte vor einigen Wochen auch das Problem das große Datenmengen in Beziehung gesetzt werden mussten und die Verarbeitung elendig lange gedauert hat. War übrigens SQL2012 Im SQL Management Studio habe ich mir dann mal die Ausführungspläne angesehen, und der Ausführungsplan hat mir Empfehlungen für weitere Indices gegeben. Ich konnte durch den Einsatz eines ORDER(!) die Verarbeitungszeit drastisch senken ... von mehren Minuten auf einige Sekunden! Vielleicht siehst Du dann einen Flaschenhals ... Grüße Rolf |
AW: Array in SQL
Hi roldi,
es werden alle Datensätze aller genutzten Tabellen sequentiell durchlaufen und für den Test habe ich alles deaktiviert, was die Daten verändert. Ein fehlender Index ist ganz sicher nicht das Problem. Ich habe aber jetzt zur Sicherheit noch einen Test-Durchlauf gemacht. - Durchlauf der kann 500.000 Datensätze ohne einen Änderungs-Cursor dauert ca. 15 Sekunden - Mit Aktivierung eines Änderungs-Cursor (ohne irgendwelche weiteren Aktionen) knapp 3 Minuten |
AW: Array in SQL
Du durchläufst 500' Sätze und gehst bei jedem Satz die kleinen (Cursor-) Tabellen durch?
Kannst Du das evtl. anders miteinander verbinden? |
AW: Array in SQL
Leider nicht.
Ich muss in 2-3 Feldern ein Wort-Splitting machen, dabei verschiedene Neben-Bedingungen beachten, bestimmte Wörter ignorieren/ersetzen. Teilweise betrifft das auch nur einzelne Buchstaben. Joins oder ähnliches sind auf diese Weise nicht zu realisieren, da die Inhalte durch die Operationen ja variabel sind. Gebraucht wird das für: Pro Datensatz wird dann je zulässigem Wort in eine neue Tabelle geschrieben/aktualisiert. Diese dient dann der Überprüfung, ob neue, ähnliche oder gleiche Daten bei der nächsten Daten-Lieferung kommen. |
AW: Array in SQL
puh :cry:
|
AW: Array in SQL
Zitat:
|
AW: Array in SQL
Wenn ich das richtig verstanden habe läuft diese Stored Procedure einmal täglich mit einer Dauer von 15 sec. ? Lohnt sich da wirklich der Aufwand stundenlang zu Forschen, zu Proggen und zu Testen ? Oder ist das eine Fingerübung ?
Hat der DB-Server deine volle Aufmerksamkeit (keine anderen Jobs), sind die Parameter der DB so eingestellt, dass er bei bestimmten Operationen (z.B. ORDER BY) keinen Speichermangel hat und kann man vielleicht die Verarbeitung der Daten auf dem Client machen, um den Server zu entlasten. Alles Fragen, die gestellt werden sollten ... |
AW: Array in SQL
Hallo Aviator,
das wäre nur eine sehr theoretische Möglichkeit, da ich selbst keinen Zugriff auf die anderen DB-Systeme habe. So muss ich mit dem leben, was ich bekomme. Zitat:
@TRomano: Die 15 Sekunden erreiche ich nur, wenn ich die beiden Cursor zum Abgleich nicht verwende. Sobald die aktiviert werden, dauert es ca. 10 Minuten. Wenn ich es vom Server auslagere, habe ich ja auch noch eine Schicht mehr. Der Server hat zu dem Zeitpunkt nichts anderes zu tun, außer weitere Jobs durchzuführen, die aber sowieso sequentiell abgearbeitet werden müssen. Allerdings ist das auch der Grund, warum die Performance besser werden sollte. Speicher ist genug da. Im Moment habe ich einen Test-Server, der sehr ähnlich ausgestattet ist, aber wirklich sonst nichts zu tun hat. |
AW: Array in SQL
Zitat:
|
AW: Array in SQL
Ich kenne mich am SQL Server nicht aus und auch dein Programm nicht. Somit kann ich nur raten.
Bitte nicht prügeln wenn die Idee Unsinn ist. Rechnest du die Statistiken und machst du Zugriffsoptimierung über Statistiken. So ich mich ganz düster erinnere war da mal was. Welche Ausführungspläne bei welchem Cursor wie gezogen werden und wie man die traced weiß ich ehrlich nichts mehr. Meine Grundsätzliche Überlegung wäre. 15 Sekunden auf 10 Minuten sind Faktor 40. Das wären so im Mittel 2 Tabellen bei denen die Hälfte der Datensätze durchkämmt wird pro Zugriff. Hat der Cursor zu wenig Einträge oder sind eventuell Statistiken nicht aktuell oder so ähnlich. Scrollable Cursor ist gut für viele Rows, wenn man nachschlagen muss. Kennst du die Anzahl der Zeilen in den Nachschlagetabellen und hilft es wenn man diese fixiert ala static. Das Nachschlagen geht auf die tempdb & KEYSET (musste kurz nachschauen) oder STATIC. Die Frage die sich mir stellt, ob sich die gewonnene Flexibilität durch SCROLL tatsächlich im Mittel auszahlt und ob man nicht auf ReadOnly und ForwardOnly nimmt. Ob diese Variante schneller ist kann ich so nicht beurteilen. Zumal diese Cursor keine echten in memory buffer sind scheint mir die variante einen Stand aus der tempdb zu lesen noch eher die 'beste' Annährung oder eben ein KEYSET usw.. Stellt sich die Frage ob du die tempdb nicht auf ein RAMDRIVE kannst legen. Aber das ist schon etwas gewagt. Die tempdb wird an sich von alle benutzt. Wäre allein interessant zu sehen, ob es einen Unterschied macht zur tempdb auf Platte. Vermute die Datenbankseiten sollten sowieso gepuffert sein. |
AW: Array in SQL
@Aviator:
Kein Problem. Manchmal sind es die einfachen Dinge, die zur Lösung führen, weil man zu kompliziert denkt. @MichaelT: Alle Daten werden sequentiell verarbeitet (alles ohne order by, joins, ...). Die Aktualisierungen natürlich nicht, aber die sind im Test auch nicht aktiviert. Die Statistiken können somit keine Rolle spielen. Aber der Hinweis auf Static hat mich ein bisschen weiter gebracht. Allerdings war es read_only, was mir 2 Minuten Verbesserung gebracht hat. Danke für den Anstoß. Static bringt in diesem Zusammenhang nur ein paar Sekunden. Ich bewege mich also jetzt zwischen 7 und 8 Minuten, was schon deutlich besser ist, aber immer noch relativ lange ist. Weitere Tipps sind willkommen. |
AW: Array in SQL
Bei so wenigen Datensätzen in den Nachschlagetabellen könnte eine Tabellenvariable deutlich schneller sein als eine temporäre Tabelle.
DECLARE @MyTableVar table(ID int, ...) |
AW: Array in SQL
Zitat:
Es gibt dabei keinen zeitlichen Unterschied. Ich habe den Eindruck, dass der Server intern eine temporäre Tabelle dafür anlegt. Ich muss ja auch bei einer Tabellen-Variablen einen Cursor verwenden. Oder gibt es dann andere Möglichkeiten, die ich übersehen habe und die schneller ist? |
AW: Array in SQL
Zitat:
Damit würde sich der Aufwand darauf reduzieren die neu hinzugekommenen oder geänderten Werte zu übersetzen. Je nach Aufwand kann man das durch einen Aufruf deiner SP machen oder direkt bei der Eingabe / Änderung mittels eines Triggers reagieren. cu Ha-Jö |
AW: Array in SQL
Ja, das habe ich natürlich gemacht. :wink:
Aber jeder Hinweis kann helfen. Könnte ja sein, dass ich mich irgendwo verrannt habe. Die Dauer für die erste Erstellung der Abgleich-Tabellen ist ca. eine halbe Stunde. Die tägliche Aktualisierung liegt jetzt bei ca. 7 Minuten. |
AW: Array in SQL
Zitat:
1. In einem anderen Post hast Du geschrieben das es sich um einen Bulk-Insert handelt. Wenn ich richtig vermute liegen die einzufügenden Daten dann in irgendeiner Form extern vor. Wenn es sich bei den einzufügenden Daten (beispielsweise) um Textdateien handelt, dann könntest Du die Umwandlung/Übersetzung doch vor dem Einfügen der Datensätze in bearbeiten. Du könntest Dir ein Programm schreiben das die externen Daten übernimmt, die gewünschten Berechnungen durchführt und dann entweder in die DB schreibt oder ebenfalls wieder extern ablegt. Wenn keine Abhängigkeiten zwischen den einzelnen Datensätzen existieren dann könntest Du die einzufügenden Daten aufteilen und in mehreren Threads bzw. Prozessen, wenn es sein muss sogar über mehrere Rechner verteilt, verarbeiten und nach der Verarbeitung die Originaldaten wieder zusammenfügen oder häpchenweise bearbeiten. 2. Falls die Datensätze aus mehreren Datenquellen kommen und nicht alle gleichzeitig behandelt werden müssen bzw. zu unterschiedlichen Zeiten zur Verfügung stehen, könnte es Sinn machen jede Datenquelle einzeln zu behandeln und das Ergebnis temporär abzuspeichern, und später alles zusammen, also wenn alle Daten vorhanden und umgewandelt sind, zu kopieren. Die Erzeugung der Zwischenergebnisse kann man im Hintergrund laufen lassen so das am Ende nur die Einfügeoperation der bearbeiteten Daten Last erzeugt. 3. (Abwandlung und Kombination von 1 und 2) Falls es mehrere Datenlieferanten auf mehreren Rechnern gibt und meine Annahme mit den externen Dateien richtig ist, wäre es auch eine Möglichkeit das jeder Datenlieferant bzw. jede Maschine vor dem Export der Daten diese selbst aufarbeitet und die aufgearbeiteten Daten an den DB Server liefert. cu Ha-Jö |
AW: Array in SQL
Gerne. Ich habe das nur geschwind aus der Doku nachgeschlagen.
READ ONLY glaube ich sofort. 2 Minuten war nicht der Big Win :-D Gut. Soweit ich die CURSOR Thematik am SQL Server have verstanden. Ich gehe jetzt mal davon aus, dass du den CURSOR nicht so einfach kannst ersetzen... Damit verbleiben jetzt mal 2 Varianten a) Sortieren a1) In der Hoffnung, dass der SQL Server einen Index nimmt oder so b) Sequentiell durchklappern und die wahrscheinlicheren Kombinationen vorne anstellen. b1) Möglw. hilft das vermeiden von der Neuerstellung von Execution Plans beim Absetzen von Queries Möglw. kannst du einen Clustered Index verwenden, je nachdem kann das etwas bringen. Wenn das nichts hilft dann temporary table mit Schlüssel. Die Frage bleibt ob du überhaupt einen Schlüssel hast usw.. Mir kommt aber vor, dass aus irgendeinem Grund bei der Navigation jedes mal ein Statement wird abgesetzt. Selbst im ABAP bei einer unsortierten Tabelle (in memory buffer ohne key) verliert man zwar Zeit aber bei weitem nicht in der Dimension. Das einzige mal bei dem mir Laufzeitunterschiede in der Dimension unterkam (mal von SQL abgesehen) war als der ABAP Prozesser aufgrund von Cluster Gruß Zitat:
|
AW: Array in SQL
Ich habe nicht alle Antworten gescannt. Würde sowas helfen?
SQL-Code:
Hab kein MSSQL im Einsatz, also ist es nur Theorie. Macht nur Sinn, wenn die Werte auf diesem Weg gecached werden, statt immer erneut aus der Originaltabelle gelesen zu werden.
DECLARE @ListOfWord TABLE(aWord VARCHAR(100), aCategoryID VARCHAR(100), aID IDENTITY(1,1)); -- oder so ähnlich
INSERT INTO @ListOfWord SELECT tword, tcat from WordTable; SELECT * FROM @ListOfWord; GO Ansonsten wäre es auch nicht verkehrt, das Statement selbst mal hier zu zeigen oder die Hilfsoperationen. |
AW: Array in SQL
@jobo:
Im Prinzip mache ich es bereits genau so. Das hat aber keinen erkennbaren Unterschied gemacht. @hanvas: Wirkt ein wenig, wie mit Kanonen auf Spatzen, wobei es prinzipiell besser sein würde, näher an der Quelle der Daten zu arbeiten. Ich hatte gestern beim Einschlafen noch eine Idee, die ich heute ausprobieren werde. Ansatz: Ich bastel mir einen String, der einen definiertes Trennzeichen zwischen den Datensätzen und einen anderen zwischen den Wortpaaren hat. Alternativ nehme ich zwei Strings. Über Charindex durchlaufe ich dann die Listen anstatt den Cursor zu durchlaufen. Bin gespannt, wie effektiv das ist. EDIT ERGEBNIS: Die dadurch notwendigen String-Operationen kosten soviel Performance, dass es kaum einen Vorteil bringt. |
AW: Array in SQL
Nach eingehender Untersuchung meiner String-Operationen, konnte ich noch doch noch einiges rauskitzeln.
Von der ursprünglichen Laufzeit von ca. 10 Minuten, bin ich jetzt runter auf unter 6,5 Minuten und ich habe noch einige Stellen gesehen, die potential haben. Der Trick: Es geht ja um Wort- und Buchstaben-Ersetzung mit verschiedenen Nebenbedingungen. Die Bedingungen lasse ich aber jetzt mal weg. Die Austauschtabellen haben insgesamt keine 100 Datensätze, sind also ziemlich kein. Ich habe mir zwei varchar-Variablen und zwei Integer-Variablen deklariert. Über einen Cursor fülle ich varchars mit einem Trennzeichen zwischen den Feldern. Einer enthält die Suchbegriffe und der andere die Ersetzungsbegriffe. Die Integer halten in einer Schleife fest, bei welchem "Index" ich mich befinde. Bestimmt wird das über den Charindex des Trennzeichens. Ich bin mit der Lösung erstmal zufrieden. Jetzt muss ich nur noch schauen, wo ich die String-Operationen optimieren kann. |
AW: Array in SQL
Zeig doch mal die Definition von dem Cursor.
Es gibt da die ein oder andere Möglichkeit so einen Cursor schneller zu machen. ![]() |
AW: Array in SQL
Code:
scroll ist leider zwingend.
declare crsParam1 cursor scroll READ_ONLY for
select Begriff, Ersatz from @ParamTable1 Ich habe auch mit local und static getestet. Die haben aber keinen spürbaren Effekt. |
AW: Array in SQL
Nur so als Versuch
SQL-Code:
Ok, hast du ja schon gemacht ...
declare crsParam1 cursor
LOCAL scroll STATIC READ_ONLY for select Begriff, Ersatz from @ParamTable1 |
AW: Array in SQL
Hi Jasocul,
oder benutze endlich eine Oracle, die kann das :-D Beste Grüße Michael |
AW: Array in SQL
Hi Michael,
Ich leide schon genug. :roll: :wink: |
AW: Array in SQL
Zitat:
Zum Thema Letztlich kostet es halt etwas mehr Zeit 500T x100 Ops zu verarbeiten, als eben nur 500T. Dabei geht es dann vielleicht einfach nur um flinke Suchen&Ersetzen Algos. Ob TSQL sich da mit den Cursorn sturer anstellt als notwendig, kannst Du allerdings nur selbst rausfinden, wenn Du nicht mehr Infos lieferst. P.S.: Gerade noch mal "Index " gelesen. Wenn entsprechend häufige Änderungen wirklich erfolgen, ist der Zeitvergleich auch gerade wegen des Index unfair, weil der auch 500Tx100 worstcase geändert wird. Für den Cursor und die Rohgeschwindigkeit ist er vermutlich eh irrelevant, hast Du schon mal probiert, wie es mit gelöschten Indizex läuft? Kann SQL Server die Indexaktualisierung verzögern bzw. aussetzen? |
AW: Array in SQL
Hast du einen Index auf der 'großen' Tabelle der laufend upgedated wird?
Ok, jobo war flinker. Wobei sich sofort die Frage stellt. Wieviele Datensätze Altbestand werden angegriffen bei der Verarbeitung usw... Dann mal einen Test mit der doppelten bis 4 fachen Datenmenge machen. |
AW: Array in SQL
Jedes DBMS hat Vor- und Nachteile. Ich komme halt ursprünglich aus der Oracle-Ecke.
Die Zieltabelle hat zwar einen Index, aber das ist kein Problem. Ohne Index würde ich vermutlich Stunden warten. Die Zieltabelle hat etwa 1,3 Mio Datensätze und ist definitiv nicht der Flaschenhals. Das weiß ich deswegen, weil ich alle Aktionen auf dieser Tabelle für die Tests deaktiviert habe. Auf die Tabelle, die mir die zu prüfenden Datensätze liefert greife ich sequentiell zu. Die Reihenfolge spielt keine Rolle und es ist nur ein lesender Zugriff. Die Tabelle für den Abgleich lese ich ja inzwischen nur noch einmal ein, damit ich mein neues Verfahren (nenne ich es mal Pseudo-Array-Verfahren) nutzen kann. Dieses Verfahren hat ja schon etwas gebracht, allerdings bremst mich die Funktion Charindex jetzt erheblich aus, die ich dann doch ein paar mal mehr benötige, als erwartet. Hier mal der Ausschnitt, der mich ausbremst (ist SQL-Code, kein Delphi-Code):
Delphi-Quellcode:
Das ist nur C&P und ein bisschen bereinigt. Ich hoffe, es ist lesbar.
while @@FETCH_STATUS = 0
begin -- Aufbereitung des Namens für die weitere Verarbeitung set @Str = @Name1 set @Str = ' ' + lower(@Str) + ' ' -- Alles in Kleinbuchstaben set @Ort = lower(@Ort) set @WortPos1 = 0 set @WortPos2 = 0 -- Abgleichliste durchlaufen while @WortPos1 < LEN(@WortList1) begin set @Pos = CHARINDEX(@Delimiter, @WortList1, @WortPos1 + 1) set @Wort1 = Substring(@WortList1, @WortPos1 + 1, @Pos - @WortPos1 - 1) if CHARINDEX(@Wort1, @Str) > 0 begin set @Pos = CHARINDEX(@Delimiter, @WortList2, @WortPos2 + 1) set @Wort2 = Substring(@WortList2, @WortPos2 + 1, @Pos - @WortPos2 - 1) end if Len(@Wort1) = 1 begin if CHARINDEX(@Wort1, @Ort) > 0 begin set @Pos = CHARINDEX(@Delimiter, @WortList2, @WortPos2 + 1) set @Wort2 = Substring(@WortList2, @WortPos2 + 1, @Pos - @WortPos2 - 1) set @Ort = replace(@Ort, @Wort1, @Wort2) end end set @WortPos1 = charindex(@Delimiter, @WortList1, @WortPos1+1) set @WortPos2 = charindex(@Delimiter, @WortList2, @WortPos2+1) end fetch next from crsDeb into @Name1, @Ort end Wie man sieht, benötige ich oft charindex. Pro 10.000 Datensätze des Haupt-Cursors, verursacht charindex eine Sekunde Zeit. Ich habe auch schon probiert, es durch ein "Like" zu ersetzen (wo es theoretisch ginge), aber ohne Effekt. Vielleicht kann man auch einfach nicht mehr rausholen. :gruebel: Meine Birne raucht, ich mache jetzt erst mal Feierabend. |
AW: Array in SQL
Sooo, eine Nacht drüber geschlafen und Thema erledigt.
Man kann sicher noch ein paar Sekunden rauskitzeln, aber bei den Datenmengen und den notwendigen Vergleichen, gibt es einfach Grenzen. Also habe ich einen neuen Ansatz gemacht, und die zu prüfende Datenmenge reduziert. Statt der halben Mio Datensätze, sind es dadurch zur Zeit nur noch wenige hundert. Selbst wenn es ein paar tausend werden, dauert der Abgleich nur noch ein paar Sekunden. Trotzdem Danke an alle, die sich mit mir den Kopf zerbrochen haben. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:00 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