2016-10-15 2 views
1

zu vermeiden habe ich folgende DB-Tabellen, die ich abfragen versuche:Abfragen über mehrere Tabellen, die eine Vereinigung aller

t_shared_users 
user_id 
user_category 
folder_id 
expiry 

t_documents 
id 
folder_id 
user_id 
user_category 
description 
created 
updated 

t_folder 
id 
type 
user_id 
user_category 
created 
updated 

Ich möchte alle Dokumente finden, haben Sie besitzen und den gemeinsamen Zugriff auf. dh. Suchen Sie nach allen Dokumenten in t_documents, wobei user_id = 1 UND user_category = 100, aber auch diese Dokumente in den Ordner einschließen, auf den Sie in t_shared_users zugreifen können. Hier ist mein Versuch, bei der Abfrage:

SELECT 
    id, 
    folder_id, 
    user_id, 
    user_category, 
    description, 
    created, 
    updated 
    FROM 
    t_documents 
    WHERE 
    user_category = 100 
    AND user_id = 1 

    UNION ALL 

    SELECT 
    d.id, 
    d.folder_id, 
    d.user_id, 
    d.user_category, 
    d.description, 
    d.created, 
    d.updated 
    FROM 
    t_documents d 
    JOIN 
    t_shared_users s 
    ON 
    d.folder_id = s.folder_id 
    WHERE 
    d.user_category = 100 
    d.AND user_id = 1 

ORDER BY 
    created ASC 
LIMIT 
    10 

Gibt es eine bessere/mehr performant/prägnante Art und Weise, diese Abfrage zu schreiben? Das obige scheint ein wenig wortreich und langsam.

edit:

CREATE TABLE t_folder (
    id   SERIAL     NOT NULL, 
    user_category SMALLINT     NOT NULL, 
    user_id  INTEGER     NOT NULL, 
    type   INTEGER     NOT NULL, 
    description TEXT, 
    created  TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), 
    updated  TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), 
    PRIMARY KEY (id) 
); 

CREATE TABLE t_documents (
    id   BIGSERIAL    NOT NULL, 
    folder_id  INTEGER, 
    user_category SMALLINT     NOT NULL, 
    user_id  INTEGER     NOT NULL, 
    description TEXT      NOT NULL, 
    created  TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), 
    updated  TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), 
    PRIMARY KEY (id) 
); 

CREATE TABLE t_shared_users (
    id   SERIAL, 
    folder_id  INTEGER     NOT NULL, 
    user_category INTEGER     NOT NULL, 
    user_id  INTEGER     NOT NULL, 
    expiry  TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), 
    PRIMARY KEY (id) 
); 
+0

http: // Meta. stackoverflow.com/questions/333952/why-should-i-provide-an-mcve-for-what-seems-to-me-to-be-very-simple-sql-query – Strawberry

+0

@Strawberry Ich war wonde klingeln, wenn es vielleicht einen effizienten/cleveren JOIN gab, der den gleichen Effekt hätte? –

+0

wie in, wenn ich den Befehl "EXPLAIN-Analyse" ausführen, würde ich eine kleinere Obergrenze Kosten erhalten. Im Moment ist es wie folgt: Append (Kosten = 0,57..749.08 Zeilen = 282 Breite = 36) (tatsächliche Zeit = 0.024.0.047 Zeilen = 3 Schleifen = 1) –

Antwort

1

Das ist Ihre Abfrage:

SELECT 
    id, 
    folder_id, 
    user_id, 
    user_category, 
    description, 
    created, 
    updated 
    FROM 
    t_documents 
    WHERE 
    user_category = 100 
    AND user_id = 1 

    UNION ALL 

    SELECT 
    d.id, 
    d.folder_id, 
    d.user_id, 
    d.user_category, 
    d.description, 
    d.created, 
    d.updated 
    FROM 
    t_documents d 
    JOIN 
    t_shared_users s 
    ON 
    d.folder_id = s.folder_id 
    WHERE 
    d.user_category = 100 
    AND d.user_id = 1 -- your query actually has a typo here 

