Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Array mit DB Ergebnismenge vergleichen (https://www.delphipraxis.net/196352-array-mit-db-ergebnismenge-vergleichen.html)

rokli 15. Mai 2018 12:33

Array mit DB Ergebnismenge vergleichen
 
Hallo zusammen,

ich benötige mal einen Denkanstoß: Ich habe ein Array, dessen Inhalt in die WHERE - Clause eines SQL wandert. Danach möchte ich die Ergebnismenge mit dem Ausgangsarry vergleichen. Damit will ich feststellen, welche Werte des Array nicht im Ergebnis des SQL enthalten ist/sind.

Beispiel:

Delphi-Quellcode:
a[1] := 'Peter';
a[2] := 'Klaus';
a[3] := 'Paul';
a[4] := 'Theo';
a[5] := 'Bernd';
Daraus folgt die SQL Abfrage:

Code:
SELECT
   NAME
   ,COUNT(*) AS Anz
FROM
   Tabelle
WHERE
   NAME IN ('Peter', 'Klaus', 'Paul', 'Theo', 'Bernd')
GROUP BY
   NAME
ORDER BY
   Anz ASC
Die Ergebnismenge soll mal sein:

NAME, Anz
Klaus, 10
Bernd, 8
Peter, 5
Theo, 5


Klar ist jetzt: Ich suche 'Paul', weil er in der Ergebnismenge nicht vorhanden ist.

So, nun könnte ich Zeile 1 der DB nehmen und mit einer Schleife durch das Array laufen und fragen: Tabelle.Name = a[i] und entsprechend handeln.
So, nun könnte ich Zeile 2 der DB nehmen und mit einer Schleife durch das Array laufen und fragen: Tabelle.Name = a[i] und entsprechend handeln.
So, nun könnte ich Zeile 3 der DB nehmen und mit einer Schleife durch das Array laufen und fragen: Tabelle.Name = a[i] und entsprechend handeln.

Und hier frage ich mich, ob das auch eleganter zu lösen ist oder seht Ihr einen anderen Weg diesen Abgleich durchzuführen?

Danke für Tipps!

Beste Grüße

mischerr 15. Mai 2018 13:24

AW: Array mit DB Ergebnismenge vergleichen
 
Hallo,

verwende statt des Arrays eine TStringList oder TList<string>.
Durchlaufe die Ergebnismenge und lösche jeden gefundenen Namen aus der Liste.
Was übrig bleibt darfst du behalten. ;-)

Jumpy 15. Mai 2018 13:28

AW: Array mit DB Ergebnismenge vergleichen
 
In Oracle könnte man das mit Hilfe der Dummy-Tabelle DUAL so lösen:

SQL-Code:
Select * From (
SELECT 'PAUL' as Name, (Select count(*) From Tabelle Where Name='PAUL') as ANZAHL FROM DUAL
UNION ALL SELECT 'KLAUS' as Name, (Select count(*) From Tabelle Where Name='KLAUS') as ANZAHL FROM DUAL
UNION ALL SELECT 'PETER' as Name, (Select count(*) From Tabelle Where Name='PETER') as ANZAHL FROM DUAL
-- usw.
) Where ANZAHL=0

rokli 15. Mai 2018 13:45

AW: Array mit DB Ergebnismenge vergleichen
 
Hallo Jumpy,

danke, verwende allerdings MS SQL! Hatte mal was mit "HAVING" probiert, geht aber nicht, weil aus der Abfrage ja kein <NULL> Satz hervorgeht, sondern <gar kein> Satz.

Hallo Mischerr,

das hat was! Ich kann gar nicht nach denen suchen kann, die NICHT da sind, sondern nur die ausschließen, die vorhanden sind! Very cool - Danke.

Jumpy 15. Mai 2018 14:03

AW: Array mit DB Ergebnismenge vergleichen
 
Die MSSQL-Version wäre dann:

SQL-Code:
SELECT 'PAUL' as Name Where (Select count(*) From Tabelle Where Name='PAUL') = 0
UNION SELECT SELECT 'KLAUS' as Name Where (Select count(*) From Tabelle Where Name='KLAUS') = 0
UNION SELECT SELECT 'PETER' as Name Where (Select count(*) From Tabelle Where Name='PETER') = 0
Da sollten dann nur die Einträge ohne Namen rauskommen

rokli 15. Mai 2018 15:34

