sql >> Database >  >> RDS >> PostgreSQL

Meer van mijn favoriete PostgreSQL-query's - en waarom ze er ook toe doen

In een eerdere blogpost My Favorite PostgreSQL-query's en waarom ze ertoe doen, heb ik interessante query's bezocht die voor mij van belang zijn terwijl ik leer, ontwikkel en groei naar een rol als SQL-ontwikkelaar.

Een daarvan, in het bijzonder een UPDATE met meerdere rijen en een enkele CASE-uitdrukking, leidde tot een interessant gesprek op Hacker News.

In deze blogpost wil ik vergelijkingen zien tussen die specifieke zoekopdracht en een met meerdere enkele UPDATE-instructies. Voor goed of slecht.

Machine-/omgevingsspecificaties:

  • Intel(R) Core(TM) i5-6200U CPU @ 2,30GHz
  • 8 GB RAM
  • 1TB opslag
  • Xubuntu Linux 16.04.3 LTS (Xenial Xerus)
  • PostgreSQL 10.4

Opmerking:om te beginnen heb ik een 'staging'-tabel gemaakt met alle kolommen van het TEKST-type om de gegevens te laden.

De voorbeeldgegevensset die ik gebruik, is te vinden via deze link hier.

Maar houd er rekening mee dat de gegevens zelf in dit voorbeeld worden gebruikt, omdat het een set van behoorlijke omvang is met meerdere kolommen. Elke 'analyse' of UPDATES/INSERTS aan deze dataset, weerspiegelt niet de werkelijke 'real-world' GPS/GIS-operaties en is niet als zodanig bedoeld.

location=# \d data_staging;
               Table "public.data_staging"
    Column     |  Type   | Collation | Nullable | Default 
---------------+---------+-----------+----------+---------
 segment_num   | text    |           |          | 
 point_seg_num | text    |           |          | 
 latitude      | text    |           |          | 
 longitude     | text    |           |          | 
 nad_year_cd   | text    |           |          | 
 proj_code     | text    |           |          | 
 x_cord_loc    | text    |           |          | 
 y_cord_loc    | text    |           |          | 
 last_rev_date | text    |           |          | 
 version_date  | text    |           |          | 
 asbuilt_flag  | text    |           |          | 

location=# SELECT COUNT(*) FROM data_staging;
count
--------
546895
(1 row)

We hebben ongeveer een half miljoen rijen met gegevens in deze tabel.

Voor deze eerste vergelijking zal ik de kolom proj_code BIJWERKEN.

Hier is een verkennende zoekopdracht om de huidige waarden te bepalen:

location=# SELECT DISTINCT proj_code FROM data_staging;
proj_code
-----------
"70"
""
"72"
"71"
"51"
"15"
"16"
(7 rows)

Ik zal trim gebruiken om aanhalingstekens uit de waarden te verwijderen en naar een INT te casten en te bepalen hoeveel rijen er zijn voor elke individuele waarde:

Laten we daar een CTE voor gebruiken en er dan een SELECTEER van maken:

location=# WITH cleaned_nums AS (
SELECT NULLIF(trim(both '"' FROM proj_code), '') AS p_code FROM data_staging
)
SELECT COUNT(*),
CASE
WHEN p_code::int = 70 THEN '70'
WHEN p_code::int = 72 THEN '72'
WHEN p_code::int = 71 THEN '71'
WHEN p_code::int = 51 THEN '51'
WHEN p_code::int = 15 THEN '15'
WHEN p_code::int = 16 THEN '16'
ELSE '00'
END AS proj_code_num
FROM cleaned_nums
GROUP BY p_code
ORDER BY p_code DESC;
count  | proj_code_num
--------+---------------
353087 | 0
139057 | 72
25460  | 71
3254   | 70
1      | 51
12648  | 16
13388  | 15
(7 rows)

Voordat ik deze tests uitvoer, ga ik door en verander ik de proj_code-kolom om INTEGER te typen:

BEGIN;
ALTER TABLE data_staging ALTER COLUMN proj_code SET DATA TYPE INTEGER USING NULLIF(trim(both '"' FROM proj_code), '')::INTEGER;
SAVEPOINT my_save;
COMMIT;

En ruim die NULL-kolomwaarde op (die wordt weergegeven door de ELSE '00' in de verkennende CASE-expressie hierboven), en stel deze in op een willekeurig getal, 10, met deze UPDATE:

UPDATE data_staging
SET proj_code = 10
WHERE proj_code IS NULL;

Nu hebben alle proj_code-kolommen een INTEGER-waarde.

Laten we doorgaan en een enkele CASE-expressie uitvoeren die alle proj_code-kolomwaarden bijwerkt en kijken wat de timing meldt. Ik zal alle commando's in een .sql-bronbestand plaatsen om het gemakkelijker te kunnen verwerken.

Dit is de inhoud van het bestand:

BEGIN;
\timing on
UPDATE data_staging
SET proj_code =
(
CASE proj_code
WHEN 72 THEN 7272
WHEN 71 THEN 7171
WHEN 15 THEN 1515
WHEN 51 THEN 5151
WHEN 70 THEN 7070
WHEN 10 THEN 1010
WHEN 16 THEN 1616
END
)
WHERE proj_code IN (72, 71, 15, 51, 70, 10, 16);
SAVEPOINT my_save;

Laten we dit bestand uitvoeren en controleren wat de timing meldt:

location=# \i /case_insert.sql
BEGIN
Time: 0.265 ms
Timing is on.
UPDATE 546895
Time: 6779.596 ms (00:06.780)
SAVEPOINT
Time: 0.300 ms

Iets meer dan een half miljoen rijen in 6+ seconden.

Dit zijn de tot nu toe weergegeven wijzigingen in de tabel:

location=# SELECT DISTINCT proj_code FROM data_staging;
proj_code
-----------
7070
1616
1010
7171
1515
7272
5151
(7 rows)

Ik zal deze wijzigingen ROLLBACK (niet getoond) zodat ik individuele INSERT-instructies kan uitvoeren om deze ook te testen.

Hieronder worden de wijzigingen weergegeven in het .sql-bronbestand voor deze reeks vergelijkingen:

BEGIN;
\timing on

UPDATE data_staging
SET proj_code = 7222
WHERE proj_code = 72;

UPDATE data_staging
SET proj_code = 7171
WHERE proj_code = 71;

UPDATE data_staging
SET proj_code = 1515
WHERE proj_code = 15;

UPDATE data_staging
SET proj_code = 5151
WHERE proj_code = 51;

UPDATE data_staging
SET proj_code = 7070
WHERE proj_code = 70;

UPDATE data_staging
SET proj_code = 1010
WHERE proj_code = 10;

UPDATE data_staging
SET proj_code = 1616
WHERE proj_code = 16;
SAVEPOINT my_save;

En die resultaten,

location=# \i /case_insert.sql
BEGIN
Time: 0.264 ms
Timing is on.
UPDATE 139057
Time: 795.610 ms
UPDATE 25460
Time: 116.268 ms
UPDATE 13388
Time: 239.007 ms
UPDATE 1
Time: 72.699 ms
UPDATE 3254
Time: 162.199 ms
UPDATE 353087
Time: 1987.857 ms (00:01.988)
UPDATE 12648
Time: 321.223 ms
SAVEPOINT
Time: 0.108 ms

Laten we de waarden eens controleren:

location=# SELECT DISTINCT proj_code FROM data_staging;
proj_code
-----------
7222
1616
7070
1010
7171
1515
5151
(7 rows)

En de timing (Opmerking:ik doe de wiskunde in een query omdat \timing deze run geen hele seconden heeft gerapporteerd):

