phibble

SQL pipeline

This page uses the simulated rentals data. It does not describe real rentals.

This example runs one phplyr pipeline in two ways. On a lazy phibble table, created with ->phibble() on an Eloquent query, the pipeline builds a single SQL query that the database runs. On an in-memory DataFrame, created with ->toDataFrame(), the same pipeline runs in PHP. The page shows the generated SQL and both results.

The pipeline describes columns with col() expressions, which phibble can translate to SQL. A PHP closure in a verb would make phibble load the rows into memory first. Eloquent casts and accessors do not apply to a lazy table, because its rows come straight from SQL.

Code

$pipeline = fn ($rentals) => $rentals |>
    filter(col('bedrooms')->ge($minBedrooms)) |>
    mutate(rent_per_m2: col('monthly_rent')->div(col('area_m2'))) |>
    group_by('neighborhood') |>
    summarise(rentals: n(), mean_rent_per_m2: mean('rent_per_m2')) |>
    arrange(desc('mean_rent_per_m2'));

$sql = SimulatedRental::query()->phibble() |> $pipeline;
$sql |> show_query();

$memory = SimulatedRental::query()->toDataFrame() |> $pipeline;
$pipeline = fn ($rentals) => phplyr($rentals)
    ->filter(col('bedrooms')->ge($minBedrooms))
    ->mutate(rent_per_m2: col('monthly_rent')->div(col('area_m2')))
    ->groupBy('neighborhood')
    ->summarise(rentals: n(), mean_rent_per_m2: mean('rent_per_m2'))
    ->arrange(desc('mean_rent_per_m2'))
    ->get();

$sql = $pipeline(SimulatedRental::query()->phibble());
phplyr($sql)->showQuery();

$memory = $pipeline(SimulatedRental::query()->toDataFrame());

Generated SQL

SELECT "neighborhood", "rentals", "mean_rent_per_m2" FROM (SELECT "neighborhood", "rentals", "mean_rent_per_m2", ROW_NUMBER() OVER (ORDER BY (("neighborhood" COLLATE "C") IS NULL), ("neighborhood" COLLATE "C")) AS "__phibble_row" FROM (SELECT ("neighborhood" COLLATE "C") AS "neighborhood", COUNT(*) AS "rentals", (CASE WHEN COUNT(*) > COUNT("rent_per_m2") THEN NULL ELSE (CASE WHEN COUNT("rent_per_m2") = 0 THEN 'NaN'::float8 ELSE AVG(CAST("rent_per_m2" AS DOUBLE PRECISION)) END) END) AS "mean_rent_per_m2" FROM (SELECT "id", "neighborhood", "area_m2", "bedrooms", "monthly_rent", (CASE WHEN "area_m2" = 0 THEN (CASE WHEN "monthly_rent" > 0 THEN 'Infinity'::float8 WHEN "monthly_rent" < 0 THEN '-Infinity'::float8 WHEN "monthly_rent" = 0 THEN 'NaN'::float8 END) ELSE CAST("monthly_rent" AS DOUBLE PRECISION) / "area_m2" END) AS "rent_per_m2", "__phibble_row" FROM (SELECT * FROM (SELECT *, ROW_NUMBER() OVER () AS "__phibble_row" FROM (select * from "simulated_rentals") AS phibble_source) AS q1 WHERE ("bedrooms" >= ?)) AS q2) AS q3 GROUP BY ("neighborhood" COLLATE "C")) AS q4) AS q5 ORDER BY ("mean_rent_per_m2" IS NULL), "mean_rent_per_m2" DESC, ("__phibble_row" IS NULL), "__phibble_row"

Bindings: [2]. The database runs this one query and returns 6 rows to PHP.

Run as SQL

Neighborhood Rentals Mean rent per m²
Harbor 390 31.8965
Old Town 381 30.0595
Hillcrest 359 28.3780
University 374 27.8808
Riverside 328 26.3653
Northgate 384 24.9868

Run in memory

Neighborhood Rentals Mean rent per m²
Harbor 390 31.8965
Old Town 381 30.0595
Hillcrest 359 28.3780
University 374 27.8808
Riverside 328 26.3653
Northgate 384 24.9868

The two results have the same rows in the same order. The largest difference in mean rent per m² is 0.0e+0.