AW: Array mit DB Ergebnismenge vergleichen
 
Hi Jumpy,

ich hatte das mit der "DUAL" Tabelle nicht verstanden. Aber Du hast Recht!

In der IBM DB2 (die habe ich gerade im Zugriff) Lösung sieht das dann so aus:

Code:
SELECT 'PAUL' AS Name FROM sysibm.sysdummy1 WHERE (SELECT COUNT(*) FROM Tabelle WHERE VARI = 'PAUL') = 0
UNION
SELECT 'KLAUS' AS Name FROM sysibm.sysdummy1 WHERE (SELECT COUNT(*) FROM Tabelle WHERE VARI = 'KLAUS') = 0
UNION
SELECT 'PETER' AS Name FROM sysibm.sysdummy1 WHERE (SELECT COUNT(*) FROM Tabelle WHERE VARI = 'PETER') = 0
und bringt die Namen raus, die eigentlich gar nicht vorhanden sind!

Dank und Gruß

himitsu 15. Mai 2018 16:10

AW: Array mit DB Ergebnismenge vergleichen
 
Viele DBMS können mit Array-Parametern umgehen und es gibt DB-Zugriffskomponenten, welche das ebenfalls durchreichen können.
SQL-Code:
SELECT Name, COUNT(*) AS Anz
FROM Tabelle
WHERE Name IN :ArrayParameter
GROUP BY Name
ORDER BY Anz ASC

-- SQL.ParamByName('ArrayParameter').ArrayIrgendwas[0] := 'PAUL'; ...
-- SQL.ParamByName('ArrayParameter').ArrayIrgendwas := ['PAUL', 'KLAUS', ...];
-- SQL.ParamByName('ArrayParameter').SetArrayIrgendwas(['PAUL', 'KLAUS', ...]);
-- oder SonstWie
Leider geht das nicht überall, aber da gibt es auch einen Trick.
* den ganze SQL-String manuell zusammensetzen
* oder Makros und nur diesen einen Teil selber basteln
SQL-Code:
SELECT Name, COUNT(*) AS Anz
FROM Tabelle
WHERE Name IN (&ArrayMacro)
GROUP BY Name
ORDER BY Anz ASC

