PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Moje ulubione zapytania PostgreSQL i ich znaczenie

Bazy danych, tabele, normalizacja i solidny plan tworzenia kopii zapasowych pozwalają nam przechowywać i utrzymywać dane.

Z kolei te połączone najlepsze praktyki umożliwiają nam interakcję z tymi danymi. W dzisiejszym świecie opartym na danych dane są cenne. Dane są nie tylko cenne, ale często mają kluczowe znaczenie dla rozwiązań dla użytkowników końcowych dostarczanych przez produkty i usługi. Wydobywanie wglądu, odpowiadanie na pytania i znaczące metryki z danych za pomocą zapytań i manipulacji danymi jest ogólnie integralnym elementem SQL.

PostgreSQL nie jest inny.

To fundamentalne sedno ma kluczowe znaczenie dla sukcesu w każdym aspekcie opartym na danych.

Poniżej przedstawiam kombinację 8 różnych zapytań lub typów zapytań, które uważam za interesujące i angażujące do odkrywania, studiowania, uczenia się lub manipulowania zbiorami danych w inny sposób.

Nie są one wymienione w kolejności ważności.

Większość prawdopodobnie będzie znajomymi starymi przyjaciółmi. Być może niektórzy zostaną nowymi znajomymi.

Przykładowe tabele i użyte dane nie są tak ważne, jak rzeczywista konstrukcja samych zapytań i to, co każde zapytanie zwraca, oferuje lub dostarcza. Wiele z nich jest wyśmiewanych i wyprowadzonych w celach demonstracyjnych i nie należy ich traktować dosłownie w ich wartościach.

1. Połączenie z lewej strony, zwróć uwagę na wartości null po prawej...

Załóżmy, że w tym przykładzie mamy trwającą dwa miesiące wyprzedaż i otrzymujemy w sumie obie razem.

Jednak z jakiegoś powodu drugi miesiąc nie nadążał i chcemy określić, w które dni w pierwszym miesiącu nastąpił zastój.

W przypadku tej demonstracji sprzedaż ta jest reprezentowana jako tabele płatności i fake_month.

Uwaga:

  • Sprawdzimy tylko sumy większe niż 2000.
  • Ograniczymy dane wyjściowe do zaledwie 10 wierszy.

Na początek mamy to Common Table Expression (CTE) „generowanie ' tabela fake_month dla nas i następujące zapytanie.

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)

Wygląda na to, że przyczyniły się do tego oba miesiące. Czy to jest rozwiązane?

Zanim uznamy to za rozwiązane, przejdźmy do klauzuli ORDER BY.

Oczywiście możesz ZAMÓWIĆ WEDŁUG ASC lub DESC.

Jednak możesz również ORDER BY NULLS najpierw lub na końcu, co trochę zmienia sytuację.

Przepiszmy to zapytanie i najpierw użyjmy ORDER BY NULLS w prawidłowej kolumnie.

Dla zwięzłości usunę CTE z danych wyjściowych, po prostu wiedz, że nadal tam jest i jest używane.

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)

Nie ma tu żadnej różnicy.

Co jeśli najpierw ORDER BY NULLS na fałszywej kolumnie? Ten po po prawej strona DOŁĄCZ?

Zobaczmy.

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)

Teraz do czegoś dochodzimy. Widzimy, że w dniach 29 i 30 fałszywa kolumna została uszeregowana od góry zestawu wyników.

Najpierw z powodu ORDER BY fałszywych NULL.

To rozwiązuje nasze pytanie, do jakich dni spadła „wyprzedaż 2”.

Zastanawiasz się...

Czy możemy po prostu filtrować za pomocą opcji GDZIE podróbka JEST NULL? "

Tak:

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)

Tak, to działa. Dlaczego więc nie użyć tego zapytania? Dlaczego to ma znaczenie?

Uważam, że używanie najpierw LEFT JOIN i ORDER BY NULLS dla tabeli po prawej stronie JOIN jest świetnym sposobem na poznanie nieznanych tabel i zestawów danych.