Was ich ist nicht über die obige Abfrage verstehen, warum Sie auf d.user_category und d.user_id (t_documents Tabelle) im unteren Teil der Abfrage filtern. Sind Sie sicher, dass Sie nicht s.user_category und s.user_id (t_shared_users) gemeint haben? Wenn nicht, was ist der Zweck der Verbindung mit t_shared_users?

Unter der Annahme, dass ich richtig bin, dass Ihre Abfrage fehlerhaft ist, das ist, wie ich es umschreiben:

select d.* 
    from t_documents d 
where d.user_category = 100 
    and d.user_id = 1 
union 
select d.* 
    from t_shared_users s 
    join t_documents d 
    on d.folder_id = s.folder_id 
where s.user_category = 100 
    and s.user_id = 1 

Beachten Sie, dass ich union statt union all verwenden, wie ich es technisch möglich glaube möglicherweise unerwünschte zu bekommen Dokumente sonst vervielfältigen.

Auch nur als grobe Annäherung, das sind die Indizes ich für eine gute Leistung definieren würde:

  • t_documents (user_id, user_category)
  • t_documents (folder_id)
  • t_shared_users (user_id, user_category, folder_id)
+0

Glauben Sie, dass es für t_shared_users besser ist, diese Indizes in zwei Teile zu teilen? dh. eine für nur folder_id und die andere eine Zusammensetzung für user_id, user_category? Das habe ich zur Zeit. –

+0

Auch wenn Sie glauben, ein Duplikat könnte mit UNION ALL auftreten? Der Leistungsunterschied zwischen UNION und UNION ALL ist nicht trivial. Ich habe diese Einschränkung auf t_shared_users btw: CONSTRAINT constraint_user_per_doc UNIQUE (Benutzerkategorie, Benutzer-ID, Ordner-ID) –

+1

Über Ihre Indexfrage: Ich denke, dass Ihre aktuellen geteilten Indizes eine akzeptable Leistung bieten sollten. Aber ich denke, dass das Hinzufügen von 'folder_id' zum' user_id, user_category'-Index zu einem besseren Ausführungsplan führen sollte. Sie müssten es ausprobieren, um zu sehen, was für einen Unterschied es tatsächlich macht. Über das Potenzial für Duplikate: Ich kenne Ihre Geschäftsregeln nicht, aber wenn es möglich ist, dass ein Dokument sowohl dem Benutzer "1" gehört, als auch gleichzeitig Teil eines Ordners ist, der für den Benutzer freigegeben ist ' 1 ', dann würde eine' union all 'das Dokument zweimal zurückgeben. "Union" würde das regeln. – sstan

-1

aus der Abfrage starten, haben Sie gegeben, ich join mit left join

select 
    d.id, 
    d.folder_id, 
    d.user_id, 
    d.user_category, 
    d.description, 
    d.created, 
    d.updated 
from t_documents d 
left join t_shared_users s on d.folder_id = s.folder_id 
where (d.user_category = 100 and d.user_id = 1) 
     or (s.user_category = 100 and s.user_id = 1) 

Dies würde Ihnen alle Einträge aus t_documents mit user_id = 1 und user_category = ersetzen würde 100, und auch alle Einträge mit der gleichen where-Klausel, wo Sie Zugriff auf freigegebene Dokumente haben.

+0

Ich bekomme einen Fehler, wenn ich nicht die Tabelle in der WHERE-Klausel angeben "FEHLER: Spaltenreferenz" user_category "ist mehrdeutig." Wenn ich d.user_type oder s.user_type wählen würde, wäre die Abfrage falsch –

+0

Dann muss die Abfrage, die Sie zur Verfügung gestellt haben, den gleichen Fehler geben. –

+0

Ja, ich habe gerade den Tippfehler bemerkt. Gibt es angesichts der obigen Abfrage eine Möglichkeit, Ihre Antwort anzupassen, um sie zu vereinfachen? –

Verwandte Themen