PostgreSQL antipatterns: "Csak egynek kell lennie!"
Az SQL-ben azt írja le, hogy „mit” szeretne elérni, nem pedig azt, hogy „hogyan” kell végrehajtani. Ezért az SQL-lekérdezések „ahogyan hallják, így írják” stílusban történő fejlesztésének problémája kerül megtisztelő helyére. a feltételek számításának jellemzői SQL-ben.
Ma, rendkívül egyszerű példákon keresztül, nézzük meg, mire vezethet ez a használat kontextusában GROUP/DISTINCT и LIMIT velük.
Most, ha írtál a kérésben „Először kösd össze ezeket a jeleket, majd dobd ki az összes másolatot, csak egy maradjon másolat minden kulcshoz" - pontosan így fog működni, még akkor is, ha a csatlakozásra egyáltalán nem volt szükség.
És néha szerencséd van, és „csak működik”, néha kellemetlen hatással van a teljesítményre, néha pedig a fejlesztői szemszögből teljesen váratlan hatásokat ad.
Nos, talán nem annyira látványos, de...
„Édes pár”: CSATLAKOZÁS + ELTÁVOLÍTÁS
SELECT DISTINCT
X.*
FROM
X
JOIN
Y
ON Y.fk = X.pk
WHERE
Y.bool_condition;
Világos lenne, mit akarnak válassza ki azokat az X rekordokat, amelyekhez Y-ban vannak olyan rekordok, amelyek a teljesített feltételhez kapcsolódnak. Kérést írt a következőn keresztül JOIN - többször kapott néhány pk értéket (pontosan hány megfelelő bejegyzés jelent meg Y-ban). Hogyan kell eltávolítani? Biztosan DISTINCT!
Különösen „örömteli”, amikor minden X-rekordhoz több száz kapcsolódó Y-rekord tartozik, majd a duplikátumokat hősiesen eltávolítják...
Hogyan lehet javítani? Kezdésként vegye észre, hogy a probléma módosítható „válassza ki azokat az X rekordokat, amelyekhez Y-ban LEGALÁBB EGY van társítva a teljesített feltételhez” - elvégre magából az Y-rekordból nem kell semmi.
Beágyazott LÉTEZIK
SELECT
*
FROM
X
WHERE
EXISTS(
SELECT
NULL
FROM
Y
WHERE
fk = X.pk AND
bool_condition
LIMIT 1
);
A PostgreSQL egyes verziói megértik, hogy az EXISTS-ben elegendő megtalálni az első bejegyzést, a régebbiek nem. Ezért inkább mindig jelzem LIMIT 1 belső EXISTS.
OLDALOS CSATLAKOZÁS
SELECT
X.*
FROM
X
, LATERAL (
SELECT
Y.*
FROM
Y
WHERE
fk = X.pk AND
bool_condition
LIMIT 1
) Y
WHERE
Y IS DISTINCT FROM NULL;
„Miért fizetne többet”: KÜLÖNBÖZŐ [BE] + 1. KORLÁT
Az ilyen lekérdezéstranszformációk további előnye, hogy könnyen korlátozható a rekordok keresése, ha csak egy vagy néhány szükséges belőlük, mint a következő esetben:
SELECT DISTINCT ON(X.pk)
*
FROM
X
JOIN
Y
ON Y.fk = X.pk
LIMIT 1;
Most elolvassuk a kérést, és megpróbáljuk megérteni, mit javasolnak a DBMS-nek:
a jelek összekötése
egyedi az X.pk
a fennmaradó bejegyzések közül válasszon ki egyet
Szóval mit kaptál? "Csak egy belépés" az egyediek közül - és ha ezt vesszük a nem egyediek közül, akkor változni fog valahogy az eredmény?.. "És ha nincs különbség, akkor minek fizessenek többet?"
SELECT
*
FROM
(
SELECT
*
FROM
X
-- сюда можно подсунуть подходящих условий
LIMIT 1 -- +1 Limit
) X
JOIN
Y
ON Y.fk = X.pk
LIMIT 1;
És pontosan ugyanaz a téma GROUP BY + LIMIT 1.
„Csak meg kell kérdeznem”: implicit GROUP + LIMIT
Hasonló dolgok máshol fordulnak elő nem-üresség ellenőrzések aláírja vagy CTE-k a kérés előrehaladtával:
...
CASE
WHEN (
SELECT
count(*)
FROM
X
LIMIT 1
) = 0 THEN ...
Összesített függvények (count/min/max/sum/...) sikeresen végrehajtódnak a teljes készleten, még kifejezett utasítások nélkül is GROUP BY. Csak vele LIMIT nem túl barátságosak.
A fejlesztő gondolkodhat „Ha vannak ott rekordok, akkor nem kell több, mint LIMIT”. De ezt ne tedd! Mert az alapnak ez:
azt számolják, amit akarnak minden feljegyzés szerint
annyi sort adjanak, amennyit kérnek
A célfeltételektől függően célszerű a következő helyettesítések egyikét végrehajtani:
(count + LIMIT 1) = 0onNOT EXISTS(LIMIT 1)
(count + LIMIT 1) > 0onEXISTS(LIMIT 1)
count >= Non(SELECT count(*) FROM (... LIMIT N))
„Mennyi akasztható grammban”: DISTINCT + LIMIT
SELECT DISTINCT
pk
FROM
X
LIMIT $1
Egy naiv fejlesztő őszintén azt hiheti, hogy a kérés végrehajtása leáll. amint találunk 1 dollárt az első talált különböző értékekből.
Valamikor a jövőben ez működhet és működni fog egy új csomópontnak köszönhetően Index Skip Scan, melynek megvalósítása jelenleg kidolgozás alatt van, de még nem.
Egyelőre először minden rekord le lesz kérve, egyediek, és csak tőlük jár vissza a kért összeg. Különösen szomorú, ha valami ilyesmit akartunk $ 1 = 4, és több százezer rekord van a táblázatban...