-- SQL.MacroByName('ArrayMacro').Value := 'a,b,c'; // siehe (1)
1) Ich hab das bisher vorallem mit Integern gemacht, da ist es extrem einfach über eine ArrayToString-Funktion aus dem Array
Delphi-Quellcode:
[1,2,3]
einen
Delphi-Quellcode:
'1,2,3'
String zu generieren, ohne auf das Quoting achten zu müssen.
Für Strings sollte man hier "unbedingt" die Quote-Funktion der DB-Zugriffskomponenten verwenden und damit den String erzeugen.
also sowas wie http://php.net/manual/de/function.my...ape-string.php
bzw. quote_name, quote_value, quote_literal oder z.b. TPgTextConverter.EscapeString (PgDAC).
Delphi-Quellcode:
'''PAUL'', ''KLAUS'', ...'

Delphi-Quellcode:
S := '';
for i := 0 to High(DeinArray) do begin
  if S <> '' then S := S + ', ';
  S := S + DBQuoteValueFunction(DeinArray[i]);
end;

PS: https://modern-sql.com/de/anwendung/select-ohne-from
SQL-Code:
--SELECT n
--FROM (VALUES ('Peter'), ('Klaus'), ('Paul'), ('Theo'), ('Bernd')) AS t(n)
--LEFT JOIN tabelle ON name = n
--WHERE ... weiß ich grade nix ... halt irgendwas mit count()=0 oder so

SELECT n
FROM (VALUES ('Peter'), ('Klaus'), ('Paul'), ('Theo'), ('Bernd')) AS t(n)
WHERE not exists(SELECT * FROM tabelle WHERE upper(name) = upper(n))

Delphi.Narium 15. Mai 2018 16:27

AW: Array mit DB Ergebnismenge vergleichen
 
Also die Idee mit der Tabelle Dual von Oracle finde ich gut.

Da ich aber überwiegend mit FireBird arbeite, lege ich mir dort in jeder Datenbank die Tabelle Dual an und schon kann ich alles so lösen, wie ich es von Oracle gewohnt bin. (Und brauche mir bei der Portierung von DB-System A nach DB-System B keinen Kopp machen, ob die SQLs unverändert übernommen werden können oder nicht ;-))

Was spricht dagegen, diese Lösung auch bei anderen Datenbanken einzusetzen?
SQL-Code:
create table dual(dummy char(1));
insert into dual (dummy) values ('X');
commit;
(Mehr ist Dual unter Oracle auch nicht ;-))

Uwe Raabe 15. Mai 2018 16:31

AW: Array mit DB Ergebnismenge vergleichen
 
Bei MSSQL sollte auch folgendes Statement alle Namen auflisten, die nicht im obigen SELECT vorkommen
SQL-Code:
SELECT A.Name
FROM (VALUES ('Peter'), ('Klaus'), ('Paul'), ('Theo'), ('Bernd')) AS A(Name)
LEFT JOIN Tabelle B ON (A.Name= B.Name)
WHERE (B.Name IS NULL)

himitsu 15. Mai 2018 16:32

AW: Array mit DB Ergebnismenge vergleichen
 
@Delphi.Narium: Warum keine Temp-Table? (am Besten nur für diese Session)

Ich war davon ausgegangen, dass es mehrere Nutzer mit dem Namen "Bernd" geben könnte, also auch mehrere Datensätze,
aber im Printip geht es dann auch wie bei Uwe Raabe, aber vielleicht noch mit einem DISTINCT usw.

Uwe Raabe 15. Mai 2018 16:40

AW: Array mit DB Ergebnismenge vergleichen
 
Zitat:

Zitat von himitsu (Beitrag 1402189)
Ich war davon ausgegangen, dass es mehrere Nutzer mit dem Namen "Bernd" geben könnte, also auch mehrere Datensätze,
aber im Printip geht es dann auch wie bei Uwe Raabe, aber vielleicht noch mit einem DISTINCT usw.

In der konstruierten A-Tabelle gibt es jeden Namen nur einmal. Da der JOIN ja bewusst ins Leere läuft, können wir uns das DISTINCT sparen.

Delphi.Narium 15. Mai 2018 17:54

AW: Array mit DB Ergebnismenge vergleichen
 
Zitat:

Zitat von himitsu (Beitrag 1402189)
@Delphi.Narium: Warum keine Temp-Table? (am Besten nur für diese Session)

Um es auf allen Systemen gleich zu haben und nicht bei der Portierung immer daran zu denken:

Hier hab' ich die Tabelle, dort ist es eine Temp-Tabelle, die ich mir ggfls. anlegen bzw. befüllen muss. Anderswo geht ein Select auch ohne Angabe eines Tabellennamens, ...

'ne Temp-Tabelle für eine Session muss ich dann pro Session entsprechend "versorgen". Pech, wenn ich dann "mal eben" von FireBird nach Oracle wechsel, dann muss ich den entsprechenden Quelltext "weglassen", brauche also ein (wenn auch nur marginal) andere Implementierung. Mag es halt, wenn ein Programm unverändert gegen jede x-beliebige Datenbank läuft. Sorge halt dafür, dass auf Datenbankseite eine möglichst große Übereinstimmung besteht, sodass ich mir im Programm keine Gedanken darüber machen muss, welcher Quelltextbereich, welche SQLs ... nun bei der Nutzung von Datenbank X zu nehmen sind, welche bei Datenbank Y und was muss ich morgen machen, wenn noch Datenbank Z unterstützt werden soll?

Mit der Einheitlichkeit der SQL-Möglichkeiten zwischen den verschiedenen Datenbanken ist es ja nunmal nicht so weit her. (Wie man ja auch hier an den unterschiedlichen Lösungen sehen kann.)

Meine Lösung sieht halt so aus, dass ich für möglichst große Einheitlichkeit auf Datenbankseite sorge. Und da scheint mir das einmalige Anlegen und Befüllen der Tabelle Dual eben ein gangbarer Weg zu sein. (Und ja, es ist nur einer von vielen möglichen.)

jobo 15. Mai 2018 18:27

AW: Array mit DB Ergebnismenge vergleichen
 
Zu dual:
Selbstgestrickt ist okay, aber wer mal ein update oder insert auf der Oracle Dual Tabelle versucht, wird feststellen, dass das nicht geht. Man sollte diesen Effekt mit nachbauen sonst erlebt man eines Tages böse Überraschungen.

dual bei firebird:
RDB$DATABASE (man kann natürlich in jedem System eine Table nehmen, die verspricht nur einen Datensatz auszugeben)

dual bei mssql:
dual einfach nicht angeben

dual bei postgres
wie mssql oder oracle extension installieren

usw.

Temptable: muss man leider immer wieder anlegen

ansonsten:
Es gibt DB, die können auch gleich arrays, records, xml, json, ..? und das per SQL abfragen oder je nach Bedarf vorwärts / rückwärts konvertieren, also StringZuArray, usw.

Performance:
ganze Tabellen aus der DB abzusaugen, um sie mit ein paar lokalen Daten zu vergleichen, das ist vielleicht wie alle Vögel aus Athen holen, um zu schauen, welche davon Eulen sind (oder keine)

P.S: Die Variante von Uwe mit outer Join gefällt mir am besten.

Delphi.Narium 15. Mai 2018 18:46

AW: Array mit DB Ergebnismenge vergleichen
 
Zitat:

Zitat von jobo (Beitrag 1402201)
Zu dual:
Selbstgestrickt ist okay, aber wer mal ein update oder insert auf der Oracle Dual Tabelle versucht, wird feststellen, dass das nicht geht. Man sollte diesen Effekt mit nachbauen sonst erlebt man eines Tages böse Überraschungen.

Doch, das geht auch unter Oracle, man muss nur die passenden Rechte haben ;-)
Und ja, das ist eine sehr schlechte Idee, weil sich dann halt die Ergebnisse entsprechend der Anzahl Sätze in Dual "vervielfältigen" oder bei leerer Dual "wegbleiben" ;-)

Wenn man sein eigenes Dual baut, dann muss man sicherstellen, dass da niemand was dran manipulieren kann. Muss man aber letztlich auch bei allen anderen Tabellen, an die der User "nicht dran darf" bzw. nur lesend ...

himitsu 15. Mai 2018 21:28

AW: Array mit DB Ergebnismenge vergleichen
 
Zitat:

Zitat von Delphi.Narium (Beitrag 1402197)
Zitat:

Zitat von himitsu (Beitrag 1402189)
@Delphi.Narium: Warum keine Temp-Table? (am Besten nur für diese Session)

Um es auf allen Systemen gleich zu haben und nicht bei der Portierung immer daran zu denken

Vorallem falls mal wer auf die blöde Idee kommt sowas in einer MultiUserDB zu machen, wo mehrere gleichzeitig auf die "selbe" Tabelle schreiben und ihre Daten gegenseitig löschen und füllen würden. :stupid:


PS: das "Dual" als View ala "SELECT true" oder als StoredProc und schon kann auch ohne Rechtevergabe niemand was dran ändern. :angle:

mkinzler 15. Mai 2018 22:07

AW: Array mit DB Ergebnismenge vergleichen
 
Zitat:

Temptable: muss man leider immer wieder anlegen
Eine globale (GTT) kann man auch als persistent konfigurieren.

rokli 16. Mai 2018 07:26

AW: Array mit DB Ergebnismenge vergleichen
 
Hallo Jungs!

Vielen Dank für die Infos! Im Moment halte ich die Idee von Uwe auch für die Beste - und wahrscheinlich auch die performanteste.

Gruß
Rolf

jobo 16. Mai 2018 07:57

AW: Array mit DB Ergebnismenge vergleichen
 
Zitat:

Zitat von rokli (Beitrag 1402230)
Im Moment halte ich die Idee von Uwe auch für die Beste - und wahrscheinlich auch die performanteste.

Sag ich doch :)

dual oder was auch immer:
Es sollte möglichst nicht nur eine Rechtefrage sein, sondern auch per Constraints abgesichert sein.

Was Einheitlichkeit angeht, bietet sich natürlich ein View an, wie Himitsu sagte. Ob der unbedingt dual heißen muss, sei dahin gestellt. Ich denke, genau wie jeder (jedes Unternehmen) ein set von "home grown" Delphi units hat, hat man eine solche View Schicht vielleicht für die hauseigenen Anwendungen.

Viele solcher Statements oder Scripte kursieren auch im Netz beim Thema Datenbankmigration. Die sind in der Regel darauf gemünzt eine DB sql kompatibel zu einer anderen zu machen, aber das kann man ja auch an eigene Bedürfnisse anpassen.

Jumpy 16. Mai 2018 08:11

AW: Array mit DB Ergebnismenge vergleichen
 
Zitat:

Zitat von jobo (Beitrag 1402232)
Zitat:

Zitat von rokli (Beitrag 1402230)
Im Moment halte ich die Idee von Uwe auch für die Beste - und wahrscheinlich auch die performanteste.

Sag ich doch :)

Gibts sowas auch für Oracle? Das mit der Values(...) kannte ich so noch nicht.
Oder gäbe es da eine andere Möglichkeit das mit Join zu lösen, statt mit Subselect?

jobo 16. Mai 2018 09:49

AW: Array mit DB Ergebnismenge vergleichen
 
Zitat:

Zitat von Jumpy (Beitrag 1402236)
Gibts sowas auch für Oracle? Das mit der Values(...) kannte ich so noch nicht.
Oder gäbe es da eine andere Möglichkeit das mit Join zu lösen, statt mit Subselect?

Es gibt erstmal die Table Function, darüber kann man views, functions, vermutlich auch package functions und letztlich auch Wertelisten einbinden.
Wenn es erstmal dann erstmal Tabelle ist, dann von da an normal mit Join weiter.
Code:
select column_value
from table(sys.dbms_debug_vc2coll('One', 'Two', 'Three', 'Four'));
..
select column_value
from table(sys.dbms_debug_vc2coll(1,2,3,4));
..
select distinct column_value from table(sys.odcinumberlist(1,1,2,3,3,4,4,5))
aus SO:
https://stackoverflow.com/questions/...lues-in-oracle

für "Spezial"funktionen z.B.:
http://stevenfeuersteinonplsql.blogs...ction-and.html

P.S.: Bevor man das so wie im Beispiel einbindet (entsprechende Berechtigungen müssen da gesetzt sein), würde ich auch hier empfehlen für die sys functions Wrapper im Anwendungsschema oder Tool Schema zu definieren, die dann mit normalen Berechtigungen auskommen. Dann werden zwar bei der Erzeugung der Wrapper Sonderrechte benötigt, aber die Wrapperfunktion ist dann ein normales Schemaobjekt ohne Sonderlocken.

Jumpy 16. Mai 2018 10:06

AW: Array mit DB Ergebnismenge vergleichen
 
Danke Jobo!

Zitat:

Zitat von jobo (Beitrag 1402251)
P.S.: Bevor man das so wie im Beispiel einbindet (entsprechende Berechtigungen müssen da gesetzt sein), würde ich auch hier empfehlen für die sys functions Wrapper im Anwendungsschema oder Tool Schema zu definieren, die dann mit normalen Berechtigungen auskommen. Dann werden zwar bei der Erzeugung der Wrapper Sonderrechte benötigt, aber die Wrapperfunktion ist dann ein normales Schemaobjekt ohne Sonderlocken.

Wichtiger Hinweis, der zu beachten ist, wenn einem der DBA nicht den Kopf abreißen soll :).

jobo 16. Mai 2018 10:18

AW: Array mit DB Ergebnismenge vergleichen
 
Ja! Wer wem den Kopf abreißt, ist im Zweifel nachher von einem Ermittlerteam zu ermitteln.. ;)

[ot]
Ach und noch was Oracle spezifisches (also für Leute, die nicht Multi DB affin sind oder sein müssen)
Mit Package Functions hat man die Möglichkeit, die Wertebefüllung und den Datenabruf zu trennen.
Die Package.Funktion wird im (tiefsten ) Inneren eines Views verwendet, das Setzen der Werte läuft nebenher über einen Setter im gleichen(!) Package unter Verwendung einer Package Variable.
Damit hat kann man Views so konstruieren, dass sie Session (~user} spezifisch aufgerufen werden können und jeweils ganz unterschiedliche (oder gar keine, wenn nicht initialisiert) Daten zurückgeben.

Achtung, das geht natürlich nicht in Systemen, die Verbindungen nur bei Bedarf öffnen, also häufig Web oder .Net.
In klassischen CS Umgebungen mit single session, oder Session Kontrolle kann man es nutzen. Würde man aber wohl nur tun, wenn es Performanceprobleme mit komplexen Selektstatemntes gibt (oder vielleicht zum Datenschutz- aber da hat Oracle auch einige andere Mechanismen)
[ot]


Alle Zeitangaben in WEZ +1. Es ist jetzt 02:35 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz