sql >> Database >  >> RDS >> PostgreSQL

Mijn favoriete PostgreSQL-query's en waarom ze ertoe doen

Databases, tabellen, normalisatie en een solide back-upplan stellen ons in staat om gegevens op te slaan en te onderhouden.

Die gecombineerde best practices bieden ons op hun beurt interactie met die gegevens. In de datagedreven wereld van vandaag zijn data waardevol. Niet alleen waardevolle gegevens zijn vaak cruciaal voor eindgebruikersoplossingen die door producten en diensten worden geleverd. Het verkrijgen van inzicht, het beantwoorden van vragen en zinvolle statistieken uit gegevens door middel van query's en gegevensmanipulatie is een integraal onderdeel van SQL in het algemeen.

PostgreSQL is niet anders.

Deze fundamentele crux is van cruciaal belang voor succes in elk datagedreven aspect.

Hieronder presenteer ik een combinatie van 8 verschillende zoekopdrachten of soorten zoekopdrachten die ik interessant en boeiend vond om datasets te verkennen, bestuderen, leren of anderszins te manipuleren.

Ze worden niet in volgorde van belangrijkheid vermeld.

De meesten zullen waarschijnlijk bekende oude vrienden zijn. Misschien worden sommigen nieuwe kennissen.

Voorbeeldtabellen en gebruikte gegevens zijn niet zo belangrijk als de feitelijke constructie van de query's zelf en wat elke query retourneert, aanbiedt of levert. Velen van hen zijn bespot en afgeleid voor demonstratiedoeleinden en moeten niet letterlijk in hun waarden worden genomen.

1. Linker join, let op eventuele nullen aan de rechterkant...

Stel dat we in dit voorbeeld een lopende verkoop hebben van twee maanden en een totaal van beide gecombineerd krijgen.

Maar om de een of andere reden trok de tweede maand niet aan zijn gewicht en we willen ons richten op welke dagen in maand één de speling heeft opgelopen.

Deze verkopen worden weergegeven als tafelbetalingen en nep_maand voor deze demonstratie.

Let op:

  • We controleren alleen op totalen groter dan 2000.
  • We zullen de uitvoer beperken tot slechts 10 rijen.

Om te beginnen hebben we deze Common Table Expression (CTE) 'genereren ' de nep_maandtabel voor ons, en de vraag die volgt.

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)

Het lijkt erop dat beide maanden daaraan hebben bijgedragen. Dus is dit opgelost?

Voordat we dit als opgelost beschouwen, gaan we eerst naar de ORDER BY-clausule.

U kunt natuurlijk BESTELLEN PER ASC of DESC.

Je kunt echter ook als eerste of laatste BESTELLEN BY NULLS en dat verandert de zaken een beetje.

Laten we deze query herschrijven en eerst ORDER BY NULLS gebruiken in de legitieme kolom.

Kortheidshalve zal ik de CTE uit de uitvoer verwijderen, weet gewoon dat deze er nog steeds is en wordt gebruikt.

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)

Er is helemaal geen verschil.

Wat als we eerst BESTELLEN OP NULLEN in de nepkolom? Die aan de rechter kant van de JOIN?

Eens kijken.

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)

Nu komen we ergens. We kunnen dag 29 en 30 zien dat de nepkolom bovenaan de resultatenset is gerangschikt.

Vanwege ORDER BY nep NULLS eerst.

Dit lost onze vraag op, op welke dagen 'sale 2' verslapte.

Vraag je je af...

"Kunnen we gewoon filteren met WHERE fake IS NULL? "

Zoals dit:

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 dat werkt. Dus waarom niet gewoon die query gebruiken? Waarom is het belangrijk?

Ik denk dat het gebruik van LEFT JOIN en ORDER BY NULLS eerst voor de tabel aan de rechterkant van de JOIN een geweldige manier is om onbekende tabellen en datasets te verkennen.

Door te bevestigen welke gegevens, indien aanwezig, 'ontbrekend . zijn ' eerst aan die kant van de join-voorwaarde; verbetert de duidelijkheid en het bewustzijn, zodat u vervolgens de resultaten kunt filteren die zijn ingesteld met de WHERE IS NULL-clausule, om de zaken af ​​te ronden.

Natuurlijk zou bekendheid met de tabellen en datasets de noodzaak van de hier gepresenteerde LEFT JOIN kunnen elimineren.

Het is een waardige vraag voor iedereen die PostgreSQL gebruikt om het op zijn minst te proberen tijdens de verkenning.

2. String aaneenschakeling

Concatenatie, het samenvoegen of toevoegen van twee strings, biedt een presentatieoptie voor resultatensets. Veel 'dingen ' kan aaneengeschakeld worden.

Zoals opgemerkt in de documentatie, accepteert de tekenreeksaaneenschakelingsoperator ('||') echter niet-tekenreeksinvoer, zolang één een tekenreeks is.

Laten we enkele voorbeelden bekijken met de onderstaande vragen:

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

We kunnen zien dat getallen en tekenreeksen allemaal aan elkaar kunnen worden gekoppeld, zoals hierboven vermeld.

De '||' operator is er maar één van die beschikbaar is in PostgreSQL.

De functie concat() accepteert meerdere argumenten en voegt ze bij terugkeer allemaal samen.

Hier is een voorbeeld van die functie in actie:

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

We kunnen indien gewenst meer dan twee argumenten doorgeven:

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

Laten we snel iets opmerken met deze volgende voorbeelden:

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)

Merk op dat de functie concat() NULL negeert, ongeacht waar deze in de lijst met parameters is geplaatst, terwijl de tekenreeksaaneenschakelingsoperator dat niet doet.

NULL wordt geretourneerd als deze ergens in de tekenreeks aanwezig is om samen te voegen.

Houd daar rekening mee.

In plaats van handmatig op te nemen in de tekenreeks die moet worden samengevoegd, bevat PostgreSQL ook een functie concat_ws() die een tekenreeksscheidingsteken als de eerste parameter accepteert.

We zullen het bezoeken met deze vragen:

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() accepteert getallen of tekenreeksen als argumenten en gebruikt, zoals hierboven vermeld, het eerste argument als scheidingsteken.

Hoe 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 wordt genegeerd tenzij het het scheidingstekenargument is dat aan concat_ws() is gegeven.

Alle argumenten worden dan genegeerd en in plaats daarvan wordt NULL geretourneerd.

Samenvoegen is cool...

Nu we een idee hebben van hoe aaneenschakeling werkt, laten we een paar voorbeelden ervan bekijken.

Terug naar de database met nep-dvd's

Stel dat we een lijst met voor- en achternaam van klanten moeten samenstellen, samen met hun e-mailadres om een ​​memo te sturen voor het bijwerken van hun account.

Ik zal de uitvoer beperken tot slechts 10 rijen omwille van de beknoptheid, maar ik laat nog steeds de || . zien telefoniste.

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)

Merk op dat we moesten ontsnappen aan het enkele aanhalingsteken dat werd gebruikt met apostrof s, door een extra enkel aanhalingsteken te gebruiken om het bezit van het e-mailadres voor elke klant aan te tonen.

Waarom zou je het weten?

Het kan voorkomen dat het samenvoegen van gegevens u een beter inzicht en begrip geeft van de gegevensset waarmee u werkt. Naast rapportage-opties kan het samenvoegen van gedeelde datasets met die van anderen deze (de gegevens) mogelijk leesbaarder en beter verteerbaar maken.

3. IN-waardenlijst aanleveren met subquery's

Een subquery heeft tal van krachtige toepassingen. Hiervan is het verstrekken van een IN-lijst met waarden om te controleren op lidmaatschap gebruikelijk.

Hier is een snel gebruik.

Stel dat we klanten- en betalingstafels hebben in een nep-dvd-verhuurwinkel en dat we onze top vijf van klanten met de hoogste uitgaven die films huurden in de dagen van 10 - 13 april, willen belonen.

Stel je voor dat dat een bijzondere streefperiode is. Dus als de klant meer dan $ 30 heeft uitgegeven, willen we hem bedanken.

Houd er rekening mee dat er andere beschikbare opties zijn om dit soort vragen op te lossen (d.w.z. samenvoegen, resultaten vastleggen van meerdere selecties, enz...), maar subquery's kunnen dit ook aan.

We beginnen hier met de hele kreng. Deze volledige zoekopdracht levert alles op wat we willen voor deze specifieke vraag.

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);

Dit voorbeeld bevat eigenlijk geneste subquery's, waarvan er één een afgeleide tabel is.

Laten we beginnen met in te zoomen op de binnenste subquery, die afgeleide tabel.

Deze subquery is een op zichzelf staande SELECT-instructie op zich, die een customer_id en een SUM() retourneert in de bedragkolom.

Alleen die klanten die voldoen aan de criteria die worden gecontroleerd door de WHERE- en HAVING-clausules komen door, en worden verder uitgedund met LIMIT 5;

Waarom de volgende subquery die je vraagt?

Kunnen we niet gewoon het gedeelte WHERE customer_id IN van de buitenste SELECT hier gebruiken?

Laten we eens kijken met een praktische benadering.

Ik zal de AS top_five uit de subquery verwijderen en nu de buitenste query ermee proberen:

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 wordt het IN-lidmaatschap getest met alleen de kolom customer_id, maar de afgeleide tabel retourneert twee kolommen en PostgreSQL laat het ons weten.

Een remedie is om een ​​andere subquery te gebruiken. Door alleen de customer_id te selecteren uit de set afgeleide tabelresultaten, wordt de volgende binnenste geneste subquery gemaakt.

Het IN-predikaat bevat nu meerdere rijen met de waarden van één kolom om het lidmaatschap te vergelijken met de WHERE-clausule voor customer_id om de uiteindelijke resultaten vast te stellen.

Waarom is het belangrijk?

Het op deze manier gebruiken van subquery's is krachtig vanwege het aantal waarden dat mogelijk kan worden getest met het IN()-predikaat.

Stel je voor dat er 100 waren? Of meer?

'Hard coderen ' allemaal in de IN()-lijst kunnen problematisch en foutgevoelig worden naarmate het volume van waarden toeneemt.

4. Genereer_serie()

Deze set-retourfunctie is handig en superleuk om te gebruiken en te verkennen. Ik heb Genereer_series() in bovenstaande voorbeelden gebruikt, maar het verdient een eigen verhaal. Meer focussen op de functie en mogelijkheden.

Ik vind generator_series() handig voor vergelijkende zoekopdrachten waarbij sommige of alle gegevens ontbreken.

Of er zijn slechts gedeeltelijke gegevens beschikbaar op het moment dat ik onderzoek. Een handig gebruik is het vullen van tabellen met 'dummy data '.

Om te beginnen zullen we een eenvoudige tabel maken:

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

Gebruik dan Genereer_series() als de VALUES voor onze INSERT-instructie:

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

Maak dan een tweede tafel

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

Vul het ook in met behulp van generation_series() in de INSERT-instructie:

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

Waarom is het belangrijk?

Om nog maar eens te herhalen, generation_series() is zo handig voor het maken van nep- of oefengegevens.

Ik heb gemerkt dat het nabootsen van maand- of dagbereiken voor vergelijking uitzonderlijk is met Genereer_series(). Raadpleeg sectie 1 en de CTE daar, die dit gebruik demonstreert.

Het maken van een set volledige gegevens met Genereer_series() en gebruiken om te vergelijken met opgeslagen gegevens om te bepalen of er gegevens ontbreken, is ook van grote waarde.

5. Query's met de COUNT() aggregatiefunctie.

Deze eenvoudige, maar effectieve aggregaatfunctie zou in ieders arsenaal moeten zitten. Vooral als je voor het eerst tabellen of datasets verkent.

Ik bedoel, wil je echt 'alles SELECTEREN ' uit een tabel met 1 miljoen rijen?

Bepaal met COUNT(*) hoeveel records er aanwezig zijn voordat je gaat laden.

Laten we eens kijken hoeveel rijen de filmtafel heeft in deze nep-dvd-verhuurtabel:

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

Hoewel niet zo uitgebreid als meer dan 1 miljoen rijen, weet ik zeker dat je het nut ervan inziet.

Om het aantal specifieke rijen te retourneren, kan COUNT(*) worden gefilterd met een WHERE-component.

Laten we eens kijken hoeveel films een 'G'-beoordeling hebben:

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

Er is een andere vorm van COUNT() om op te letten. COUNT(some_expression) .

De verschillen tussen hen zijn:

  • COUNT(*) geeft het totaal van alle invoerrijen terug (inclusief NULLS en duplicaten).
  • COUNT(some_expression ) telt het aantal niet-NULL invoerrijen.

Wanneer gebruikt in combinatie met het DISTINCT-sleutelwoord, elimineert COUNT() dubbele vermeldingen en retourneert alleen unieke waarden.

Laten we dat in actie zien door COUNT() met DISTINCT te gebruiken om te bepalen hoeveel unieke soorten beoordelingen aanwezig zijn:

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

Met deze zoekopdracht weten we dat er 5 soorten beoordelingen zijn.

Waarom is het belangrijk?

Afhankelijk van wat er wordt gevolgd of wordt getarget, kan het belangrijk zijn om te weten hoeveel er van iets bestaat. Gebruik daarom COUNT(*) of COUNT(some_expression ) helpt bij dit soort uitdagingen.

Onthoud dat COUNT(*) NULL niet negeert. Alle rijen, inclusief dubbele en NULL-waarden, worden geretourneerd als onderdeel van het uiteindelijke getal.

6. UPDATE meerdere rijen met een CASE-expressie.

Stel dat we deze tabel hebben:

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)

We moeten de kolom member_status hernoemen en 'groep . toevoegen ' aan het einde van de huidige naam die aanwezig is voor elke record.

Om te beginnen zullen meerdere individuele UPDATE-statements dit geen probleem doen.

Maar een enkele CASE-uitdrukking kan dat ook.

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

Laten we de tabel opnieuw doorzoeken om de wijzigingen te zien:

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 succesvol.

Waarom is het belangrijk?

U kunt zich voorstellen hoeveel retourvluchten dit naar de server zou kosten als er meerdere afzonderlijke UPDATE-instructies waren uitgevoerd. In werkelijkheid slechts 4 voor dit voorbeeld. Maar toch, het potentieel voor velen is er altijd.

Maar door een UPDATE met CASE-expressie te gebruiken, sturen we in plaats daarvan slechts één query.

7. KOPIE en \kopie

PostgreSQL biedt COPY, een commando voor het exporteren van gegevens tussen bestanden en tabellen.

Zorg ervoor dat u de meegeleverde link bezoekt om het overvloedige aantal beschikbare opties met COPY te zien.

Een belangrijke opmerking over COPY. SUPERUSER-rolprivilege is vereist om deze opdracht uit te voeren.

Het psql-meta-commando \copy is een alternatief voor gebruikers die dit rolkenmerk niet hebben. We zullen dat commando binnenkort om de beurt bezoeken.

Laten we eerst een COPY-opdracht uitvoeren om bepaalde kolommen naar een CSV-bestand op de lokale computer te exporteren.

Stel dat we dit zoekresultaat hebben om te exporteren:

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)

