[mysql] Problem mit MYSQL Abfrage

mandelbrot

Member
ID: 106373
L
2 Mai 2006
14
1
HI@all

in meiner tabelle hab ich folgendes gespeichert:

PHP:
 userid | rechtid | von       | bis
19      | recht 1 | 2008-01-01| 2008-12-31
sprich eine Tabelle in dem von jedem User alle Rechte und deren zeitliche Begrenzung stehen.

Nun hab ich aber folgendes Problem:

Wie kann ich effektiv und schnell jene User herausfinden die zb recht1,recht2 und recht3 besitzen, ohne das auf mehrere Abfragen aufteilen zu müssen??

Meine jetzige (unschöne) Lösung benutzt mehrere Abfragen und nützt Arrays in php.

Vielen Dank für eure Hilfe schon im Voraus.
lg
 
Mein spontaner Einfall hätte auf jeden Fall nur eine Abfrage:
Hol dir alle Rechte, die der User grade hat, d.h.
Code:
[FONT=Courier New][B][COLOR=#9932cc]SELECT[/COLOR][/B] rechtid [B][COLOR=#9932cc]FROM[/COLOR][/B] rechte
[B][COLOR=#9932cc]WHERE[/COLOR][/B] userid = 42
      [B][COLOR=#9932cc]AND[/COLOR][/B] [B][COLOR=#9932cc]NOW[/COLOR][/B][COLOR=#9932cc]([/COLOR][COLOR=#9932cc])[/COLOR] [B][COLOR=#9932cc]BETWEEN[/COLOR][/B] von [B][COLOR=#9932cc]AND[/COLOR][/B] bis;[/FONT]
In PHP steck ich alle Rechte in ein Array und vergleiche dann, ob ein Recht fehlt.
PHP:
$rights_must_have = array(RIGHT_FOO, RIGHT_BAR, RIGHT_WHATEVER); // Konstanten verwenden

while($row = $db->fetch_array($res))
  $rights_have[] = $row['rightid'];

foreach($rights_must_have as $right)
  if(!in_array($right, $rights_have))
    die("rechte fehlen");

die("ok");
Im schlimmsten Fall: n in MySQL + 2n in PHP = O(n)
 
Zuletzt bearbeitet:
also das mit
Code:
AND NOW() BETWEEN von AND bis;
hatte ich eh schon dabei, nur habe ich es aus Übersichtsgründen weggelassen.

Nur möchte ich eben ( sofern möglich ) von mysql gleich ein resource bekommen in dem die Userids gesammelt drinnen stehen für die zb die Rechte Recht1 UND Recht2 UND Recht3 gelten.

in php hab ich eine ähnliche lösung, nur wollte ich wissen vieleicht kann man mithilfe von mysql das ganze etwas "schöner" machen...
 
Achso, du willst alle User :think:

Mit entsprechenden Unterabfragen dürfte das schon alles in einer MySQL-Abfrage zu erledigen sein. Quasi ein SELECT DISTINCT userid JOIN (hatRecht1) JOIN (hatRecht2) ... WHERE r1=true AND r2=true ... so als Grobgedankenanstoß.

Ob das aber dann schöner is...

Ich persönlich wäre beim verständlichen PHP-Ansatz geblieben. Nur eben halt DISTINCT alle User auswählen und dann in PHP für jeden einzelnen durchlaufen.
Wird zwar dann O(n²), aber obs mit Unterabfragen in der DB anders aussieht... das Problem is ja dasselbe.
 
das mit den vielen joins hab ich probiert aber nach 2-3 joins is die benötigte zeit schon über 5 sekunden und das is mir etwas zu langsam :-(

brauchen würd ich bei den abfragen 5-10 rechte, dann werd ich wohl bei der php lösung mit den arrays bleiben, war mir nur eben nicht sicher obs vielleicht was besseres gibt, aber trotzdem danke für die tatkräftige unterstützung
 
Wenn du nur eine Liste der Benutzer möchtest, ohne weiteren angaben wie "rechtid, von, bis", dann gäbe es noch diese Lösung:
(Vorausgesetzt, ein user kann zu einer bestimmten zeit eine rechtId nicht zwei mal besitzen.)

PHP:
SELECT userid
FROM rechte
WHERE NOW() BETWEEN von AND bis
     AND rechtid IN ( 1,5,6,9,7 )
GROUP BY  userid
HAVING COUNT(*) = 5;

So in etwa sollte das glaub ich gehen.

D.h. die Anzahl der Rechte, die du übergibst (1,5,6,9,7) muss mit der Zahl beim Having Count überreinstimmen.
 
Oder Du überarbeitest/konvertierst Deine Rechte-Tabelle und verwendest dann statt "recht 1"-Strings Bits und die entsprechenden Operatoren in mysql, das dürfte spontan gedacht eine der klinischen Lösungen sein (Stichwort: Bit Operationen | Bitwise).
 
Wenn ich anmerken darf:

Code:
WHERE NOW() BETWEEN von AND bis ...

Eine Abfrage
WHERE wert BETWEEN spalte1 AND spalte2
ist im allgemeinen ineffektiv. ( z.B. keine Indexnutzung möglich )

Eine Umformulierung zu
WHERE spalte1 <= wert AND spalte2 >= wert
kann die Performance steigern
 
Code:
WHERE NOW() BETWEEN von AND bis ...
Eine Abfrage
WHERE wert BETWEEN spalte1 AND spalte2
ist im allgemeinen ineffektiv. ( z.B. keine Indexnutzung möglich )

Eine Umformulierung zu
WHERE spalte1 <= wert AND spalte2 >= wert
kann die Performance steigern
Das ist absoluter Quark!
Code:
foo BETWEEN x AND y
ist semantisch äquivalent zu
Code:
(foo >= x AND foo <= y)

edit:
Du hast eh die Kleiner-Größer-Zeichen vertauscht ;)
 
Bitte genau lesen:


WHERE wert BETWEEN spalte1 AND spalte2

ist was völlig anderes als

WHERE spalte BETWEEN wert1 AND wert2

und die grösser / kleiner Zeichen habe ich NICHT verwechselt !
 
Code:
> SHOW create TABLE explaintab\G
*************************** 1. row ***************************
Table: explaintab
Create Table: CREATE TABLE `explaintab` (
  `id` bigint(20) unsigned NOT NULL auto_increment,
  `von` datetime NOT NULL,
  `bis` datetime NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `time` (`von`,`bis`)
) ENGINE=MyISAM AUTO_INCREMENT=1000001 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

mysql> EXPLAIN SELECT id, von, bis FROM explaintab WHERE NOW() BETWEEN von AND b
is\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: explaintab
         type: ALL
[COLOR="Red"][B]possible_keys: time[/B][/COLOR]
          [COLOR="Red"][B]key: NULL[/B][/COLOR]
      key_len: NULL
          ref: NULL
         rows: 1000000
        Extra: Using where
1 row in set (0.00 sec)

ist leider kein Quark ;)
 
ist leider kein Quark ;)
Ok, ich bin zwar kein Spezialist, was Datenbanken angeht, aber ich erlaube mir trotzdem noch einmal zu widersprechen. Bei mir kommt nämlich was anderes raus:
Code:
[FONT=Courier New][B][COLOR=#9932cc]SHOW[/COLOR][/B] [B][COLOR=#9932cc]CREATE[/COLOR][/B] [B][COLOR=#9932cc]TABLE[/COLOR][/B] explaintab;[/FONT]
Code:
[FONT=Courier New]CREATE TABLE `explaintab` (
  `id` bigint(20) unsigned NOT NULL auto_increment,
  `von` datetime NOT NULL,
  `bis` datetime NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `time` (`von`,`bis`)
) ENGINE=MyISAM AUTO_INCREMENT=1000122 DEFAULT CHARSET=latin1[/FONT]
Code:
[FONT=Courier New][B][COLOR=#9932cc]EXPLAIN[/COLOR][/B]
[B][COLOR=#9932cc]SELECT[/COLOR][/B] id, von, bis
[B][COLOR=#9932cc]FROM[/COLOR][/B] explaintab [B][COLOR=#9932cc]WHERE[/COLOR][/B] [B][COLOR=#9932cc]NOW[/COLOR][/B][COLOR=#9932cc]([/COLOR][COLOR=#9932cc])[/COLOR] [B][COLOR=#9932cc]BETWEEN[/COLOR][/B] von [B][COLOR=#9932cc]AND[/COLOR][/B] bis;[/FONT]
Code:
[FONT=Courier New]+----+-------------+------------[COLOR=Red]+-------+---------------+[/COLOR]
| id | select_type | table      [COLOR=Red]| type  | possible_keys |[/COLOR]
+----+-------------+------------[COLOR=Red]+-------+---------------+[/COLOR]
|  1 | SIMPLE      | explaintab [COLOR=Red]| range | time          |[/COLOR]
+----+-------------+------------[COLOR=Red]+-------+---------------+[/COLOR]

[B][COLOR=Red]+------+[/COLOR][/B]---------+------+------+-------------+
[B][COLOR=Red]| key  |[/COLOR][/B] key_len | ref  | rows | Extra       |
[B][COLOR=Red]+------+[/COLOR][/B]---------+------+------+-------------+
[B][COLOR=Red]| time |[/COLOR][/B] 8       | NULL |    1 | Using where |
[B][COLOR=Red]+------+[/COLOR][/B]---------+------+------+-------------+[/FONT]
Selbiges, wenn ich es umschreibe auf Größer-Kleiner-Beziehung bzw. die Ungleichungen vertausche. Ich habe immer Index-Zugriff.
Code:
[FONT=Courier New][B][COLOR=#9932cc]SELECT[/COLOR][/B] [B][COLOR=#9932cc]VERSION[/COLOR][/B][COLOR=#9932cc]([/COLOR][COLOR=#9932cc])[/COLOR];[/FONT]
Code:
[FONT=Courier New]5.0.51a-community-nt[/FONT]
 
Ok, ich bin zwar kein Spezialist, was Datenbanken angeht, aber ich erlaube mir trotzdem noch einmal zu widersprechen. Bei mir kommt nämlich was anderes raus:

gut, freut mich, weil das hätte ich eigentlich auch erwartet, wollte den Test eigentlich zum wiederlegen machen, aber neija, wer weiß was mit dem MySQL mal wieder kaputt ist :LOL:
Freut mich, dass es funzt wie es soll.
 
Wie siehtsn aus, wenn du ihr*) den Index aufdrängst? Macht sies dann so, oder ignoriert sie den FORCE INDEX-Zusatz?

Und warum sie bei dirn Tablescan macht, begreif ich beim besten Willen ned :hö:

*) Notiz an mich: Mir angewöhnen, (die) Datenbank als Weibchen anzureden
 
Neija, wenn ich ihr (*gg*) mit USE den Vorschlag gebe ihn zu nutzen ignoriert sie ihn, wenn ich aber mit FORCE drangehe nimmt sie ihn auch.

Dass sie bei mir keinen Index nimmt kann viele Ursachen haben, an den Permutationen der Daten (da der MySQL-Optimizer anhand der Daten ja entscheidet welchen Index er nimmt), vllt wiedermal ein Windows-Build der Probleme macht (wäre nicht das erste mal) oder soviel anderes.

Edit: Also bei mir ist es definitv ein Full-Table-Scan auf Grund der Daten, liegt an meiner Permutation.
 
Zuletzt bearbeitet: