Postgres mehrere joins langsame Abfragen, wie zum speichern von standardmäßigen untergeordneten Datensatz

dba.stackexchange https://dba.stackexchange.com/questions/15082

Frage

Ich habe eine kleine Datenbank in Postgres, über 10.000 Datensätze speichern-Unternehmen Kunden.
Ich habe eine "langsame" ausführen der Abfrage (etwa eine halbe Sekunde), die ausgeführt wird, sehr Häufig vor, und mein Chef will mich zu verbessern.

Erstens - mein code:

select customer_alias.id, customer_alias.name, site.address, phone.phonenumber
from customer_alias
join customer on customer_alias.customer_id = customer.id
left join site on customer.default_site_id = site.id
left join contact_phonenumbers as phone on site.default_phonenumber = phone.id

(Bearbeitet left join customer zu join customer)

Was springt für mich ist ich bin die Durchführung einer Verknüpfung zu customer obwohl ich bin nicht der Auswahl nichts von diesem Rekord.Ich habe derzeit zu verbinden, um die default_site_id, eine foreign key-dem site Tabelle.

Jeder Kunde kann mehrere Standorte haben, aber nur einer sollte werden in dieser Liste angezeigt (ein Kunde hat geöffnet werden, um alle Seiten).Also meine Frage ist, wenn ich nicht die Optimierung der Abfrage, gibt es einen anderen Weg, ich kann speichern eine Standard-site für einen bestimmten Kunden?Das gleiche gilt für die Standard-Telefon-Nummer

Ein Kunde kann viele Seiten, aber eine Website hat nur einen Kunden( viele zu eins).


EXPLAIN Rückkehr:

Hash Join  (cost=522.72..943.76 rows=5018 width=53)
  Hash Cond: (customer.id = customer_alias.customer_id)
  ->  Hash Right Join  (cost=371.81..698.77 rows=5018 width=32)
        Hash Cond: (site.id = customer.default_site_id)
        ->  Hash Right Join  (cost=184.91..417.77 rows=5018 width=32)
              Hash Cond: (phone.id = site.default_phonenumber)
              ->  Seq Scan on contact_phonenumbers phone  (cost=0.00..121.70 rows=6970 width=17)
              ->  Hash  (cost=122.18..122.18 rows=5018 width=23)
                    ->  Seq Scan on site  (cost=0.00..122.18 rows=5018 width=23)
        ->  Hash  (cost=124.18..124.18 rows=5018 width=8)
              ->  Seq Scan on customer  (cost=0.00..124.18 rows=5018 width=8)
  ->  Hash  (cost=88.18..88.18 rows=5018 width=29)
        ->  Seq Scan on customer_alias  (cost=0.00..88.18 rows=5018 width=29)

EXPLAIN ANALYZE Rückkehr:

Hash Join  (cost=522.72..943.76 rows=5018 width=53) (actual time=12.457..26.655 rows=5018 loops=1)
  Hash Cond: (customer.id = customer_alias.customer_id)
  ->  Hash Right Join  (cost=371.81..698.77 rows=5018 width=32) (actual time=8.589..18.796 rows=5018 loops=1)
        Hash Cond: (site.id = customer.default_site_id)
        ->  Hash Right Join  (cost=184.91..417.77 rows=5018 width=32) (actual time=4.499..11.067 rows=5018 loops=1)
              Hash Cond: (phone.id = site.default_phonenumber)
              ->  Seq Scan on contact_phonenumbers phone  (cost=0.00..121.70 rows=6970 width=17) (actual time=0.007..1.581 rows=6970 loops=1)
              ->  Hash  (cost=122.18..122.18 rows=5018 width=23) (actual time=4.465..4.465 rows=5018 loops=1)
                    Buckets: 1024  Batches: 1  Memory Usage: 277kB
                    ->  Seq Scan on site  (cost=0.00..122.18 rows=5018 width=23) (actual time=0.007..2.383 rows=5018 loops=1)
        ->  Hash  (cost=124.18..124.18 rows=5018 width=8) (actual time=4.072..4.072 rows=5018 loops=1)
              Buckets: 1024  Batches: 1  Memory Usage: 197kB
              ->  Seq Scan on customer  (cost=0.00..124.18 rows=5018 width=8) (actual time=0.009..2.270 rows=5018 loops=1)
  ->  Hash  (cost=88.18..88.18 rows=5018 width=29) (actual time=3.855..3.855 rows=5018 loops=1)
        Buckets: 1024  Batches: 1  Memory Usage: 309kB
        ->  Seq Scan on customer_alias  (cost=0.00..88.18 rows=5018 width=29) (actual time=0.008..1.664 rows=5018 loops=1)
Total runtime: 27.290 ms"

Tabelle Struktur

/* ---------------------------------------------------------------------------
 * Table: contacts.customer
 * -------------------------------------------------------------------------*/
CREATE TABLE contacts.customer (
    id                  SERIAL NOT NULL,
    name                integer,   -- Foreign key to contacts.customer_alias
    default_site_id     integer,   -- Foreign key to contacts.site
    -- 12 other fields unrelated to query
    CONSTRAINT customer_pkey PRIMARY KEY (id )
    CONSTRAINT default_site_id FOREIGN KEY (default_site_id)
    REFERENCES contacts.site (id) MATCH SIMPLE
    ON UPDATE NO ACTION ON DELETE NO ACTION;
);

-- circular foreign key to customer_alias added later

-- Indexed: id, name, default_site_id (btree)


/*----------------------------------------------------------------------------
 * Table: contacts.customer_alias
 *--------------------------------------------------------------------------*/
CREATE TABLE contacts.customer_alias (
    id              SERIAL NOT NULL,
    customer_id     integer,
    name            text,
    -- 5 other fields (not used in query)
    CONSTRAINT customer_alias_pkey PRIMARY KEY (id ),
    CONSTRAINT customer_alias_customer_id_fkey FOREIGN KEY (customer_id)
         REFERENCES contacts.customer (id) MATCH SIMPLE
         ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED
)
-- indexed: id, customer_id (btree)

-- Customer_alias foreign key
ALTER TABLE contacts.customer
    ADD CONSTRAINT customer_name_fkey FOREIGN KEY (name)
         REFERENCES contacts.customer_alias (id) MATCH SIMPLE
         ON UPDATE NO ACTION ON DELETE NO ACTION;


/* ---------------------------------------------------------------------------
 * Table: contacts.site
 * -------------------------------------------------------------------------*/
CREATE TABLE contacts.site (
    id                  SERIAL NOT NULL,
    customer_id         integer,
    address             text,
    default_contact_id  integer,
    default_phonenumber integer,
    -- 9 other unrelated fields
    CONSTRAINT site_pkey PRIMARY KEY (id ),
    CONSTRAINT site_customer_id_fkey FOREIGN KEY (customer_id)
         REFERENCES contacts.customer (id) MATCH SIMPLE
         ON UPDATE NO ACTION ON DELETE NO ACTION
)
indexed: id, customer_id, default_contact_id, default_phonenumber ( btree)


/* ---------------------------------------------------------------------------
 * Table: contacts.contact_phonenumbers
 * -------------------------------------------------------------------------*/
CREATE TABLE contacts.contact_phonenumbers (
    id              SERIAL NOT NULL,
    site_id         integer,
    phonenumber     text,
    -- 4 other unrelated fields
    CONSTRAINT contact_phonenumbers_pkey PRIMARY KEY (id )
)
-- indexed: id, site_id (btree)

Wenn ich die Abfrage ausführen der client-Seite, über ODBC, dauert es zwischen 450-500 Millisekunden.Wenn ich die Abfrage in pgAdmin III, es heißt, die Abfrage dauert etwa 250 Millisekunden, auch wenn es manchmal dauert 60-100ms (das ist, was ich bin mit dem Ziel für).
Ich habe derzeit nicht den SSH-Zugriff auf den server, so kann ich nicht führen Sie es direkt.