Met COPY kunnen we die SELECT-instructie gebruiken om deze export te voltooien.

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

*Opmerking:volgens de documentatie moet de zoekopdracht tussen haakjes staan.

Laten we nu de inhoud van dat bestand controleren:

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

We kunnen zien dat de eerste regel de HEADER bevat (dit zijn de kolomnamen) en beide regels hebben de gegevens voor kosten_amt en lid_status voor beide kolommen die worden geretourneerd door het WHERE-componentfilter.

Nog een belangrijk voorbehoud dat ik ontdekte bij het uitvoeren van het bovenstaande COPY-commando.

De gebruiker moet rechten hebben om naar het bestand te schrijven op OS-niveau.

In mijn geval opgelost met:

$ sudo chown postgres awards_to_honor.csv

U kunt dit probleem voorkomen door in plaats daarvan naar een systeembestand te schrijven waartoe de huidige gebruiker toegang heeft, zoals /tmp (hieronder weergegeven.)

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

Een van mijn testrollen zonder het SUPERUSER-attribuut stuitte echter op problemen bij het schrijven naar het /tmp-bestand.

Zie hieronder voor bevestiging:

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

Probeer nu hetzelfde COPY-commando te schrijven naar de /tmp-map

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.

Misschien is een betere maatstaf, zoals gesuggereerd in de HINT:, voor rollen zonder het SUPERUSER-attribuut het psql \copy meta-commando.

Laten we een soortgelijk type commando uitvoeren met \copy in plaats daarvan met dezelfde rol, zonder dat het SUPERUSER-attribuut nodig is.

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

Geen problemen daar.

En de inhoud van de bestanden,

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

Werkt ook voor de map /tmp:

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

Dezelfde inhoud is ook aanwezig in het geschreven bestand:

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

Waarom is het belangrijk?

Het importeren van gegevens in PostgreSQL via bestanden is een trefzekere bulkuploadmethode. Hoewel ze niet allemaal in deze blogpost worden behandeld, bieden COPY en \copy beide verschillende opties om met verschillende bestandsindelingen en extensies te werken.

Op hetzelfde token kan het exporteren van gegevens uit tabellen of specifieke kolommen ook gemakkelijk worden afgehandeld met beide opdrachten.

8. psql \help meta-commando

U bevindt zich in een psql-opdrachtregelsessie. Benieuwd naar de CREATE INDEX-opdrachtsyntaxis?

Niet nodig en naar een browser of een ander document gaan.

Probeer in plaats daarvan dit:

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 ]

Om te weten welke helptekst beschikbaar is, kunt u \help zelf uitvoeren en een lijst met beschikbare opties krijgen.

Ik zal ze hier niet allemaal opsommen, weet alleen dat die optie beschikbaar is.

Waarom is het belangrijk?

Het feit dat dit meta-commando supergemakkelijk te gebruiken, krachtig en handig is, zijn genoeg pluspunten om het hier te vermelden. Het heeft me tonnen tijd bespaard die ik besteed aan het doorzoeken van andere documentatie. En omdat ik een nieuweling ben, gebruik ik het natuurlijk heel vaak!

Conclusie

Dit is geen uitputtende lijst. Noch de 'be all end all ' van zoekopdrachten en gegevensmanipulatie.

Alleen mijn kijk op degenen die mijn interesse wekken en die me aanspreken terwijl ik blijf leren en uitgroei tot een SQL-ontwikkelaarsrol. Ik hoop dat je door deze blogpost gebruiksgevallen zult vinden voor de bovenstaande vragen en commando's, en die waar je nodig acht kunt implementeren.


  1. Hoe tel je het aantal keren dat een bepaalde substring in een SQL-varchar voorkomt?

  2. Verschil tussen JOIN en INNER JOIN

  3. 6 functies om de dag, maand en jaar van een datum in SQL Server te krijgen

  4. Docker-compose gebruiken om tabellen te maken in de postgresql-database