Forum: PC Hard- und Software SQL-Abfrage ueber normalisierte Tabellen


von Klaus Trophob (Gast)


Lesenswert?

Ich speichere Konfigurationen in einer Datenbank ab, die Daten 
normalisiere ich. DBMS ist Microsoft SQL Server 2008.
Beispiel:

Tabelle ID: (ID wird automatisch inkreementiert)
ID:
1
2
3

Tabelle Modul:
ID: ModulID:
1   1
1   2
2   1
3   3

Tabelle ModuleID: (ModuleID wird automatisch inkrementiert)
ModulID: Modul:
1        foo
2        bar
3        nope

Konfiguration 1 hat also die Module foo und bar eingebunden, Konfig. 2 
Modul foo und Konfiguration 3 Modul nope.

Bevor ich eine neue Konfiguration anlege, muss ich schauen, ob es sie 
schon gibt - habe ich also zB Modul bar und nope in einer Konfiguration, 
frage ich nach allen IDs, die die ModulIDs von bar und nope haben. 
Kommen keine zurueck, leg ich sie an, kommt eine ID zurueck, gibt es die 
Konfiguration, kommen mehrere zurueck, ist die DB zerschossen ;)
Soweit die Theorie. Real ist die DB wesentlich komplexer, es gibt noch 
mehr Tabellen als nur die Module und mehr Spalten in den Tabellen. Das 
ist ja ein Standardproblem: Wie fragt man sowas am geschicktesten ab? 
Wenn ich das alles zusammenjoine, kriege ich zigtausende Zeilen zurueck 
- die Tabellen multiplizieren sich ja. Ich moechte allerdings nur eine 
Spalte pro ID erhalten, und insgesamt eben 0,1 oder mehr als 1. Distinct 
koennte man benutzen, die Abfrage habe ich nach 20 Minuten abgebrochen 
;)
Irgendwie hakt es da gerade bei mir :(

von D. I. (Gast)


Lesenswert?

Bei der Tabellenaufteilung und der Aufgabenstellung scheint mir das 
persönlich Applikationslogik zu sein. Im Wesentlichen muss zu jeder 
Konfiguration ein Modultupel gebildet werden und anschließend muss man 
diese mit dem zu suchenden Tupel vergleichen.

Erste Frage: Wie häufig wird diese Operation ausgeführt?

von Klaus Trophob (Gast)


Lesenswert?

> Erste Frage: Wie häufig wird diese Operation ausgeführt?

Bei jeder neuen Hardwarekonstellation - selten, worst case ein paar Mal 
pro Minute, real wohl alle paar Monate.

von Klaus Trophob (Gast)


Lesenswert?

Achso, Applikationslogik bedeutet, dass ich diese Operation nicht in der 
Datenbank, sondern der App. mache, indem ich die Abfragen nacheinander 
absetze und mir selber die IDs sortiere, die Ergebnisse in Arrays 
schiebe usw?

von D. I. (Gast)


Lesenswert?

Der allererste Schuss wie ich es jetzt stumpf in Ruby on Rails gelöst 
hätte:
1
search_config = [4,1,2,3].sort (Konfiguration = Menge modulIDs)
2
3
config_hash = {}
4
5
Modul.each do |modul|
6
  config_hash[modul.ID] = [] unless config_hash.has_key?(modul.ID)
7
  config_hash[modul.ID] << modul.modulID
8
end
9
10
found = 0
11
12
config_hash.each do |id, config|
13
  found += 1 if config.sort == search_config  
14
end
15
16
if found == 0
17
  puts "nix"
18
elsif found == 1
19
  puts "eins"
20
else
21
  puts "mehr als eins"
22
end

von Peter II (Gast)


Lesenswert?

Klaus Trophob schrieb:
> Ich moechte allerdings nur eine
> Spalte pro ID erhalten, und insgesamt eben 0,1 oder mehr als 1. Distinct
> koennte man benutzen, die Abfrage habe ich nach 20 Minuten abgebrochen
> ;)
> Irgendwie hakt es da gerade bei mir :(

distinct ist langsam, verwende group by dann bekommst du auch über 
count() die anzahl.

Wir haben hier tabellen mit 100.000.000 einträgen und das geht recht 
schnell. Wenn es bei dir länger dauert dann hast du keinen passenden 
Primary keys.

von D. I. (Gast)


Lesenswert?

Achja mein Code ist mit Sicherheit nicht der Weisheit letzter Schluss ;)

von Klaus Trophob (Gast)


Lesenswert?

Danke fuer die Tips - ich habe gleich Feierabend, werde aber morgen 
testen.
Den letzten Schuss der Weisheit brauch ich nicht, ein Denkansatz reicht 
voellig aus ;)

von Klaus Trophob (Gast)


Lesenswert?

