PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Meine bevorzugten PostgreSQL-Abfragen und warum sie wichtig sind

Datenbanken, Tabellen, Normalisierung und ein solider Sicherungsplan ermöglichen es uns, Daten zu speichern und zu pflegen.

Diese kombinierten Best Practices ermöglichen uns wiederum die Interaktion mit diesen Daten. In der heutigen datengesteuerten Welt sind Daten wertvoll. Daten sind nicht nur wertvoll, sondern oft auch entscheidend für Endbenutzerlösungen, die von Produkten und Dienstleistungen bereitgestellt werden. Das Extrahieren von Erkenntnissen, das Beantworten von Fragen und aussagekräftige Metriken aus Daten durch Abfragen und Datenmanipulation ist ein integraler Bestandteil von SQL im Allgemeinen.

PostgreSQL ist da nicht anders.

Dieser grundlegende Kernpunkt ist entscheidend für den Erfolg in allen datengesteuerten Aspekten.

Im Folgenden präsentiere ich eine Kombination aus 8 unterschiedlichen Abfragen oder Abfragetypen, die ich interessant und ansprechend zum Erkunden, Studieren, Lernen oder anderweitigen Bearbeiten von Datensätzen fand.

Sie sind nicht nach Wichtigkeit geordnet.

Die meisten werden wahrscheinlich alte Bekannte sein. Vielleicht werden einige neue Bekanntschaften.

Beispieltabellen und verwendete Daten sind nicht so wichtig wie die eigentliche Konstruktion der Abfragen selbst und was jede Abfrage zurückgibt, anbietet oder bereitstellt. Viele von ihnen sind verspottet und zu Demonstrationszwecken abgeleitet und sollten in ihren Werten nicht wörtlich genommen werden.

1. Left Join, achten Sie auf Nullen auf der rechten Seite ...

Angenommen, wir haben in diesem Beispiel einen laufenden Verkauf von zwei Monaten und erhalten insgesamt beides zusammen.

Doch aus irgendeinem Grund hat der zweite Monat nicht an Bedeutung gewonnen, und wir möchten uns darauf konzentrieren, an welchen Tagen der erste Monat die Lücke geschlossen hat.

Diese Verkäufe werden für diese Demonstration als Tabellenzahlung und fake_month dargestellt.

Hinweis:

  • Wir prüfen nur nach Summen über 2000.
  • Wir werden die Ausgabe auf nur 10 Zeilen beschränken.

Zu Beginn haben wir diesen Common Table Expression (CTE), der generiert wird ' die Tabelle fake_month für uns und die folgende Abfrage.

dvdrental=> WITH fake_month AS(
SELECT setup::date
FROM generate_series('2007-02-01', '2007-02-28', INTERVAL '1 day') AS setup
)
SELECT date_part('day', p.payment_date)::INT AS legit,
SUM(p.amount),
date_part('day', fk.setup)::INT AS fake
FROM payment AS p
LEFT JOIN fake_month AS fk
ON date_part('day', fk.setup)::INT = date_part('day', p.payment_date)::INT
GROUP BY legit, fake
HAVING SUM(p.amount) > 2000
LIMIT 10;
legit | sum | fake
-------+---------+------
1 | 2808.24 | 1
2 | 2550.05 | 2
6 | 2077.14 | 6
8 | 2227.84 | 8
9 | 2067.86 | 9
17 | 3630.33 | 17
18 | 3977.74 | 18
19 | 3908.59 | 19
20 | 3888.98 | 20
21 | 3786.14 | 21
(10 rows)

Sieht so aus, als hätten beide Monate dazu beigetragen. Ist das also gelöst?

Bevor wir das als gelöst betrachten, sehen wir uns die ORDER BY-Klausel an.

Natürlich können Sie BY ASC oder DESC ORDER.

Sie können jedoch auch ORDER BY NULLS am Anfang oder am Ende verwenden, und das verändert die Dinge ein wenig.

Lassen Sie uns diese Abfrage umschreiben und zuerst ORDER BY NULLS für die gültige Spalte verwenden.

Der Kürze halber entferne ich den CTE aus der Ausgabe, ich weiß nur, dass er immer noch da ist und verwendet wird.

SELECT date_part('day', p.payment_date)::INT AS legit,
SUM(p.amount),
date_part('day', fk.setup)::INT AS fake
FROM payment AS p
LEFT JOIN fake_month AS fk
ON date_part('day', fk.setup)::INT = date_part('day', p.payment_date)::INT
GROUP BY legit, fake
HAVING SUM(p.amount) > 2000
ORDER BY legit NULLS first
LIMIT 10;
legit | sum | fake
-------+---------+------
1 | 2808.24 | 1
2 | 2550.05 | 2
6 | 2077.14 | 6
8 | 2227.84 | 8
9 | 2067.86 | 9
17 | 3630.33 | 17
18 | 3977.74 | 18
19 | 3908.59 | 19
20 | 3888.98 | 20
21 | 3786.14 | 21
(10 rows)

Da gibt es überhaupt keinen Unterschied.

Was ist, wenn wir zuerst in der gefälschten Spalte BY NULLS ORDERN? Der auf der Rechten Seite des JOIN?

Mal sehen.

SELECT date_part('day', p.payment_date)::INT AS legit,
SUM(p.amount),
date_part('day', fk.setup)::INT AS fake
FROM payment AS p
LEFT JOIN fake_month AS fk
ON date_part('day', fk.setup)::INT = date_part('day', p.payment_date)::INT
GROUP BY legit, fake
HAVING SUM(p.amount) > 2000
ORDER BY fake NULLS first
LIMIT 10;
legit | sum | fake
-------+---------+------
29 | 2717.60 |
30 | 5723.89 |
1 | 2808.24 | 1
2 | 2550.05 | 2
6 | 2077.14 | 6
8 | 2227.84 | 8
9 | 2067.86 | 9
17 | 3630.33 | 17
18 | 3977.74 | 18
19 | 3908.59 | 19
(10 rows)

Jetzt kommen wir irgendwo hin. Wir können für die Tage 29 und 30 sehen, dass die gefälschte Spalte oben in der Ergebnismenge angeordnet wurde.

Aufgrund von ORDER BY zuerst gefälschte NULLS.

Damit ist unsere Frage gelöst, an welchen Tagen 'Sale 2' nachgelassen hat.

Fragst du dich...

"Können wir einfach mit WHERE fake is NULL filtern? "

So:

SELECT date_part('day', p.payment_date)::INT AS legit,
SUM(p.amount),
date_part('day', fk.setup)::INT AS fake
FROM payment AS p
LEFT JOIN fake_month AS fk
ON date_part('day', fk.setup)::INT = date_part('day', p.payment_date)::INT
WHERE date_part('day', fk.setup) IS NULL
GROUP BY legit, fake
HAVING SUM(p.amount) > 2000
LIMIT 10;
legit | sum | fake
-------+---------+------
29 | 2717.60 |
30 | 5723.89 |
(2 rows)

Ja das geht. Warum also nicht einfach diese Abfrage verwenden? Warum ist das wichtig?

Ich denke, dass die Verwendung von LEFT JOIN und ORDER BY NULLS zuerst für die Tabelle auf der rechten Seite des JOINs eine großartige Möglichkeit ist, unbekannte Tabellen und Datensätze zu erkunden.

Indem Sie bestätigen, welche Daten ggf. „fehlen ’ auf dieser Seite der Join-Bedingung zuerst; verbessert die Klarheit und das Bewusstsein, sodass Sie die Ergebnisse mit der Klausel WHERE IS NULL herausfiltern können, um die Dinge abzuschließen.

Natürlich könnte die Vertrautheit mit den Tabellen und Datensätzen möglicherweise die Notwendigkeit des hier vorgestellten LEFT JOIN beseitigen.

Es ist eine würdige Abfrage für jeden, der PostgreSQL verwendet, um es während der Erkundung zumindest zu versuchen.

2. Zeichenfolgenverkettung