location=# SELECT round((795.610 + 116.268 + 239.007 + 72.699 + 162.199 + 1987.857 + 321.223) / 1000, 3) AS seconds;
seconds
---------
3.695
(1 row)

De individuele INSERT's namen ongeveer de helft van de tijd in beslag als de enkele CASE.

Deze eerste test omvatte de hele tabel, met alle kolommen. Ik ben benieuwd naar eventuele verschillen in een tabel met hetzelfde aantal rijen, maar minder kolommen, vandaar de volgende reeks tests.

Ik maak een tabel met 2 kolommen (samengesteld uit een SERIAL-gegevenstype voor de PRIMARY KEY en een INTEGER voor de proj_code-kolom) en ga over de gegevens:

location=# CREATE TABLE proj_nums(n_id SERIAL PRIMARY KEY, proj_code INTEGER);
CREATE TABLE
location=# INSERT INTO proj_nums(proj_code) SELECT proj_code FROM data_staging;
INSERT 0 546895

(Opmerking:SQL-commando's uit de eerste reeks bewerkingen worden gebruikt met de juiste wijziging(en). Ik laat ze hier weg voor de beknoptheid en weergave op het scherm )

Ik zal eerst de enkele CASE-expressie uitvoeren:

location=# \i /case_insert.sql
BEGIN
Timing is on.
UPDATE 546895
Time: 4355.332 ms (00:04.355)
SAVEPOINT
Time: 0.137 ms

En dan de individuele UPDATE's:

location=# \i /case_insert.sql
BEGIN
Time: 0.282 ms
Timing is on.
UPDATE 139057
Time: 1042.133 ms (00:01.042)
UPDATE 25460
Time: 123.337 ms
UPDATE 13388
Time: 212.698 ms
UPDATE 1
Time: 43.107 ms
UPDATE 3254
Time: 52.669 ms
UPDATE 353087
Time: 2787.295 ms (00:02.787)
UPDATE 12648
Time: 99.813 ms
SAVEPOINT
Time: 0.059 ms
location=# SELECT round((1042.133 + 123.337 + 212.698 + 43.107 + 52.669 + 2787.295 + 99.813) / 1000, 3) AS seconds;
seconds
---------
4.361
(1 row)

De timing is enigszins gelijk tussen beide sets bewerkingen op de tafel met slechts 2 kolommen.

Ik zal zeggen dat het gebruik van de CASE-uitdrukking een beetje gemakkelijker is om uit te typen, maar niet noodzakelijkerwijs de beste keuze bij alle gelegenheden. Zoals met wat werd vermeld in sommige opmerkingen over de Hacker News-thread waarnaar hierboven wordt verwezen, hangt het normaal gesproken "gewoon af" van vele factoren die al dan niet de optimale keuze zijn.

Ik realiseer me dat deze tests op zijn best subjectief zijn. Een daarvan, op een tabel met 11 kolommen, terwijl de andere slechts 2 kolommen had, die beide van een numeriek gegevenstype waren.

De CASE-expressie voor updates van meerdere rijen is nog steeds een van mijn favoriete zoekopdrachten, alleen al vanwege het gemak van typen in een gecontroleerde omgeving waar veel individuele UPDATE-query's het andere alternatief zijn.

Ik kan nu echter zien waar het niet altijd de optimale keuze is, terwijl ik blijf groeien en leren.

Zoals dat oude gezegde luidt:"Een half dozijn in de ene hand, 6 in de andere ."

Een extra favoriete zoekopdracht - PLpgSQL CURSOR's gebruiken

Ik ben begonnen met het opslaan en bijhouden van al mijn trainingsstatistieken (trail hiking) met PostgreSQL op mijn lokale ontwikkelingsmachine. Er zijn meerdere tabellen bij betrokken, zoals bij elke genormaliseerde database.

Aan het einde van de maand wil ik echter de statistieken van specifieke kolommen opslaan in hun eigen, aparte tabel.

Dit is de 'maandelijkse' tabel die ik zal gebruiken:

fitness=> \d hiking_month_total;
                     Table "public.hiking_month_total"
     Column      |          Type          | Collation | Nullable | Default 
-----------------+------------------------+-----------+----------+---------
 day_hiked       | date                   |           |          | 
 calories_burned | numeric(4,1)           |           |          | 
 miles           | numeric(4,2)           |           |          | 
 duration        | time without time zone |           |          | 
 pace            | numeric(2,1)           |           |          | 
 trail_hiked     | text                   |           |          | 
 shoes_worn      | text                   |           |          |

Ik concentreer me op de resultaten van May met deze SELECT-query:

fitness=> SELECT hs.day_walked, hs.cal_burned, hs.miles_walked, hs.duration, hs.mph, tr.name, sb.name_brand
fitness-> FROM hiking_stats AS hs
fitness-> INNER JOIN hiking_trail AS ht
fitness-> ON hs.hike_id = ht.th_id
fitness-> INNER JOIN trail_route AS tr
fitness-> ON ht.tr_id = tr.trail_id
fitness-> INNER JOIN shoe_brand AS sb
fitness-> ON hs.shoe_id = sb.shoe_id
fitness-> WHERE extract(month FROM hs.day_walked) = 5
fitness-> ORDER BY hs.day_walked ASC;

En hier zijn 3 voorbeeldrijen die zijn geretourneerd door die zoekopdracht:

day_walked | cal_burned | miles_walked | duration | mph | name | name_brand
------------+------------+--------------+----------+-----+------------------------+---------------------------------------
2018-05-02 | 311.2 | 3.27 | 00:57:13 | 3.4 | Tree Trail-extended | New Balance Trail Runners-All Terrain
2018-05-03 | 320.8 | 3.38 | 00:58:59 | 3.4 | Sandy Trail-Drive | New Balance Trail Runners-All Terrain
2018-05-04 | 291.3 | 3.01 | 00:53:33 | 3.4 | House-Power Line Route | Keen Koven WP(keen-dry)
(3 rows)

Eerlijk gezegd kan ik de target hiking_month_total tabel invullen met de bovenstaande SELECT-query in een INSERT-statement.

Maar waar is het plezier daarin?

Ik zal afzien van verveling voor een PLpgSQL-functie met een CURSOR in plaats daarvan.

Ik heb deze functie bedacht om de INSERT uit te voeren met een CURSOR:

CREATE OR REPLACE function monthly_total_stats()
RETURNS void
AS $month_stats$
DECLARE
v_day_walked date;
v_cal_burned numeric(4, 1);
v_miles_walked numeric(4, 2);
v_duration time without time zone;
v_mph numeric(2, 1);
v_name text;
v_name_brand text;
v_cur CURSOR for SELECT hs.day_walked, hs.cal_burned, hs.miles_walked, hs.duration, hs.mph, tr.name, sb.name_brand
FROM hiking_stats AS hs
INNER JOIN hiking_trail AS ht
ON hs.hike_id = ht.th_id
INNER JOIN trail_route AS tr
ON ht.tr_id = tr.trail_id
INNER JOIN shoe_brand AS sb
ON hs.shoe_id = sb.shoe_id
WHERE extract(month FROM hs.day_walked) = 5
ORDER BY hs.day_walked ASC;
BEGIN
OPEN v_cur;
<<get_stats>>
LOOP
FETCH v_cur INTO v_day_walked, v_cal_burned, v_miles_walked, v_duration, v_mph, v_name, v_name_brand;
EXIT WHEN NOT FOUND;
INSERT INTO hiking_month_total(day_hiked, calories_burned, miles,
duration, pace, trail_hiked, shoes_worn)
VALUES(v_day_walked, v_cal_burned, v_miles_walked, v_duration, v_mph, v_name, v_name_brand);
END LOOP get_stats;
CLOSE v_cur;
END;
$month_stats$ LANGUAGE PLpgSQL;

Laten we de functie maandelijks_total_stats() aanroepen om de INSERT uit te voeren:

fitness=> SELECT monthly_total_stats();
monthly_total_stats
---------------------
(1 row)

Omdat de functie RETURNS void is gedefinieerd, kunnen we zien dat er geen waarde wordt geretourneerd aan de beller.

Op dit moment ben ik niet specifiek geïnteresseerd in retourwaarden,

alleen dat de functie de gedefinieerde bewerking uitvoert en de tabel hiking_month_total vult.

Ik zal opvragen voor een telling van records in de doeltabel, om te bevestigen dat deze gegevens bevat:

fitness=> SELECT COUNT(*) FROM hiking_month_total;
count
-------
25
(1 row)

De functie Month_total_stats() werkt, maar misschien is het een beter gebruik voor een CURSOR om door een groot aantal records te bladeren. Misschien een tafel met ongeveer een half miljoen records?

Deze volgende CURSOR is gebonden aan een query die is gericht op de data_staging-tabel uit de reeks vergelijkingen in de bovenstaande sectie:

CREATE OR REPLACE FUNCTION location_curs()
RETURNS refcursor
AS $location$
DECLARE
v_cur refcursor;
BEGIN
OPEN v_cur for SELECT segment_num, latitude, longitude, proj_code, asbuilt_flag FROM data_staging;
RETURN v_cur;
END;
$location$ LANGUAGE PLpgSQL;

Om deze CURSOR te gebruiken, moet u werken binnen een TRANSACTIE (zoals aangegeven in de documentatie hier).

location=# BEGIN;
BEGIN
location=# SELECT location_curs();
location_curs 
--------------------
<unnamed portal 1>
(1 row)

Dus wat kun je doen met deze ""?

Hier zijn slechts een paar dingen:

We kunnen de eerste rij van de CURSOR teruggeven met ofwel eerste ofwel ABSOLUTE 1:

location=# FETCH first FROM "<unnamed portal 1>";
segment_num | latitude | longitude | proj_code | asbuilt_flag 
-------------+------------------+-------------------+-----------+--------------
" 3571" | " 29.0202942600" | " -90.2908612800" | 72 | "Y"
(1 row)

location=# FETCH ABSOLUTE 1 FROM "<unnamed portal 1>";
segment_num | latitude | longitude | proj_code | asbuilt_flag 
-------------+------------------+-------------------+-----------+--------------
" 3571" | " 29.0202942600" | " -90.2908612800" | 72 | "Y"
(1 row)

Wil je een rij bijna halverwege de resultatenset? (Ervan uitgaande dat we weten dat er naar schatting een half miljoen rijen aan de CURSOR zijn gekoppeld.)

Kun je zo 'specifiek' zijn met een CURSOR?

Ja.

We kunnen de waarden voor het record op rij 234888 positioneren en FETCHEREN (slechts een willekeurig nummer dat ik heb gekozen):

location=# FETCH ABSOLUTE 234888 FROM "<unnamed portal 1>";
segment_num | latitude | longitude | proj_code | asbuilt_flag 
-------------+------------------+-------------------+-----------+--------------
" 11261" | " 28.1159541400" | " -90.7778003500" | 10 | "Y"
(1 row)

Eenmaal daar gepositioneerd, kunnen we de CURSOR 'een achteruit' verplaatsen:

location=# FETCH BACKWARD FROM "<unnamed portal 1>";
segment_num | latitude | longitude | proj_code | asbuilt_flag 
-------------+------------------+-------------------+-----------+--------------
" 11261" | " 28.1159358200" | " -90.7778242300" | 10 | "Y"
(1 row)

Wat hetzelfde is als:

location=# FETCH ABSOLUTE 234887 FROM "<unnamed portal 1>";
segment_num | latitude | longitude | proj_code | asbuilt_flag 
-------------+------------------+-------------------+-----------+--------------
" 11261" | " 28.1159358200" | " -90.7778242300" | 10 | "Y"
(1 row)

Dan kunnen we de CURSOR weer terugzetten naar de ABSOLUTE 234888 met:

location=# FETCH FORWARD FROM "<unnamed portal 1>";
segment_num | latitude | longitude | proj_code | asbuilt_flag 
-------------+------------------+-------------------+-----------+--------------
" 11261" | " 28.1159541400" | " -90.7778003500" | 10 | "Y"
(1 row)

Handige tip:om de CURSOR te verplaatsen, gebruik je MOVE in plaats van FETCH als je de waarden uit die rij niet nodig hebt.

Zie deze passage uit de documentatie:

"MOVE verplaatst een cursor zonder gegevens op te halen. MOVE werkt precies hetzelfde als het FETCH-commando, behalve dat het alleen de cursor positioneert en geen rijen teruggeeft."

De naam "" is generiek en kan in plaats daarvan ook 'benoemd' worden.

Ik zal mijn fitnessstatistieken opnieuw bekijken om een ​​functie te schrijven en de CURSOR een naam te geven, samen met een mogelijke 'real-world' use case.

De CURSOR zal zich richten op deze extra tabel, die resultaten opslaat die niet beperkt zijn tot de maand mei (in feite alles wat ik tot nu toe heb verzameld) zoals in het vorige voorbeeld:

fitness=> CREATE TABLE cp_hiking_total AS SELECT * FROM hiking_month_total WITH NO DATA;
CREATE TABLE AS

Vul het vervolgens met gegevens:

fitness=> INSERT INTO cp_hiking_total 
SELECT hs.day_walked, hs.cal_burned, hs.miles_walked, hs.duration, hs.mph, tr.name, sb.name_brand
FROM hiking_stats AS hs
INNER JOIN hiking_trail AS ht
ON hs.hike_id = ht.th_id
INNER JOIN trail_route AS tr
ON ht.tr_id = tr.trail_id
INNER JOIN shoe_brand AS sb
ON hs.shoe_id = sb.shoe_id
ORDER BY hs.day_walked ASC;
INSERT 0 51

MAAK nu met de onderstaande PLpgSQL-functie een 'benoemde' CURSOR:

CREATE OR REPLACE FUNCTION stats_cursor(refcursor)
RETURNS refcursor
AS $$
BEGIN
OPEN $1 FOR
SELECT *
FROM cp_hiking_total;
RETURN $1;
END;
$$ LANGUAGE plpgsql;

Ik noem dit CURSOR 'statistieken':

fitness=> BEGIN;
BEGIN
fitness=> SELECT stats_cursor('stats');
stats_cursor 
--------------
stats
(1 row)

Stel dat ik de '12e' rij wil verbinden met de CURSOR.

Ik kan de CURSOR op die rij plaatsen en die resultaten ophalen met het onderstaande commando:

fitness=> FETCH ABSOLUTE 12 FROM stats;
day_hiked | calories_burned | miles | duration | pace | trail_hiked | shoes_worn 
------------+-----------------+-------+----------+------+---------------------+---------------------------------------
2018-05-02 | 311.2 | 3.27 | 00:57:13 | 3.4 | Tree Trail-extended | New Balance Trail Runners-All Terrain
(1 row)

Voor de doeleinden van deze blogpost, stel je voor dat ik uit eerste hand weet dat de waarde van de tempokolom voor deze rij onjuist is.

Ik herinner me specifiek dat ik die dag 'doodmoe' was en tijdens die wandeling slechts een tempo van 3,0 aanhield. (Hé, het gebeurt.)

Oké, ik zal gewoon de cp_hiking_total tabel BIJWERKEN om die verandering weer te geven.

Relatief eenvoudig ongetwijfeld. Saai…

Hoe zit het met de statistieken CURSOR in plaats daarvan?

fitness=> UPDATE cp_hiking_total
fitness-> SET pace = 3.0
fitness-> WHERE CURRENT OF stats;
UPDATE 1

Om deze wijziging permanent te maken, geeft u COMMIT op:

fitness=> COMMIT;
COMMIT

Laten we een vraag stellen en zien dat UPDATE wordt weergegeven in tabel cp_hiking_total:

fitness=> SELECT * FROM cp_hiking_total
fitness-> WHERE day_hiked = '2018-05-02';
day_hiked | calories_burned | miles | duration | pace | trail_hiked | shoes_worn 
------------+-----------------+-------+----------+------+---------------------+---------------------------------------
2018-05-02 | 311.2 | 3.27 | 00:57:13 | 3.0 | Tree Trail-extended | New Balance Trail Runners-All Terrain
(1 row)

Hoe cool is dat?

Verplaatsen binnen de resultatenset van de CURSOR en indien nodig een UPDATE uitvoeren.

Best krachtig als je het mij vraagt. En handig.

Enige 'voorzichtigheid' en informatie uit de documentatie over dit type CURSOR:

"Het wordt over het algemeen aanbevolen om FOR UPDATE te gebruiken als de cursor bedoeld is om te worden gebruikt met UPDATE ... WHERE CURRENT OF of DELETE ... WHERE CURRENT OF. Het gebruik van FOR UPDATE voorkomt dat andere sessies de rijen tussen de tijd wijzigen ze worden opgehaald en het tijdstip waarop ze worden bijgewerkt. Zonder FOR UPDATE heeft een volgend WHERE CURRENT OF-commando geen effect als de rij is gewijzigd sinds de cursor is gemaakt.

Een andere reden om FOR UPDATE te gebruiken is dat zonder dit een volgende WHERE CURRENT OF zou kunnen mislukken als de cursorquery niet voldoet aan de regels van de SQL-standaard om "eenvoudig bij te werken" (in het bijzonder moet de cursor naar slechts één tabel verwijzen en gebruik geen groepering of ORDER BY). Cursors die niet eenvoudig kunnen worden bijgewerkt, werken mogelijk wel of niet, afhankelijk van de details van de abonnementskeuze; dus in het ergste geval kan een toepassing werken tijdens het testen en vervolgens mislukken in de productie."

Met de CURSOR die ik hier heb gebruikt, heb ik de standaard SQL-regels gevolgd (uit de bovenstaande passages) in het aspect van:Ik heb slechts naar één tabel verwezen, zonder groepering of ORDER per clausule.

Waarom het belangrijk is.

Zoals met talloze bewerkingen, query's of taken in PostgreSQL (en SQL in het algemeen), is er meestal meer dan één manier om uw einddoel te bereiken en te bereiken. Dat is een van de belangrijkste redenen waarom ik me aangetrokken voel tot SQL en ernaar streef om meer te leren.

Ik hoop dat ik met deze vervolgblogpost enig inzicht heb gegeven in waarom de UPDATE met meerdere rijen met CASE was opgenomen als een van mijn favoriete zoekopdrachten, in die eerste begeleidende blogpost. Het is voor mij de moeite waard om het gewoon als optie te hebben.

Daarnaast het verkennen van CURSORS, voor het doorlopen van grote resultatensets. Het uitvoeren van DML-bewerkingen, zoals UPDATES en/of DELETES, met het juiste type CURSOR, is gewoon 'kers op de taart'. Ik wil ze graag verder bestuderen voor meer gebruiksscenario's.


  1. Schending van UNIQUE KEY-beperking op INSERT WHERE COUNT(*) =0 op SQL Server 2005

  2. Een tabel bijwerken in Oracle als een veldwaarde Null is en bepalen of de update succesvol is

  3. Wat kan intermitterende ORA-12519 (TNS:geen geschikte handler gevonden) fouten veroorzaken?

  4. Taken op afstand uitvoeren vanuit IRI Workbench