sql >> Database teknologi >  >> RDS >> PostgreSQL

CTE og fødselsdagsparadokset

En interessant forespørgsel er blevet twitret af Will Leinweber fra Postgres Open:

-- this returns a different result each time it is ran
with recursive s as (
  select random()
union
  select random() from s
) select count(*) from s;

Jeg kan godt lide dette eksempel:et overraskende resultat, som kan forklares med (og faktisk hjælper med at forklare) CTE-adfærd.

Uventede sandheder betegnes med ordet "paradoks", og faktisk er dette en manifestation (en "forekomst", i programmørers jargon) af det, der er kendt som fødselsdagsparadokset .

Den enkleste formulering er sandsynligvis denne:Hvis du tilfældigt vælger 23 personer, er sandsynligheden for, at to af dem har samme fødselsdag større end 50 %.

Resultatet er uventet, for der er 366 forskellige fødselsdage, og tallet 23 virker meget lille sammenlignet med 366.

Det er dog korrekt, da det kan vises med en direkte beregning. I PostgreSQL kan vi køre en anden rekursiv CTE:

WITH RECURSIVE r(i,acc) AS (
  SELECT 1, 1 :: double precision
UNION
  SELECT i + 1,
    acc * (((366 - i) :: double precision) / 366)
  FROM r WHERE acc > 0.5
) SELECT count(1) FROM r;

producerede 23 som resultat.

En rekursiv CTE stopper, når det rekursive trin ikke tilføjer nogen nye rækker. I den sidste forespørgsel, acc repræsenterer sandsynligheden for, at den første i fødselsdage er forskellige, så rekursion stopper, når dette tal ikke er over 50%.

I forespørgslen nævnt i begyndelsen, som vi vil kalde den "tilfældige forespørgsel" for kort, afsluttes den rekursive CTE, når random() tilføjer ikke en ny række. Det vil sige, når den tilfældigt beregnede værdi allerede er blevet beregnet i en tidligere iteration; det er fordi den rekursive CTE bruger UNION i stedet for UNION ALL .

Dette er virkelig fødselsdagsparadokset, hvor 366 erstattes af det maksimalt mulige antal distinkte værdier, der random() kan producere. Hvad er det helt præcist?

random() funktion returnerer en dobbelt præcisionsværdi, hvis nøjagtige definition afhænger af systemet. Ikke alle de dobbelte præcisionsværdier kan dog produceres; den underliggende C-funktion kan producere 2^31 forskellige resultater, uanset bitstørrelsen af ​​en dobbelt præcisionsværdi. Dette er godt nok i praksis, og samtidig sikres kompatibilitet med alle de forskellige arkitekturer og biblioteksimplementeringer.

Så vi kan erstatte 366 med 2^31 i vores forespørgsel, og i stedet for 23 får vi 54563 som svar.

Kommer det tæt på det faktiske output fra den tilfældige forespørgsel? Lad os køre det et par gange, indsamle resultatet og beregne gennemsnittet:

gianni=# create table t(n int);
CREATE TABLE

gianni=# with recursive s as (
  select random()
union
  select random() from s
) insert into t select count(1) from s;
INSERT 0 1

/* repeat the last query 49 times */

gianni=# select count(1), avg(n) from t;

 count | avg
-------+--------------------
    50 | 54712.060000000000
 (1 row)

Gennemsnittet af de faktiske resultater er ret tæt på den forventede tærskel på 54563; forskellen er mindre end 0,3 %, ganske ortodoksisk , kan vi sige!


  1. SQL WHERE.. IN klausul flere kolonner

  2. Sådan opretter du en procedure inde i en pakke i Oracle

  3. Oracle hvordan man eksporterer forespørgsel til en tekst/csv-fil

  4. Netværksadapteren kunne ikke etablere forbindelsen, når den oprettede forbindelse til Oracle DB