Potwierdzając, czy i jakie dane są „brakujące .” ’ najpierw po tej stronie warunku sprzężenia; zwiększa przejrzystość i świadomość, umożliwiając następnie odfiltrowanie wyników ustawionych za pomocą klauzuli WHERE IS NULL, finalizując wszystko.

Oczywiście znajomość tabel i zbiorów danych może potencjalnie wyeliminować potrzebę zaprezentowanego tutaj LEFT JOIN.

To godne zapytanie dla każdego, kto używa PostgreSQL, aby przynajmniej spróbować podczas eksploracji.

2. Łączenie ciągów

Konkatenacja, czyli łączenie lub dołączanie dwóch ciągów, udostępnia opcję prezentacji zestawów wyników. Wiele „rzeczy ' można łączyć.

Jednak, jak zauważono w dokumentacji, operator konkatenacji ciągów („||”) akceptuje dane wejściowe niebędące ciągami, o ile jeden jest ciągiem.

Zobaczmy kilka przykładów z poniższymi zapytaniami:

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

Widzimy, że wszystkie liczby i łańcuchy można łączyć ze sobą, jak wspomniano powyżej.

„||” operator jest tylko jednym z dostępnych w PostgreSQL.

Funkcja concat() przyjmuje wiele argumentów, łącząc je wszystkie po powrocie.

Oto przykład działania tej funkcji:

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

W razie potrzeby możemy przekazać więcej niż dwa argumenty:

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

Zwróćmy uwagę na coś naprawdę szybkiego na następnych przykładach:

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)

Zauważ, że funkcja concat() ignoruje NULL bez względu na to, gdzie jest umieszczona na liście parametrów, podczas gdy operator łączenia ciągów nie.

Zwracana jest wartość NULL, jeśli występuje w dowolnym miejscu ciągu w celu połączenia.

Po prostu miej tego świadomość.

Zamiast ręcznie włączać łańcuch, który ma zostać połączony, PostgreSQL zawiera również funkcję concat_ws(), która jako pierwszy parametr przyjmuje separator łańcucha.

Odwiedzimy go z następującymi zapytaniami:

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() akceptuje liczby lub łańcuchy jako argumenty i jak wspomniano powyżej, używa pierwszego argumentu jako separatora.

Jak concat_ws() traktuje 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 jest ignorowane, chyba że jest to argument separatora podany funkcji concat_ws().

Następnie wszystkie argumenty są ignorowane i zamiast tego zwracane jest NULL.

Łączenie jest fajne...

Teraz, gdy mamy już wyobrażenie, jak działa konkatenacja, spójrzmy na kilka przykładów.

Powrót do pozorowanej bazy danych wypożyczania płyt DVD

Załóżmy, że musimy sporządzić listę imion i nazwisk klientów wraz z ich adresem e-mail, aby wysłać notatkę o aktualizacji konta.

Ze względu na zwięzłość ograniczę dane wyjściowe do zaledwie 10 wierszy, ale nadal pokazuję || operatora.

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)

Zauważ, że musieliśmy ominąć pojedynczy cytat użyty z apostrofem s, używając dodatkowego pojedynczego cytatu, aby pokazać posiadanie adresu e-mail dla każdego klienta.

Dlaczego powinieneś wiedzieć?

Może się zdarzyć, że łączenie danych zapewni lepszy wgląd i zrozumienie zestawu danych, z którym pracujesz. Wraz z opcjami raportowania, łączenie udostępnionych zbiorów danych z danymi innych osób może potencjalnie sprawić, że będą one (dane) bardziej czytelne i strawne.

3. Dostarczanie listy wartości IN z podzapytaniem

Podzapytanie ma wiele potężnych zastosowań. Spośród nich powszechne jest dostarczanie listy IN wartości do sprawdzenia członkostwa.

Oto szybkie zastosowanie.

Załóżmy, że mamy tabele klientów i płatności w pozorowanej wypożyczalni DVD i chcemy nagrodzić naszych pięciu największych klientów o najwyższych wydatkach, którzy wypożyczyli filmy w dniach 10-13 kwietnia.

Wyobraź sobie, że to specjalny okres docelowy. Jeśli więc klient wydał więcej niż 30 USD, chcemy go docenić.

Pamiętaj, że istnieją inne dostępne opcje rozwiązywania tego typu pytań (tj. łączenia, przechwytywanie wyników z wielu wyborów itp.), ale podzapytania również je obsłużą.

Zaczniemy od całego tego łobuza tutaj. To kompletne zapytanie zwraca wszystko, czego potrzebujemy dla tego konkretnego pytania.

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

Ten przykład faktycznie zawiera zagnieżdżone podzapytania, z których jedno jest tabelą pochodną.

Zacznijmy od drążenia do najbardziej wewnętrznego podzapytania, czyli tabeli pochodnej.

To podzapytanie jest samodzielną instrukcją SELECT, która zwraca identyfikator klienta i SUM() w kolumnie kwoty.

Tylko ci klienci spełniający kryteria sprawdzone przez klauzule GDZIE i POSIADAJĄ dokonują cięcia, będąc dodatkowo ograniczeni LIMIT 5;

Dlaczego kolejne podzapytanie, o które zapytasz?

Czy nie możemy tutaj po prostu użyć części WHERE customer_id IN najbardziej zewnętrznego elementu SELECT?

Zobaczmy w praktycznym podejściu.

Usunę AS top_five z podzapytania i wypróbuję teraz najbardziej zewnętrzne zapytanie:

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 (

Tutaj członkostwo w IN jest testowane tylko z kolumną customer_id, ale tabela pochodna zwraca dwie kolumny, a PostgreSQL daje nam o tym znać.

Jednym z rozwiązań jest użycie innego podzapytania. Wybranie tylko identyfikatora klienta z zestawu wyników tabeli pochodnej powoduje utworzenie następnego wewnętrznego podzapytania zagnieżdżonego.

Teraz predykat IN zawiera wiele wierszy wartości jednej kolumny, aby sprawdzić członkostwo względem klauzuli WHERE dla customer_id, aby ustawić ostateczne wyniki.

Dlaczego to ma znaczenie?

Wykorzystanie podzapytań w ten sposób jest potężne ze względu na liczbę wartości, które potencjalnie mogą być testowane za pomocą predykatu IN().

Wyobraź sobie, że byłoby 100? Lub więcej?

Twarde kodowanie ' wszystkie z nich na liście IN() mogą stać się problematyczne i podatne na błędy wraz ze wzrostem ilości wartości.

4. generate_series()

Ta funkcja zwrotu zestawu jest poręczna i bardzo przyjemna w użyciu i odkrywaniu. Użyłem generate_series() w powyższych przykładach, ale zasługuje na własną rozmowę. Skupienie się bardziej na funkcji i możliwościach.

Uważam, że generate_series() jest przydatne w przypadku zapytań porównawczych, w których brakuje niektórych lub wszystkich danych.

Lub tylko częściowe dane są dostępne w czasie, który badam. Jednym z przydatnych zastosowań jest zapełnianie tabel „danymi fikcyjnymi '.

Na początek utworzymy prostą tabelę:

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

Następnie użyj generate_series() jako WARTOŚCI dla naszej instrukcji INSERT:

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

Następnie utwórz drugi stół

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

Wypełnij go również za pomocą generate_series() w instrukcji INSERT:

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

Dlaczego to ma znaczenie?

Powtarzam, generate_series() jest bardzo przydatne do tworzenia próbnych lub ćwiczeniowych danych.

Zauważyłem, że naśladowanie zakresów miesięcy lub dni dla porównania jest wyjątkowe dzięki generate_series(). Zapoznaj się z sekcją 1 i tam CTE, demonstruje to zastosowanie.

Tworzenie zestawu kompletnych danych za pomocą generate_series() i używanie do porównywania z przechowywanymi danymi w celu ustalenia, czy brakuje jakichkolwiek danych, również ma dużą wartość.

5. Zapytanie zawiera funkcję agregującą COUNT().

Ta prosta, ale skuteczna funkcja zbiorcza powinna znajdować się w arsenale każdego. Zwłaszcza podczas eksploracji tabel lub zestawów danych po raz pierwszy.

To znaczy, czy naprawdę chcesz „WYBRAĆ wszystko ' z tabeli z 1 mln wierszy?

Określ za pomocą COUNT(*), ile rekordów jest obecnych przed załadowaniem.

Sprawdźmy, ile wierszy ma tabela filmów w tej pozorowanej tabeli wypożyczania płyt DVD:

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

Chociaż nie jest tak obszerny jak 1 mln+ wierszy, jestem pewien, że widzisz przydatność.

Aby zwrócić liczbę określonych wierszy, COUNT(*) można przefiltrować za pomocą klauzuli WHERE.

Zobaczmy, ile filmów ma ocenę „G”:

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

Jest jeszcze inna forma COUNT(), o której należy pamiętać. LICZBA(niektóre_wyrażenie) .

Różnice między nimi to:

  • COUNT(*) zwraca sumę wszystkich wierszy wejściowych (łącznie z wartościami NULL i duplikatami).
  • LICZBA(niektóre_wyrażenie ) zlicza liczbę wierszy wejściowych innych niż NULL.

W połączeniu ze słowem kluczowym DISTINCT, COUNT() wyeliminuje zduplikowane wpisy i zwróci tylko unikalne wartości.

Zobaczmy to w akcji, używając funkcji COUNT() z DISTINCT, aby określić, ile unikalnych typów ocen jest obecnych:

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

Dzięki temu zapytaniu wiemy, że istnieje 5 rodzajów ocen.

Dlaczego to ma znaczenie?

W zależności od tego, co jest śledzone lub celowane, wiedza o tym, ile czegoś istnieje, może być ważna. Dlatego użyj COUNT(*) lub COUNT(jakieś_wyrażenie ) pomaga w tego typu wyzwaniach.

Pamiętaj tylko, że COUNT(*) nie ignoruje NULL. Wszystkie wiersze, w tym duplikaty i wartości NULL, są zwracane jako część końcowej liczby.

6. AKTUALIZUJ wiele wierszy za pomocą wyrażenia CASE.

Załóżmy, że mamy taką tabelę:

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)

Musimy zmienić nazwę kolumny member_status i dodać „grupę ' na końcu aktualnej nazwy dla każdego rekordu.

Na początek, wiele indywidualnych instrukcji UPDATE zapewni to bez problemu.

Ale tak samo może być pojedyncze wyrażenie CASE.

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

Ponownie zapytajmy tabelę, aby zobaczyć zmiany:

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)

Wszystkie aktualizacje powiodły się.

Dlaczego to ma znaczenie?

Możesz sobie wyobrazić, ile podróży w obie strony zajęłoby to serwerowi, gdyby uruchomiono wiele pojedynczych instrukcji UPDATE. W rzeczywistości tylko 4 dla tego przykładu. Ale wciąż istnieje potencjał dla wielu.

Jednak używając UPDATE z wyrażeniem CASE, zamiast tego wysyłamy tylko jedno zapytanie.

7. KOPIUJ i \kopiuj

PostgreSQL zapewnia COPY, polecenie do eksportowania danych między plikami i tabelami.

Pamiętaj i odwiedź podany link, aby zobaczyć dużą liczbę opcji dostępnych z KOPIA.

Ważna uwaga dotycząca KOPIOWANIA. Do wykonania tego polecenia wymagane są uprawnienia roli SUPERUSER.

Metapolecenie psql \copy jest alternatywą dla tych użytkowników, którzy nie uznali tego atrybutu roli. Wkrótce odwiedzimy to polecenie.

Najpierw uruchommy polecenie COPY, aby wyeksportować określone kolumny do pliku CSV na komputerze lokalnym.

Załóżmy, że mamy ten wynik zapytania do eksportu:

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)

Dzięki COPY możemy użyć tej instrukcji SELECT, aby zakończyć ten eksport.

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

*Uwaga:zgodnie z dokumentacją zapytanie musi być umieszczone w nawiasach.

Sprawdźmy teraz zawartość tego pliku:

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

Widzimy, że pierwszy wiersz zawiera nagłówek HEADER (które są nazwami kolumn), a oba wiersze zawierają dane cost_amt i member_status dla obu kolumn zwrócone przez filtr klauzuli WHERE.

Kolejne ważne zastrzeżenie, które odkryłem podczas wykonywania powyższego polecenia KOPIUJ.

Użytkownik musi mieć uprawnienia do zapisu do pliku na poziomie systemu operacyjnego.

W moim przypadku naprawiono za pomocą:

$ sudo chown postgres awards_to_honor.csv

Możesz uniknąć tego problemu, pisząc do pliku systemowego, do którego bieżący użytkownik ma dostęp, takiego jak /tmp (pokazany poniżej).

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

Jednak jedna z moich ról testowych bez atrybutu SUPERUSER napotkała problemy z zapisem do pliku /tmp.

Zobacz poniżej potwierdzenie:

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

Teraz próbujesz wykonać to samo polecenie COPY, zapisując do folderu /tmp

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.

Być może lepszym środkiem, jak sugeruje WSKAZÓWKA:, dla ról bez atrybutu SUPERUSER, jest metapolecenie psql \copy.

Wykonajmy podobne polecenie z \copy, zamiast tego używając tej samej roli, bez potrzeby używania atrybutu SUPERUSER.

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

Żadnych problemów.

I zawartość plików,

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

Działa również dla folderu /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

Ta sama zawartość znajduje się również w pisanym pliku:

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

Dlaczego to ma znaczenie?

Importowanie danych do PostgreSQL za pośrednictwem plików to niezawodna metoda przesyłania zbiorczego. Chociaż nie wszystkie są omówione w tym poście na blogu, zarówno COPY, jak i \copy oferują kilka opcji pracy z różnymi formatami plików i rozszerzeniami.

Z tego samego powodu eksportowanie danych z tabel lub określonych kolumn jest łatwo obsługiwane za pomocą obu tych poleceń.

8. psql \help meta-polecenie

Jesteś w sesji wiersza poleceń psql. Ciekawi Cię składnia polecenia CREATE INDEX?

Nie ma potrzeby i wchodzenia do przeglądarki lub innego dokumentu.

Spróbuj zamiast tego:

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 ]

Aby dowiedzieć się, jaki tekst pomocy jest dostępny, możesz samodzielnie uruchomić \help i uzyskać listę dostępnych opcji.

Nie wymienię ich wszystkich tutaj, po prostu wiedz, że ta opcja jest dostępna.

Dlaczego to ma znaczenie?

Fakt, że ta metakomenda jest bardzo łatwa w użyciu, potężna i wygodna, jest wystarczającym plusem, aby o tym wspomnieć. Zaoszczędziło mi to mnóstwo czasu spędzonego na przeszukiwaniu innej dokumentacji. I oczywiście jako nowicjusz używam go dość często!

Wniosek

Ta lista nie jest wyczerpująca. Ani „być wszystkim, koniec wszystkiego ' zapytań i manipulacji danymi.

Tylko moje podejście do tych, które wzbudzają moje zainteresowanie i przemawiają do mnie, gdy kontynuuję naukę i dorastam do roli programisty SQL. Mam nadzieję, że w tym poście na blogu znajdziesz przypadki użycia powyższych zapytań i poleceń, wdrażając te, które uznasz za stosowne.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Koncepcje Oracle High Availability w PostgreSQL

  2. Klauzule Care To Know:Wszystko o SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY i LIMIT

  3. Jak wyświetlić listę tabel dotkniętych kaskadowym usuwaniem?

  4. Akcent PostgreSQL + wyszukiwanie bez uwzględniania wielkości liter

  5. Niepowodzenie instalacji gem pg, mkmf.rb nie może znaleźć plików nagłówkowych dla ruby ​​(Mac OSX 10.6.5)