Soweit ich das bis jetzt beurteilen kann, funktioniert GROUP BY besser 
als DISTINCT.
Ich habe erstmal noch ein anderes Problem: Ich moechte im Beispiel ja 
die ID haben, die foo UND bar enthaelt. Frage ich also nach der 
Konfiguration, in der Modul.ModulID 1 UND 2 ist, kommt nichts zurueck, 
frage ich mit ODER, dann kaemen auch Ergebnisse zurueck, in denen nur 
eins passt - was uebersehe gedanklich?

von D. I. (Gast)


Lesenswert?

Klaus Trophob schrieb:
> was uebersehe gedanklich?

Wie soll das gehen nach UND zu fragen?

Eine modulID die einer Konfiguration zugeordnet ist kann nur einen Wert 
haben und nicht mehrere gleichzeitig.

Mit der Gestaltung der Tabellen und dem geforderten Problem wirst du mit 
"normalem" SQL nicht weit kommen. Da wirst du dich in der 
Applikationslogik deutlich leichter tun.

von Peter II (Gast)


Lesenswert?

Klaus Trophob schrieb:
> Ich moechte im Beispiel ja
> die ID haben, die foo UND bar enthaelt.

select distinct
   m.id
from Modul m
join ModuleId mi1
   on mi1.ModulId = m.ModulId
join ModuleId mi2
   on mi2.ModulId = m.ModulId
where
   mi1.Modul = 'foo'
   and mi2.Modul = 'bar'

> Mit der Gestaltung der Tabellen und dem geforderten Problem wirst du mit
> "normalem" SQL nicht weit kommen. Da wirst du dich in der
> Applikationslogik deutlich leichter tun.

das wird viel zu oft gesagt, aber es gibt fast immer eine lösung mit 
SQL. Das ist dann meist auch noch schneller weil weniger daten 
übertragen werden müssen.

von D. I. (Gast)


Lesenswert?

Peter II schrieb:
> das wird viel zu oft gesagt, aber es gibt fast immer eine lösung mit
> SQL. Das ist dann meist auch noch schneller weil weniger daten
> übertragen werden müssen.

Da hast du schon recht.

Was nun wenn eine Konfiguration aus 10 oder mehr Moduln besteht. 10 
Joins? Müsste dann halt die Query dynamisch erstellen lassen, das ginge 
durchaus.

von Klaus Trophob (Gast)


Lesenswert?

Soweit war ich schon - automatisch erstellte, hunderte Zeilen lange 
Queries - geklappt hats aber nicht.
Jetzt laeuft es erstmal in der Anwendung in Form eines feuerspuckendens 
Schleifen- und Arraymonster :D

von Peter II (Gast)


Lesenswert?

D. I. schrieb:
> Was nun wenn eine Konfiguration aus 10 oder mehr Moduln besteht. 10
> Joins? Müsste dann halt die Query dynamisch erstellen lassen, das ginge
> durchaus.

dafür gibt es bei MSSQL sogar recursion im SQL. Damit sollte das möglich 
sein.

http://msdn.microsoft.com/en-us/library/ms175972.aspx

Using a recursive common table expression to display multiple levels of 
recursion

von Peter II (Gast)


Lesenswert?

nicht schön sollte aber gehen

select
   m.id, dbo.concat( '#' + mi1.Modul + '#' )
from Modul m
join ModuleId mi
   on mi1.ModulId = m.ModulId
group by
   m.id
having
   dbo.concat( '#' + mi1.Modul + '#' ) like '%#foo#%'
   and dbo.concat( '#' + mi1.Modul + '#' ) like '%#bar#%'

von D. I. (Gast)


Lesenswert?

Peter II schrieb:
> Klaus Trophob schrieb:
>> Ich moechte im Beispiel ja
>> die ID haben, die foo UND bar enthaelt.
>
> select distinct
>    m.id
> from Modul m
> join ModuleId mi1
>    on mi1.ModulId = m.ModulId
> join ModuleId mi2
>    on mi2.ModulId = m.ModulId
> where
>    mi1.Modul = 'foo'
>    and mi2.Modul = 'bar'


jetzt stellt sich aber noch die Frage ob eine Konfiguration 
Permutationsinvariant ist oder nicht. Bei deinem Beispiel wäre sie 
permutationsvariant.

von Jens G. (jensig)


Lesenswert?

select id from (
  select count(id) a, id
  from modul
  where ModulID in (select modulid
                    from modulID
                    where Modul in ('foo','bar'))
  group by id)
where a=(select count(*)
         from modulID
         where Modul in ('foo','bar'))
  )




Ich gehe hierbei davon aus, daß in Tab ModulID der Modulname (foo, bar, 
nope, ...) ebenfalls unique ist, wie eben auch modulid - also eindeutig 
1:1.
Beide Subselects müssen natürlich immer gleiche Suchwerte enthalten, 
sonst gehts schief.
Allerdings findet dieses SQL auch die ID's, die mehr als foo AND bar 
haben.
Es findet also IDs, denen mindestens 'foo' AND 'bar' zugeordnet sind.

von Peter II (Gast)


Lesenswert?

Jens G. schrieb:
> select id from (
..

Es fehlt der alias name des Subselects. Und eine Klammer ist zu viel.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.