Die Verkettung, das Verbinden oder Anhängen zweier Strings, bietet eine Darstellungsmöglichkeit für Ergebnismengen. Viele 'Dinge ' kann verkettet werden.

Wie jedoch in der Dokumentation erwähnt, akzeptiert der String-Verkettungsoperator ('||') Nicht-String-Eingaben, solange einer ein String ist.

Sehen wir uns einige Beispiele mit den folgenden Abfragen an:

postgres=> SELECT 2||' times'||' 2 equals: '|| 2*2;
?column?
---------------------
2 times 2 equals: 4
(1 row)

Wir können sehen, dass Zahlen und Zeichenketten alle wie oben erwähnt miteinander verkettet werden können.

Das '||' Operator ist nur einer von denen, die in PostgreSQL verfügbar sind.

Die Funktion concat() akzeptiert mehrere Argumente und verkettet sie alle bei der Rückgabe.

Hier ist ein Beispiel dieser Funktion in Aktion:

postgres=> SELECT concat('Josh ','Otwell') AS first_name;
first_name
-------------
Josh Otwell
(1 row)

Auf Wunsch können wir mehr als zwei Argumente übergeben:

postgres=> SELECT concat('Josh',' ','Otwell') AS first_name;
first_name
-------------
Josh Otwell
(1 row)

Lassen Sie uns bei diesen nächsten Beispielen etwas ganz schnelles anmerken:

postgres=> SELECT CONCAT('Josh',NULL,'Otwell') AS first_name;
first_name
------------
JoshOtwell
(1 row)
postgres=> SELECT 'Josh '||NULL||'Otwell' AS first_name;
first_name
------------
(1 row)
postgres=> SELECT NULL||'Josh '||'Otwell' AS first_name;
first_name
------------
(1 row)
postgres=> SELECT CONCAT(NULL,'Josh','Otwell') AS first_name;
first_name
------------
JoshOtwell
(1 row)

Beachten Sie, dass die concat()-Funktion NULL ignoriert, unabhängig davon, wo sie in der Parameterliste platziert ist, während der String-Verkettungsoperator dies nicht tut.

NULL wird zurückgegeben, wenn es irgendwo in der zu verkettenden Zeichenfolge vorhanden ist.

Seien Sie sich dessen bewusst.

Anstatt manuell in den zu verkettenden String aufzunehmen, enthält PostgreSQL auch eine concat_ws()-Funktion, die ein String-Trennzeichen als ersten Parameter akzeptiert.

Wir werden es mit diesen Abfragen besuchen:

postgres=> SELECT concat_ws('-',333,454,1919) AS cell_num;
cell_num
--------------
333-454-1919
(1 row)
postgres=> SELECT concat_ws(' ','Josh','Otwell') AS first_name;
first_name
-------------
Josh Otwell
(1 row)

concat_ws() akzeptiert entweder Zahlen oder Zeichenketten als Argumente und verwendet, wie oben erwähnt, das erste Argument als Trennzeichen.

Wie behandelt concat_ws() NULL?

postgres=> SELECT concat_ws('-',333,NULL,1919) AS cell_num;
cell_num
----------
333-1919
(1 row)
postgres=> SELECT concat_ws(NULL,333,454,1919) AS cell_num;
cell_num
----------
(1 row)

NULL wird ignoriert, es sei denn, es handelt sich um das an concat_ws() übergebene Trennargument.

Dann werden alle Argumente ignoriert und stattdessen NULL zurückgegeben.

Verkettung ist cool...

Nachdem wir nun eine Vorstellung davon haben, wie die Verkettung funktioniert, schauen wir uns ein paar Beispiele dafür an.

Zurück zur Pseudo-DVD-Leihdatenbank

Angenommen, wir müssen eine Liste der Vor- und Nachnamen von Kunden zusammen mit ihrer E-Mail-Adresse erstellen, um ein Memo zur Aktualisierung ihres Kontos zu versenden.

Ich werde die Ausgabe der Kürze halber auf nur 10 Zeilen beschränken, demonstriere aber trotzdem das || Betreiber.

dvdrental=> SELECT first_name||' '||last_name||'''s email address is: '||email AS name_and_email
FROM customer
LIMIT 10;
name_and_email
--------------------------------------------------------------------------
Jared Ely's email address is: [email protected]
Mary Smith's email address is: [email protected]
Patricia Johnson's email address is: [email protected]
Linda Williams's email address is: [email protected]
Barbara Jones's email address is: [email protected]
Elizabeth Brown's email address is: [email protected]
Jennifer Davis's email address is: [email protected]
Maria Miller's email address is: [email protected]
Susan Wilson's email address is: [email protected]
Margaret Moore's email address is: [email protected]
(10 rows)

Beachten Sie, dass wir das einfache Anführungszeichen mit Apostroph s maskieren mussten, indem wir ein zusätzliches einfaches Anführungszeichen verwenden, um den Besitz der E-Mail-Adresse für jeden Kunden anzuzeigen.

Warum sollten Sie das wissen?

Es kann vorkommen, dass Ihnen die Verkettung von Daten einen besseren Einblick und ein besseres Verständnis für den Datensatz bietet, mit dem Sie arbeiten. Zusammen mit Berichtsoptionen könnte die Verkettung freigegebener Datensätze mit denen anderer sie (die Daten) potenziell lesbarer und verständlicher machen.

3. Liefern der IN-Werteliste mit Subquery's

Eine Unterabfrage hat zahlreiche leistungsstarke Verwendungsmöglichkeiten. Von diesen ist das Bereitstellen einer IN-Liste mit Werten zum Überprüfen der Mitgliedschaft üblich.

Hier ist eine schnelle Verwendung.

Angenommen, wir haben Kunden- und Zahlungstabellen in einem Schein-DVD-Verleih und möchten unsere fünf umsatzstärksten Kunden belohnen, die in den Tagen vom 10. bis 13. April Filme ausgeliehen haben.

Stellen Sie sich vor, das ist ein spezieller Zielzeitraum. Wenn der Kunde also mehr als 30 $ ausgegeben hat, möchten wir ihm das anerkennen.

Denken Sie daran, dass es andere verfügbare Optionen gibt, um diese Art von Frage zu lösen (z. B. Joins, Erfassen von Ergebnissen aus mehreren Auswahlen usw.), aber Unterabfragen können damit auch umgehen.

Wir fangen hier mit dem ganzen Kram an. Diese vollständige Abfrage gibt alles zurück, was wir für diese spezielle Frage wollen.

dvdrental=> SELECT first_name, last_name, email
FROM customer
WHERE customer_id IN (
SELECT customer_id FROM (
SELECT DISTINCT customer_id, SUM(amount)
FROM payment
WHERE extract(month from payment_date) = 4
AND extract(day from payment_date) BETWEEN 10 AND 13
GROUP BY customer_id
HAVING SUM(amount) > 30
ORDER BY SUM(amount) DESC
LIMIT 5) AS top_five);

Dieses Beispiel enthält tatsächlich verschachtelte Unterabfragen, von denen eine eine abgeleitete Tabelle ist.

Beginnen wir mit einem Drilldown in die innerste Unterabfrage, diese abgeleitete Tabelle.

Diese Unterabfrage ist eine eigenständige SELECT-Anweisung, die eine customer_id und eine SUM() in der Betragsspalte zurückgibt.

Nur diejenigen Kunden, die die Kriterien erfüllen, die durch die WHERE- und HAVING-Klauseln geprüft werden, machen den Schnitt, wobei sie mit LIMIT 5 weiter ausgedünnt werden;

Warum die nächste Unterabfrage, die Sie stellen?

Können wir hier nicht einfach den Teil WHERE customer_id IN des äußersten SELECT verwenden?

Lassen Sie uns mit einem praktischen Ansatz sehen.

Ich werde das AS top_five aus der Unterabfrage entfernen und jetzt die äußerste Abfrage damit versuchen:

dvdrental=> SELECT first_name, last_name, email
FROM customer
WHERE customer_id IN
(SELECT DISTINCT customer_id, SUM(amount)
FROM payment
WHERE extract(month from payment_date) = 4
AND extract(day from payment_date) BETWEEN 10 AND 13
GROUP BY customer_id
HAVING SUM(amount) > 30
ORDER BY SUM(amount) DESC
LIMIT 5);
ERROR: subquery has too many columns
LINE 3: WHERE customer_id IN (

Hier wird die IN-Mitgliedschaft nur mit der Spalte „customer_id“ getestet, aber die abgeleitete Tabelle gibt zwei Spalten zurück und PostgreSQL teilt uns dies mit.

Eine Abhilfe besteht darin, eine andere Unterabfrage zu verwenden. Wenn Sie nur die Kunden-ID aus der Ergebnismenge der abgeleiteten Tabelle auswählen, wird die nächste innere verschachtelte Unterabfrage erstellt.

Jetzt enthält das IN-Prädikat mehrere Zeilen mit den Werten einer Spalte, um die Mitgliedschaft mit der WHERE-Klausel für die Kunden-ID zu prüfen, um die endgültige Ergebnismenge zu erstellen.

Warum ist das wichtig?

Die Verwendung von Unterabfragen auf diese Weise ist aufgrund der Tatsache der Anzahl von Werten, die potenziell mit dem IN()-Prädikat getestet werden könnten, leistungsfähig.

Stellen Sie sich vor, es gäbe 100? Oder mehr?

'Hartcodierung ' Alle in der IN()-Liste könnten problematisch und fehleranfällig werden, wenn die Menge der Werte zunimmt.

4. generate_series()

Diese Set-Return-Funktion ist praktisch und macht Spaß zu benutzen und zu erkunden. Ich habe generate_series() in den obigen Beispielen verwendet, aber es verdient eine eigene Rede. Fokussierung mehr auf die Funktion und Fähigkeiten.

Ich finde generate_series() nützlich für Vergleichsabfragen, bei denen einige oder alle Daten fehlen.

Oder zum Zeitpunkt meiner Untersuchung sind nur Teildaten verfügbar. Eine praktische Verwendung ist das Füllen von Tabellen mit 'Dummy-Daten '.

Zunächst erstellen wir eine einfache Tabelle:

trial=> CREATE TABLE tbl_1(
trial(> tb_id SERIAL PRIMARY KEY,
trial(> some_day DATE,
trial(> an_amt NUMERIC(4,2));
CREATE TABLE

Verwenden Sie dann generate_series() als WERTE für unsere INSERT-Anweisung:

trial=> INSERT INTO tbl_1(some_day, an_amt)
VALUES(
generate_series('2018-04-01','2018-04-15',INTERVAL '1 day'),
generate_series(2.43, 34.20, 1.03));
INSERT 0 31

Erstellen Sie dann eine zweite Tabelle

trial=> CREATE TABLE tbl_2(
tb2_id SERIAL PRIMARY KEY,
some_day2 DATE,
an_amt2 NUMERIC(4,2));
CREATE TABLE

Füllen Sie es auch mit generate_series() in der INSERT-Anweisung:

trial=> INSERT INTO tbl_2(some_day2, an_amt2)
VALUES(
generate_series('2018-05-16','2018-05-31',INTERVAL '1 day'),
generate_series(15.43, 31., 1.03));
INSERT 0 16

Warum ist das wichtig?

Um es noch einmal zu wiederholen:generate_series() ist so nützlich, um Schein- oder Übungsdaten zu erstellen.

Ich habe festgestellt, dass das Nachahmen von Monats- oder Tagesbereichen zum Vergleich mit generate_series() außergewöhnlich ist. Siehe Abschnitt 1 und den dortigen CTE, demonstriert diese Verwendung.

Das Erstellen eines Satzes vollständiger Daten mit generate_series() und das Verwenden zum Vergleichen mit gespeicherten Daten, um festzustellen, ob Daten fehlen, ist ebenfalls von großem Wert.

5. Abfragen mit der Aggregatfunktion COUNT().

Diese einfache, aber effektive Aggregatfunktion sollte in jedem Arsenal vorhanden sein. Besonders wenn Sie zum ersten Mal Tabellen oder Datensätze erkunden.

Ich meine, wollen Sie wirklich 'alles AUSWÄHLEN ' aus einer Tabelle mit 1 Mio. Zeilen?

Bestimmen Sie mit COUNT(*), wie viele Datensätze vorhanden sind, bevor Sie laden.

Lassen Sie uns herausfinden, wie viele Zeilen die Filmtabelle in dieser Schein-DVD-Verleihtabelle hat:

dvdrental=> SELECT COUNT(*)
dvdrental-> FROM film;
count
-------
1000
(1 row)

Obwohl nicht ganz so umfangreich wie mehr als 1 Million Zeilen, bin ich sicher, dass Sie die Nützlichkeit erkennen.

Um die Anzahl bestimmter Zeilen zurückzugeben, kann COUNT(*) mit einer WHERE-Klausel gefiltert werden.

Mal sehen, wie viele Filme eine "G"-Bewertung haben:

dvdrental=> SELECT COUNT(*)
dvdrental-> FROM film
dvdrental-> WHERE rating = 'G';
count
-------
178
(1 row)

Es gibt eine andere Form von COUNT(), die Sie beachten sollten. COUNT(some_expression) .

Die Unterschiede zwischen ihnen sind:

  • COUNT(*) gibt die Summe aller Eingabezeilen zurück (einschließlich NULL und Duplikate).
  • COUNT(some_expression ) zählt die Anzahl der Nicht-NULL-Eingabezeilen.

Bei Verwendung in Verbindung mit dem Schlüsselwort DISTINCT eliminiert COUNT() doppelte Einträge und gibt nur eindeutige Werte zurück.

Sehen wir uns das in Aktion an, indem wir COUNT() mit DISTINCT verwenden, um zu bestimmen, wie viele eindeutige Bewertungstypen vorhanden sind:

dvdrental=> SELECT COUNT(DISTINCT rating) FROM film;
count
-------
5
(1 row)

Bei dieser Abfrage wissen wir, dass es 5 Arten von Bewertungen gibt.

Warum ist das wichtig?

Je nachdem, was verfolgt oder anvisiert wird, kann es wichtig sein, zu wissen, wie viele von etwas vorhanden sind. Verwenden Sie daher COUNT(*) oder COUNT(some_expression ) hilft bei solchen Herausforderungen.

Denken Sie daran, dass COUNT(*) NULL nicht ignoriert. Alle Zeilen, einschließlich Duplikate und NULL-Werte, werden als Teil der endgültigen Zahl zurückgegeben.

6. UPDATE mehrere Zeilen mit einem CASE-Ausdruck.

Angenommen, wir haben diese Tabelle:

trial=> SELECT * FROM reward_members;
rm_id | expense_amt | member_status
-------+-------------+---------------
1 | 1245.33 | gold
2 | 1300.49 | gold
3 | 900.20 | bronze
4 | 2534.44 | platinum
5 | 600.19 | bronze
6 | 1001.55 | silver
7 | 1097.99 | silver
8 | 3033.33 | platinum
(8 rows)

Wir müssen die Spalte member_status umbenennen und 'group hinzufügen ' am Ende des aktuellen Namens, der für jeden Datensatz vorhanden ist.

Für den Anfang können mehrere einzelne UPDATE-Anweisungen dies problemlos bewerkstelligen.

Aber das kann auch ein einzelner CASE-Ausdruck.

trial=> UPDATE reward_members
SET member_status = (
CASE member_status
WHEN 'gold' THEN 'gold_group'
WHEN 'bronze' THEN 'bronze_group'
WHEN 'platinum' THEN 'platinum_group'
WHEN 'silver' THEN 'silver_group'
END
)
WHERE member_status IN ('gold', 'bronze','platinum', 'silver');
UPDATE 8

Lassen Sie uns die Tabelle erneut abfragen, um die Änderungen zu sehen:

trial=> SELECT * FROM reward_members;
rm_id | expense_amt | member_status
-------+-------------+----------------
1 | 1245.33 | gold_group
2 | 1300.49 | gold_group
3 | 900.20 | bronze_group
4 | 2534.44 | platinum_group
5 | 600.19 | bronze_group
6 | 1001.55 | silver_group
7 | 1097.99 | silver_group
8 | 3033.33 | platinum_group
(8 rows)

Alle Updates waren erfolgreich.

Warum ist das wichtig?

Sie können sich vorstellen, wie viele Roundtrips zum Server erforderlich wären, wenn mehrere einzelne UPDATE-Anweisungen ausgeführt worden wären. In Wahrheit nur 4 für dieses Beispiel. Aber trotzdem ist das Potenzial für viele immer da.

Wenn wir jedoch ein UPDATE mit CASE-Ausdruck verwenden, senden wir stattdessen nur eine Abfrage.

7. COPY und \copy

PostgreSQL bietet COPY, einen Befehl zum Exportieren von Daten zwischen Dateien und Tabellen.

Besuchen Sie unbedingt den bereitgestellten Link, um die zahlreichen Optionen zu sehen, die mit COPY verfügbar sind.

Ein wichtiger Hinweis zu COPY. Zum Ausführen dieses Befehls ist die SUPERUSER-Rollenberechtigung erforderlich.

Der psql-Metabefehl \copy ist eine Alternative für Benutzer, die dieses Rollenattribut nicht haben. Wir werden diesen Befehl in Kürze der Reihe nach besuchen.

Lassen Sie uns zuerst einen COPY-Befehl ausführen, um bestimmte Spalten in eine CSV-Datei auf dem lokalen Computer zu exportieren.

Angenommen, wir haben dieses Abfrageergebnis zum Exportieren:

trial=# SELECT expense_amt, member_status
trial-# FROM reward_members
trial-# WHERE member_status = 'gold_group';
expense_amt | member_status
-------------+---------------
1245.33 | gold_group
1300.49 | gold_group
(2 rows)

Mit COPY können wir diese SELECT-Anweisung verwenden, um diesen Export abzuschließen.

trial=# COPY (SELECT expense_amt, member_status
FROM reward_members
WHERE member_status = 'gold_group')
TO '/home/linux_user_here/awards_to_honor.csv'
DELIMITER ','
CSV HEADER;
COPY 2

*Hinweis:Laut Dokumentation muss die Abfrage in Klammern stehen.

Lassen Sie uns nun den Inhalt dieser Datei überprüfen:

$ cat awards_to_honor.csv
expense_amt,member_status
1245.33,gold_group
1300.49,gold_group

Wir können sehen, dass die erste Zeile den HEADER enthält (das sind die Spaltennamen) und beide Zeilen haben die Daten cost_amt und member_status für beide Spalten, die vom Filter der WHERE-Klausel zurückgegeben werden.

Ein weiterer wichtiger Vorbehalt, den ich bei der Ausführung des obigen COPY-Befehls entdeckt habe.

Der Benutzer muss über Berechtigungen zum Schreiben in die Datei auf Betriebssystemebene verfügen.

In meinem Fall behoben mit:

$ sudo chown postgres awards_to_honor.csv

Sie können dieses Problem vermeiden, indem Sie stattdessen in eine Systemdatei schreiben, auf die der aktuelle Benutzer Zugriff hat, wie z. B. /tmp (siehe unten.)

trial=# COPY (SELECT expense_amt, member_status
FROM reward_members
WHERE member_status = 'gold_group')
TO '/tmp/awards_to_honor.csv'
DELIMITER ','
CSV HEADER;
COPY 2

Bei einer meiner Testrollen ohne das Attribut SUPERUSER traten jedoch Probleme beim Schreiben in die /tmp-Datei auf.

Siehe unten zur Bestätigung:

trial=# SET role log_user; -- changing from postgres user to log_user
SET

Versuchen Sie jetzt den gleichen COPY-Befehl und schreiben Sie in den /tmp-Ordner

trial=> COPY (SELECT expense_amt, member_status
FROM reward_members
WHERE member_status = 'gold_group')
TO '/tmp/awards_to_honor2.csv'
DELIMITER ','
CSV HEADER;
ERROR: must be superuser to COPY to or from a file
HINT: Anyone can COPY to stdout or from stdin. psql's \copy command also works for anyone.

Vielleicht ist eine bessere Maßnahme, wie im TIPP:vorgeschlagen, für Rollen ohne das Attribut SUPERUSER der Metabefehl psql \copy.

Lassen Sie uns einen ähnlichen Befehlstyp mit \copy ausführen, anstatt dieselbe Rolle zu verwenden, ohne dass dieses SUPERUSER-Attribut benötigt wird.

trial=> \copy (SELECT expense_amt, member_status
FROM reward_members
WHERE member_status = 'silver_group')
TO '/home/linux_user_here/more_awards.csv'
DELIMITER ','
CSV HEADER;
COPY 2

Da gibt es keine Probleme.

Und den Inhalt der Dateien,

$ cat more_awards.csv
expense_amt,member_status
1001.55,silver_group
1097.99,silver_group

Funktioniert auch für den /tmp-Ordner:

trial=> \copy (SELECT expense_amt, member_status
FROM reward_members
WHERE member_status = 'silver_group')
TO '/tmp/more_awards.csv'
DELIMITER ','
CSV HEADER;
COPY 2

Gleicher Inhalt auch in der geschriebenen Datei vorhanden:

trial=> \! cat /tmp/more_awards.csv
expense_amt,member_status
1001.55,silver_group
1097.99,silver_group

Warum ist das wichtig?

Das Importieren von Daten in PostgreSQL über Dateien ist eine todsichere Bulk-Upload-Methode. Obwohl nicht alle in diesem Blogbeitrag behandelt werden, bieten sowohl COPY als auch \copy mehrere Optionen für die Arbeit mit unterschiedlichen Dateiformaten und Erweiterungen.

Ebenso ist das Exportieren von Daten aus Tabellen oder bestimmten Spalten mit diesen beiden Befehlen ebenfalls problemlos möglich.

8. psql \help meta-Befehl

Sie befinden sich in einer psql-Befehlszeilensitzung. Neugierig auf die CREATE INDEX-Befehlssyntax?

Keine Notwendigkeit, zu einem Browser oder einem anderen Dokument zu gehen.

Versuchen Sie stattdessen Folgendes:

trial=> \help CREATE INDEX
Command: CREATE INDEX
Description: define a new index
Syntax:
CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] name ] ON table_name [ USING method ]
( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
[ WITH ( storage_parameter = value [, ... ] ) ]
[ TABLESPACE tablespace_name ]
[ WHERE predicate ]

Um zu erfahren, welcher Hilfetext verfügbar ist, können Sie \help selbst ausführen und eine Liste der verfügbaren Optionen erhalten.

Ich werde sie hier nicht alle auflisten, aber wissen Sie, dass diese Option verfügbar ist.

Warum ist das wichtig?

Die Tatsache, dass dieser Metabefehl super einfach zu bedienen, leistungsstark und praktisch ist, sind genug Vorteile, um ihn hier zu erwähnen. Es hat mir jede Menge Zeit gespart, die ich damit verbracht habe, andere Dokumentationen zu durchsuchen. Und natürlich benutze ich es als Neuling ziemlich oft!

Schlussfolgerung

Dies ist keine vollständige Liste. Auch nicht das 'Alles-Ende-Alles ' von Abfragen und Datenmanipulation.

Nur meine Meinung zu denen, die mein Interesse wecken und zu mir sprechen, während ich weiter lerne und in eine SQL-Entwicklerrolle hineinwachse. Ich hoffe, dass Sie in diesem Blogbeitrag Anwendungsfälle für die oben genannten Abfragen und Befehle finden und diese dort implementieren, wo Sie es für richtig halten.