![]() |
Datenbank: mssql • Version: 2008 • Zugriff über: unidac
Daten per SQL gruppieren
Hallo zusammen,
ich habe folgende Daten
Code:
und möchte sie so gruppieren, dass ich folgende Ausgabe bekommen
SerNr Typ Status
F05323690 MLC10U100 S F05323691 MLC10U100 S F05323692 MLC10U100 R F05323693 MLC10U100 S F05323694 MLC10U100 S
Code:
Mit einer einfachen
Min(SerNr) Typ Status Anzahl
F05323690 MLC10U100 S 2 F05323692 MLC10U100 R 1 F05323693 MLC10U100 S 2
Delphi-Quellcode:
sähe das Ergebnis so aus
SELECT min(SerNr), Typ, Status, count(SerNr)
FROM Ser_nr WHERE SerNr BETWEEN 'F05323690' AND 'F05323695' group by Typ, Status order by min(SerNr)
Code:
Hintergrund ist, dass ein Typenschldprogramm die Daten so übergeben bekommt.
Min(SerNr) Typ Status Anzahl
F05323690 MLC10U100 S 4 F05323692 MLC10U100 R 1 Im 1. Fall würde es die Serien-Nr.
Im 2. - falschen Fall - würde es die Serien-Nr.
Hat einer einen Tip Danke Gerd |
AW: Daten per SQL gruppieren
Zitat:
Sowohl in der gewünschten als auch in der falschen Ausgabe taucht die F05323694 nicht auf, und wird darum auch nicht gedruckt. Gruß K-H |
AW: Daten per SQL gruppieren
Ich glaube, der Drucker bekommt eine Seriennummer und eine Anzahl und zählt die Seriennummer dann automatisch hoch beim Drucken.
Zum Problem: In SQL ist die Reihenfolge der Zeilen nicht definiert. Dein Tool zeigt dir das zwar meistens reproduzierbar nett an, aber du darfst dich darauf nicht verlassen, dass das reproduzierbar ist. Demzufolge kannst du per group-by auch nicht nur "aufeinanderfolgende Zeilen" gruppieren. Lösung wäre eine extra Spalte, in die man für (aufeinanderfolgende Seriennummern/gleicher Typ/gleicher Status) Kombinationen die gleiche Zahl rein schreibt. Dann einfach nur nach dieser Spalte gruppieren. Oder du holst dir alle Datensätze nach Seriennummer sortiert und machst diesen Schritt im SQL Client. |
AW: Daten per SQL gruppieren
Zitat:
"Mein Tool" das die Daten anzeigt ist schon ein SQL und was mir fehlt ist ein Kommando das erkennt wenn innerhalb einer Gruppe ein "schwarzes Schaf" ist das die Gruppe unterbricht. In der Realität gibt es auch die Serien-Nr. F05323692 mit Status S in der DB. Viele Datensätze später gibt es dieselbe Serien-Nr. aber auch mit Status R und über das Unter-SQL suche ich zuerst mal den letzten und damit aktuelle gültigen Eintrag zu einer Serien-Nr. Das SQL für die die erste Tabelle sieht in Wirklichkeit so aus
Code:
SELECT s.sernr, s.Typ, s.[Status]
FROM Ser_nr s INNER JOIN (SELECT S1.SerNr, Max(S1.ID) AS MaxID FROM Ser_Nr AS S1 WHERE s1.SerNr BETWEEN 'F05323690' AND 'F05323694' GROUP BY S1.SerNr) AS S3 ON s.ID = S3.MaxID order by s.SerNr |
AW: Daten per SQL gruppieren
Also eine Lösung wäre ja auch, das Feature mit dem automatischen Hochzählen nicht zu benutzen und alle Datensätze einzeln zum Drucker zu schicken. :stupid:
|
AW: Daten per SQL gruppieren
Joar, also entweder eine WindowFnction schreiben, die bei Unterschied um 1 hoch zählt
das alles manuell in einer Stored Proc lösen oder erstmal im Hauptselect die Ersten raussuchen (Alles, wo direkt vor sich etwas Unterschiedliches liegt und dann z.B. in einem rekursiven Select alles suchen, wo davor etwas Gleiches liegt und das an den er jeweils ersten Datensatz dran joinen. oder es alt Clientseitig machen |
AW: Daten per SQL gruppieren
Zitat:
Nachteil: Die Typenschilder sind auf Endlosmaterial und werden vom Drucker geschnitten. Erzeugt man für jeden Nr. einen Eintrag, wird jeder Eintrag einzeln bearbeitet, d.h. die Nr wird gedruckt, rausgeschoben, abgeschnitten, wieder zurück gezogen und das nächste gedruckt. Übergiebt man ein Band, wird das komplette Band fortflaufend gedruckt und geschnitten und erst bei einem neuen Band (oder Einzeleintrag) zurückgezogen. Und manchmal bis zu 2000 Typenschilder gedruckt werden und die in aller Regel als Band vorliegen, ist die Einzelübergabe ungünstig. Bisher habe ich das so realsiert, dass ich Delphi durch das SQL Ergebniss laufen und gruppieren lasse. Da jetzt ein neues Labelprogramm mit anderem Befehlsaufbau dazu kommt, wollte ich den Job komplett in MsSQL auslagen Aber wenns nicht geht mach ich eben weiter wie bisher. Danke trotzdem. |
AW: Daten per SQL gruppieren
Wie übergibst du das denn an den Drucker?
Nur weil du das im Programm "einzeln" verarbeitest, muß es auch nicht einzeln zum Drucker. Du kannst das ja sammeln und dann gemeinsam zum Drucker schicken. Falls die Schnittstelle das DataSet entgegen nimmt, dann entweder die Daten holen und in ein Memory-DataSet umkopieren oder z.B. CachedUpdates=True und dann das DataSet bearbeiten (Edit/Delete) bevor es an den Drucker geht. (natürlich hinteher das CancelUpdates nicht vergessen) |
AW: Daten per SQL gruppieren
Moin...8-)
Zitat:
|
AW: Daten per SQL gruppieren
Man muss ein Zwischenergebnis hinbekommen, dass es ermöglicht einer gewünschten Gruppe von Werten ein eindeutiges Gruppierungsmerkmal zuzuordnen.
Ist das gelungen, dann ist der Rest einfach Standard Group by.
Code:
Das Inner Select erzeugt die Unterscheidung der Gruppe über das "Detektieren" einer Wertänderung von Status und abhängig davon dann die Auswahl eines eindeutigen Gruppenmerkmals, hier die schon vorhandene oder vorige SerNr.
select GroupedSerNr, Typ, Status, Count(*)
from ( SELECT SerNr, Typ, Status, case when LAG(status,1,'R') OVER(ORDER BY SerNR) = status then LAG(SerNR) OVER(ORDER BY SerNR) else serNr end AS GroupedSerNr FROM daten WHERE SerNr BETWEEN 'F05323690' AND 'F05323695' ) x group by GroupedSerNr, Typ, Status order by GroupedSerNr Offset und Defaultangabe von Lag(Status...) muss ggF. angepasst werden. |
AW: Daten per SQL gruppieren
Zitat:
Übergabe sähe schematisch einmal so aus
Code:
und einmal so
Anzahl = 2
AbSerNr, Typ, Status 123456, Musteryp, S Anzahl = 1 AbSerNr, Typ, Status 123458, Musteryp, R Anzahl = 2 AbSerNr, Typ, Status 123459, Musteryp, S
Code:
Wie schon gesagt, bei dem Beispiel wäre die zweite Variante durchaus vertretbar. Druckt man aber viele Typenschilder, nervt das hin und hergeziehe und führt auch gerne zu Falten am Thermotransferband
AbSerNr, Typ
123456, Musteryp, S 123457, Musteryp, S 123458, Musteryp, R 123459, Musteryp, S 123460, Musteryp, S Mit dem SQL von Jobo sieht das Ergebnis so aus
Code:
entspricht also auch nicht dem was ich wollte. Ich schau mir aber mal die Funktion LAG an. Die kenn ich nämlich noch nicht
F05323690 MLC10U100 S 2
F05323691 MLC10U100 S 1 F05323692 MLC10U100 R 2 F05323693 MLC10U100 S 2 F05323694 MLC10U100 S 1 |
AW: Daten per SQL gruppieren
Mein Ergebnis
mit diesem Select Zitat:
sieht so aus Zitat:
![]() |
AW: Daten per SQL gruppieren
Hallo Jo,
ich denke die Ursache ist einfach zu erkären. Ich denke du gehst davon aus, dass es F05323692 nur mit Status R gibt. Real gibt es ihn mind. zwei mal. Den ursprünglichen Datensatz bei Auslieferung mit Status S und dann ein- oder mehrfach als Repaartur mit Status R. Beachtet werden soll aber nur der letzte, aktuellste Datensatz Ändere ich das SQL auf
Code:
funktioniert es mit dem SQL auch.
SELECT SerNr, Typ, Status,
case when LAG(status,1,'R') OVER(ORDER BY SerNR) = status then LAG(SerNR) OVER(ORDER BY SerNR) else serNr end AS GroupedSerNr FROM (select * from Ser_nr where ID in (SELECT Max(S1.ID) AS MaxID FROM Ser_Nr AS S1 WHERE s1.SerNr BETWEEN 'F05323690' AND 'F05323694' GROUP BY S1.SerNr)) y ) x group by GroupedSerNr, Typ, Status order by GroupedSerNr
Code:
Erweitere ich aber den Ser-Nr. Bereich z.B. auf BETWEEN 'F05323690' AND 'F05323700' sieht das Ergebnis so aus
F05323690 MLC10U100 S 2
F05323692 MLC10U100 R 1 F05323693 MLC10U100 S 2
Code:
Korrekt wäre
F05323690 MLC10U100 S 2
F05323692 MLC10U100 R 1 F05323693 MLC10U100 S 2 F05323694 MLC10U100 S 1 F05323695 MLC10U100 S 1 F05323696 MLC10U100 S 1 F05323697 MLC10U100 S 1 F05323698 MLC10U100 S 1 F05323699 MLC10U100 S 1 F05323700 MLC10U100 S 1
Code:
F05323690 MLC10U100 S 2
F05323692 MLC10U100 R 1 F05323693 MLC10U100 S 8 |
AW: Daten per SQL gruppieren
Zitat:
U.U. ist es möglich über die ID eine zeitliche Abfolge zu rekonstruieren, aber solche Ergebnisse über Bande sind meiner Meinung nach nicht sehr zuverlässig. Gruß K-H |
AW: Daten per SQL gruppieren
Zitat:
Dass die Erweiterung auf größere Folgen nicht klappt war mir nicht aufgefallen. Blöd. Dazu fällt mir spontan auch keine Lösung ein, weil es nach meinem Kenntnisstand keine Konstruktion in SQL Server gibt, die eine autarke Wertefolge unabhängig von irgendwelchen Randbedingungen produziert. Also auf Deutsch eine normale Sequenz gibt es leider nicht. Damit wär es m.E. gelöst. Wahrscheinlich kann man auch mit rekursiven Queries was machen oder irgendwelchen Workarounds dafür, aber dazu hatte ich noch keine Lust. |
AW: Daten per SQL gruppieren
Himitsu hat doch eigentlich den richtigen Ansatz schon gesagt.
Eine Function sollte das Problem relativ einfach beheben. Ich habe mal was auf die Schnelle gebastelt:
Code:
Ich habe das jetzt ohne Parameter gemacht. Daher fehlt im Cursor noch die Einschränkung auf den gewünschten Bereich.
CREATE FUNCTION dbo.f_SerNr (
) RETURNS @Etiketten TABLE ( SerNr varchar(10), Typ varchar(10), Status char(1), Anzahl int ) AS BEGIN declare @SerNr varchar(10), @SerNr_1 varchar(10), @Typ varchar(10), @Status char(1), @Typ_1 varchar(10), @Status_1 char(1), @Anzahl int declare SN_Cursor Cursor for select * from Ser_Nr order by SerNr; open SN_Cursor fetch next from SN_Cursor into @SerNr, @Typ, @Status set @Anzahl = 1 while @@FETCH_STATUS = 0 begin fetch next from SN_Cursor into @SerNr_1, @Typ_1, @Status_1 if (@Typ = @Typ_1) and (@Status = @Status_1) and (@@FETCH_STATUS = 0) BEGIN set @Anzahl = @Anzahl + 1 end else begin insert @Etiketten values (@SerNr, @Typ, @Status, @Anzahl) set @SerNr = @SerNr_1 set @Typ = @Typ_1 set @Status = @Status_1 set @Anzahl = 1 end end close SN_Cursor; DeAllocate SN_Cursor; return end Ich denke, dass die paar Ergänzungen aber kein Problem darstellen sollten. |
AW: Daten per SQL gruppieren
Zitat:
Ein Select würde ich immer einer SP vorziehen, wenn es geht und performant genug ist. Daher war das Select Statement mein Ansatz. Da ja auch ebenso auch eine clientseitige Lösung im Raum steht, mit der man es analog zu einer SP machen kann, habe ich das nicht betrachtet. Ein elegantes Select reizt mich da mehr. Dass einem dabei offenbar die Einschänkungen der SQL Server Sequenzimplemntierung in die Hacken laufen, habe ich nicht geahnt. |
AW: Daten per SQL gruppieren
Zitat:
Ein schönes SQL bevorzuge ich auch, aber es gibt Grenzen. Zumal im professionellem Umfeld auch Zeit eine große Rolle spielt. Zitat:
Die kann dir sowas liefern. Allerdings ist die auf 2048 Werte beschränkt. Der Thread-Ersteller schrieb, dass das bis zu 2000 Einträge werden können und ist damit dicht an der Grenze. Ich habe die Tabelle auch schon genutzt mit einen fortlaufenden Datumswert zu generieren. Beispiel:
Code:
Vielleicht kannst du damit ja noch was elegantes zaubern :wink:
SELECT DATEADD(Day, Number, '01.' + CAST(MONTH(@StartDatum) AS VARCHAR) + '.' + CAST(YEAR(@StartDatum) AS VARCHAR)) as Datum
FROM master..spt_values WHERE Type='P' AND DATEADD(day, Number, @StartDatum) <= @EndeDatum |
AW: Daten per SQL gruppieren
Ok, SP oder UDF, unter dem Aspekt der Wartbarkeit, Komplexität, wie gesagt lieber SQL-natürlich performant, das wäre ja mit Window Functions erstmal zu erwarten.
Die SPT_VALUES ist ja offenbar auch fragwürdig. Zu "elegant" könnte man ja auch "dokumentiert" und "zuverlässig" zählen. Aber so eine alte Sybase System Tabelle, die knapp am Rangelimit ist, kann man nicht wirklich elegant nennen. Dann lieber Deine Lösung. |
AW: Daten per SQL gruppieren
Ich würde eine Lösung über den Delphi Client bevorzugen, da SQL von Haus aus für Mengen und nicht für (sortierte) Folgen gemacht ist. Im Client kann ich die Abfolge der Daten problemlos berücksichtigen. das Problem jetzt mit SQL zu lösen mag zwar reizvoll sein, aber meiner Meinung nach ist das ineffizient.
Gruß K-H |
AW: Daten per SQL gruppieren
Zitat:
Wenn ich in Folge sowohl im SQL, einer SP/UDF oder im Client auf die Cursor / Loop Geschichte verzichten kann, ist es auch ein Pluspunkt (effizientere Entwicklung, weniger Resourcenverbrauch) Wenn leider die Findung eines effizienten Statements an gewisser Funktionalität (oder Kenntnismangel) scheitert, ist man am Ende leider eben doch bei der Standardlösung. Nach meinen lauen Erfahrungen ist MS mit den Window Functions leider etwas hinterher. Der 2008er Server hat etwas aufgeholt, der 2012 kann noch deutlich mehr, wird aber hier nicht eingesetzt. Die Sequenz Thematik (die m.E. in diesem und anderen Bereichen eine weitere Steigerung böte/ Notwendigkeit ist), ist dann hier leider noch ein weiterer Sargnagel. |
AW: Daten per SQL gruppieren
Zitat:
1) SELECT auf alle Anfangs-Datensätze * über einen JOIN auf IS NULL oder als Subselect mit NOT EXISTS * alles auflisten, wo es keinen "zugehörigen" Datensatz mit AbSerNr-1 gibt 2) und dann über ein rekursives SELECT alle nachfolgenden Datensätze suchen und dranjoinen (frag mich bitte nicht wie, denn mit dem rekursiven steh ich auf Kriegsfuß und bei deinem DBMS ist das bestimmt eh anders, da ich es bissher nur im PostgreSql gemacht hatte) oder jeweils den "nächsen" Anfangsdatensatz suchen (alle Anfangs-Datensätze die eine größere AbSerNr haben, das dann ORDER BY und LIMIT 1) und über ein BETWEEN dann alle dazwischen liegenden AbSerNr dranjoinen hier bekommen alle zusammengehörigen Datensasätze als GroupID z.B. die AbSerNr des jeweiligen Anfangs-Datensatzes 3) Und zum Schluss dann GROUP BY und Count() drüber |
AW: Daten per SQL gruppieren
Zitat:
Der Vorschlag von jasucol mit einer alternativen Datenquelle für "generische" Sequenzen wäre da noch naheliegender, das ist in meinem Statement eingebaut übersichtlich und meinetwegen auch zukunftssicher, weil diese olle Systemtabelle so tief "mit drin" hängt, dass sie nie abgelöst wird. Aber dann ist da noch das 2041 Limit, was es hier konkret grenzwertig macht. (Dann noch eher eine eigene Tabelle die Zahlen von 1 bis 10000 enthält - eben genug jedenfalls, und man wäre auf der sicheren Seite. Die kann man auch an anderer Stelle immer gebrauchen, wenn man keine vernünftigen Sequenzen hat, dann würde es noch mehr Sinn machen) Recursive Aufrufe in 2008 weiß ich grad auch nicht auswendig, aber an der Stelle fliegt man bei MS SQL 2008 oder niedriger auch gern auf die Nase. Da ist die gezeigte UDF dann schon ziemlich straight forward und sicher auch relativ flott. Oder man schreibt sie nochmal clientsetig um in Delphi, wie p80286 es machen würde. Das sind alles so Themen, die im Grunde eigentlich interessant sind, wenn Leute hier aufschlagen und wissen wollen, welchen Server soll ich nehmen (siehe neulich). Bei MS stelle ich im DB Bereich leider oft fest, dass Standards oder Quasi Standards nicht vorhanden / umsetzbar / adaptierbar sind. Bei den Sequenzen bspw fürchte ich fast, die Einschränkungen, die es dort gibt, sind nicht technischer Natur, sondern sollen den Anwender vor irgendwas schützen. Ich weiß nur nicht wovor. |
AW: Daten per SQL gruppieren
Irgendwie hat es mich gereizt.
Keine Ahnung ob brauchbar, getestet mit FireBird, dürfte aber auf andere Datenbanken übersetzbar sein.
SQL-Code:
Ausgehend von meinen Testversuchen, sollte das Ganze nicht nur bei 'nem Statuswechsel funktionieren, sondern auch bei gleichem Status aber wechselndem Typ.
select SerNr, Typ, Status, Anzahl from
( select SerNr, BisNr, Typ, Status, Cast(SubString(BisNr from 2 for 10) as Integer) - Cast(SubString(SerNr from 2 for 10) as Integer) As Anzahl from ( select Min(SerNr) As SerNr, BisNr, Typ, Status from ( select SerNr, Typ, Status, /* Unter Oracle wäre das nvl, bei MSSQL dürfte es IsNull sein. */ /* Beim letzten Satz fehlt uns die BisNr für die Anzahlberechnung. */ /* Daher wird hier die folgende "Krücke" benötigt. */ /* Schön und / oder gar elegant ist was vollkommen anderes, */ /* und durch das SubString-Gewusel wird das nícht unbedingt pflegeleicht */ /* oder gar (welch Graus) auf andere SerNr-Arten anpassbar. */ Coalesce((select min(b.SerNr) from norwegen60 b where a.SerNr < b.SerNr and ( a.Status <> b.Status or a.Typ <> b.Typ ) ), /* LPAD kennt MSSQL nicht. */ /* Da müsste man wohl mit sowas in der Art "rumkaspern". /* right(replicate('0',8) + cast(cast(SubString(max(SerNr) from 2 for 10) as Integer) + 1 as varchar(15)),8) */ (select 'F'||lpad(cast(SubString(max(SerNr) from 2 for 10) as Integer) + 1,8,'0') from norwegen60) ) as BisNr from norwegen60 a where SerNr Between 'F05323690' and 'F05323695' ) group by BisNr, Typ, Status ) ) order by SerNr, Typ; Sollte die SerNr auch mit anderen Buchstaben beginnen oder gar am Anfang mehr oder weniger viele Buchstaben enthalten, dann wird das Ganze nicht mehr wirklich witzig. Bei ca. 10000 Sätzen in der Ausgangstabelle und ca. 3200 Sätzen im Ergebnis, benötigt das SQL ca. 4 Sekunden. Hardware: Ein sehr in die Jahre gekommener Laptop (D810 von Dell) Viel Vergnügen beim Testen und Auseinanderpflücken ;-) |
AW: Daten per SQL gruppieren
Hallo zusammen,
Danke für die vielen Inputs. Wenn ich Zeit habe, werde ich mir den Ansatz von Naphet mal anschauen. Es hätte mich gereizt, aber da ich im Moment den neuen Treiber zum Laufen bringen muss werde ich die Gruppierung in Delphi belassen. Sie funktioniert ja bereits. Es kommen nämlich noch genau die Probleme dazu die auch erwähnt wurden.
Danke trotzdem Gerd |
AW: Daten per SQL gruppieren
Um das SQL auch auf andere Seriennummerntypen anwendbar zu machen, schlage ich vor, die Seriennummer in den konstanten Teil (in den Beispieldaten das F) und den nummerischen Teil, also die aufsteigende Nummer, in der Tabelle / Datenbank zu unterteilen. Das macht die weitere Verarbeitung deutlich einfacher, erfordert aber eine Datenmigration mit allen erforderlichen Nacharbeiten in vorhandenen Routinen.
Das von oben übernommene SQL könnte dann in etwa so aussehen (ungetestet):
SQL-Code:
select
Concat(SerConst,Replicate('0', 8 - Len(SerNr)) + SerNr) As SerNr, Typ, Status, WeiteresFeld1, /* ... */ WeiteredFeldX, Anzahl from ( select SerConst, SerNr, BisNr, Typ, Status, WeiteresFeld1, /* ... */ WeiteresFeldX, (BisNr - SerNr) As Anzahl from ( select SerConst, Min(SerNr) As SerNr, BisNr, Typ, Status, WeiteresFeld1, /* ... */ WeiteresFeldX from ( select SerConst, SerNr, Typ, Status, WeiteresFeld1, /* ... */ WeiteresFeldX, IsNull((select min(b.SerNr) from norwegen60 b where a.SerConst = b.SerConst and a.SerNr < b.SerNr and ( a.Status <> b.Status or a.Typ <> b.Typ or a.WeiteresFeld1 <> or b.WeiteresFeld1 /* ... */ or a.WeiteresFeldX <> or b.WeiteresFeldX ) ), (select Concat(SerConst,Replicate('0', 8 - Len(Max(SerNr))) + Max(SerNr)) As SerNr from norwegen60) ) as BisNr from norwegen60 a where SerConst = 'F' and SerNr Between 5323690 and 5323695 /* Parametrisiert: */ /* where SerConst = :ParamSerConst and SerNr Between :ParamStartSerNr and :ParamEndeSerNr */ ) group by SerConst, BisNr, Typ, Status, WeiteresFeld1, /* ... */ WeiteresFeldX ) ) order by 1, 2, 3, 4, 5 /* , ... NrLetztesFeldImSelect */; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:43 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-2025 by Thomas Breitkreuz