Ich habe nur Aussicht über 100 dieser Zeilen zu jeder Zeit auf dem Bildschirm, ist es möglich, abrufen nur die relevanten Zeilen?Ich habe versucht, die Begrenzung, das Ergebnis zum Beispiel LIMIT 100, OFFSET 2345, aber, und führt eine frische search-jedes mal.

Vielen Dank für die Hilfe bisher!

War es hilfreich?

Lösung

Sie schreiben:

Jeder Kunde kann mehrere Standorte haben, aber nur einer sein sollte in dieser Liste angezeigt.

Doch Ihre Abfrage ruft alle Zeilen.Das wäre eine Punkt-zu-optimieren.Aber Sie auch nicht definieren, welche site ist, ausgewählt zu werden.

So oder so, es spielt keine Rolle, viel hier.Ihre EXPLAIN zeigt nur 5026 Zeilen für die site scan (5018 für die customer scan).Also kaum ein Kunde tatsächlich hat mehr als eine Website.Haben Sie ANALYZE Ihre Tische vor der Ausführung von EXPLAIN?

Aus den zahlen, die ich sehe in Ihre EXPLAIN, Indizes geben Ihnen nichts für diese Abfrage.Sequentielle Tabelle durchsucht werden den schnellstmöglichen Weg.Eine halbe Sekunde ist sehr langsam für 5000 Zeilen, obwohl.Vielleicht ist Ihre Datenbank braucht Allgemeine performance-tuning?

Vielleicht ist die Abfrage selbst ist schneller, aber "eine halbe Sekunde" umfasst ein Netzwerk übertragen werden? EXPLAIN ANALYZE würde uns mehr erzählen.

Wenn Sie diese Abfrage ist Ihre Engpass, ich würde vorschlagen, Sie implementieren eine materialized view.


Nachdem Sie weitere Informationen finde ich, dass meine Diagnose ziemlich viel hält.

Die Abfrage selbst braucht 27 ms.Nicht viel von einem problem."Eine halbe Sekunde" war die Art von Missverständnis ich vermutet hatte.Der langsame Teil ist die übertragung über das Netzwerk (plus ssh-Codierung / Decodierung, eventuell rendering).Sollten Sie abrufen nur 100 Zeilen, das würde lösen Sie die meisten von es, auch wenn es bedeutet, zu führen die ganze Abfrage jedes mal.

Wenn Sie gehen den Weg mit einer materialisierten Ansicht, wie ich vorgeschlagen, man könnte hinzufügen, ein Seriennummer ohne Lücken auf dem Tisch plus index - durch das hinzufügen einer Spalte row_number() OVER (<your sort citeria here>) AS mv_id.

Dann können Sie Abfragen:

SELECT *
FROM   materialized_view
WHERE  mv_id >= 2700
AND    mv_id <  2800;

Dies führt sehr schnell. LIMIT / OFFSET nicht mithalten kann, muss zur Berechnung die gesamte Tabelle vor es kann Sortieren und abholen von 100 Zeilen.


pgAdmin timing

Beim ausführen einer Abfrage aus dem Abfrage-tool, das die Nachricht-Bereich zeigt etwas wie:

Total query runtime: 62 ms.

Und der status-Zeile zeigt die gleiche Zeit.Ich zitiere pgAdmin helfen Sie über, dass:

Der status-Zeile wird zeigen, wie lange die Letzte Abfrage gedauert hat.Wenn ein dataset zurückgegeben wurde, nicht nur die verstrichene Zeit für den server Ausführung wird angezeigt, sondern auch die Zeit, die zum abrufen der Daten vom server um die Daten-Ausgabe Seite.

Wenn Sie wollen zu sehen die Zeit auf die server, die Sie verwenden müssen SQL EXPLAIN ANALYZE oder, die gebaut in Shift + F7Tastatur-shortcut oder Query -> Explain analyze.Dann, am unteren Rand der explain-Ausgabe erhalten Sie so etwas wie dieses:

Total runtime: 0.269 ms
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit dba.stackexchange
scroll top