# =============================================================================
# These queries are from the PostGIS tutorial at https://postgis.net/workshops/postgis-intro/.
# =============================================================================

import file=postgis_tutorial_schema
----

import file=postgis_tutorial_stats
----

# 11.2b
opt
SELECT
    name, boroname
FROM
    nyc_neighborhoods
WHERE
    st_intersects(
        geom,
        st_geomfromtext('POINT(583571 4506714)', 26918)
    )
ORDER BY
    name, boroname
----
sort
 ├── columns: name:3 boroname:2
 ├── immutable
 ├── ordering: +3,+2
 └── project
      ├── columns: boroname:2 name:3
      ├── immutable
      └── select
           ├── columns: boroname:2 name:3 geom:4!null
           ├── immutable
           ├── scan nyc_neighborhoods
           │    └── columns: boroname:2 name:3 geom:4
           └── filters
                └── st_intersects(geom:4, '0101000020266900000000000026CF21410000008016315141') [outer=(4), immutable, constraints=(/4: (/NULL - ])]

# 11.6
opt
SELECT
    name
FROM
    nyc_streets
WHERE
    st_dwithin(
        geom,
        st_geomfromtext('POINT(583571 4506714)', 26918),
        10
    )
ORDER BY
    name ASC
----
sort
 ├── columns: name:3
 ├── immutable
 ├── ordering: +3
 └── project
      ├── columns: name:3
      ├── immutable
      └── select
           ├── columns: name:3 geom:6!null
           ├── immutable
           ├── scan nyc_streets
           │    └── columns: name:3 geom:6
           └── filters
                └── st_dwithin(geom:6, '0101000020266900000000000026CF21410000008016315141', 10.0) [outer=(6), immutable, constraints=(/6: (/NULL - ])]

# 12.1.2
opt
SELECT
    name, boroname
FROM
    nyc_neighborhoods
WHERE
    st_intersects(
        geom,
        st_geomfromtext(
            'LINESTRING(586782 4504202,586864 4504216)',
            26918
        )
    )
ORDER BY
    name, boroname
----
sort
 ├── columns: name:3 boroname:2
 ├── immutable
 ├── ordering: +3,+2
 └── project
      ├── columns: boroname:2 name:3
      ├── immutable
      └── select
           ├── columns: boroname:2 name:3 geom:4!null
           ├── immutable
           ├── scan nyc_neighborhoods
           │    └── columns: boroname:2 name:3 geom:4
           └── filters
                └── st_intersects(geom:4, '01020000202669000002000000000000003CE8214100000080A22E514100000000E0E8214100000000A62E5141') [outer=(4), immutable, constraints=(/4: (/NULL - ])]

# 12.2.3
opt
SELECT
    name
FROM
    nyc_streets
WHERE
    st_dwithin(
        geom,
        st_geomfromtext(
            'LINESTRING(586782 4504202,586864 4504216)',
            26918
        ),
        0.1
    )
ORDER BY
    name
----
sort
 ├── columns: name:3
 ├── immutable
 ├── ordering: +3
 └── project
      ├── columns: name:3
      ├── immutable
      └── select
           ├── columns: name:3 geom:6!null
           ├── immutable
           ├── scan nyc_streets
           │    └── columns: name:3 geom:6
           └── filters
                └── st_dwithin(geom:6, '01020000202669000002000000000000003CE8214100000080A22E514100000000E0E8214100000000A62E5141', 0.1) [outer=(6), immutable, constraints=(/6: (/NULL - ])]

# 12.2.4
opt
SELECT
    sum(popn_total)
FROM
    nyc_census_blocks
WHERE
    st_dwithin(
        geom,
        st_geomfromtext(
            'LINESTRING(586782 4504202,586864 4504216)',
            26918
        ),
        50
    )
----
scalar-group-by
 ├── columns: sum:13
 ├── cardinality: [1 - 1]
 ├── immutable
 ├── key: ()
 ├── fd: ()-->(13)
 ├── select
 │    ├── columns: popn_total:3 geom:10!null
 │    ├── immutable
 │    ├── scan nyc_census_blocks
 │    │    └── columns: popn_total:3 geom:10
 │    └── filters
 │         └── st_dwithin(geom:10, '01020000202669000002000000000000003CE8214100000080A22E514100000000E0E8214100000000A62E5141', 50.0) [outer=(10), immutable, constraints=(/10: (/NULL - ])]
 └── aggregations
      └── sum [as=sum:13, outer=(3)]
           └── popn_total:3

# 13.0
opt
SELECT
    subways.name AS subway_name,
    neighborhoods.name AS neighborhood_name,
    neighborhoods.boroname AS borough
FROM
    nyc_neighborhoods AS neighborhoods
    JOIN nyc_subway_stations AS subways ON
            st_contains(neighborhoods.geom, subways.geom)
WHERE
    subways.name = 'Broad St'
ORDER BY
    subway_name, neighborhood_name, borough
----
sort
 ├── columns: subway_name:10!null neighborhood_name:3 borough:2
 ├── immutable
 ├── fd: ()-->(10)
 ├── ordering: +3,+2 opt(10) [actual: +3,+2]
 └── project
      ├── columns: boroname:2 neighborhoods.name:3 subways.name:10!null
      ├── immutable
      ├── fd: ()-->(10)
      └── inner-join (cross)
           ├── columns: boroname:2 neighborhoods.name:3 neighborhoods.geom:4!null subways.name:10!null subways.geom:22!null
           ├── immutable
           ├── fd: ()-->(10)
           ├── scan nyc_neighborhoods [as=neighborhoods]
           │    └── columns: boroname:2 neighborhoods.name:3 neighborhoods.geom:4
           ├── select
           │    ├── columns: subways.name:10!null subways.geom:22
           │    ├── fd: ()-->(10)
           │    ├── scan nyc_subway_stations [as=subways]
           │    │    └── columns: subways.name:10 subways.geom:22
           │    └── filters
           │         └── subways.name:10 = 'Broad St' [outer=(10), constraints=(/10: [/'Broad St' - /'Broad St']; tight), fd=()-->(10)]
           └── filters
                └── st_contains(neighborhoods.geom:4, subways.geom:22) [outer=(4,22), immutable, constraints=(/4: (/NULL - ]; /22: (/NULL - ])]

# 13.1a
opt
SELECT
    neighborhoods.name AS neighborhood_name,
    sum(census.popn_total) AS population,
    100.0 * sum(census.popn_white) / sum(census.popn_total)
        AS white_pct,
    100.0 * sum(census.popn_black) / sum(census.popn_total)
        AS black_pct
FROM
    nyc_neighborhoods AS neighborhoods
    JOIN nyc_census_blocks AS census ON
            st_intersects(neighborhoods.geom, census.geom)
WHERE
    neighborhoods.boroname = 'Manhattan'
GROUP BY
    neighborhoods.name
ORDER BY
    white_pct DESC
----
sort
 ├── columns: neighborhood_name:3 population:19 white_pct:22 black_pct:23
 ├── immutable
 ├── key: (3)
 ├── fd: (3)-->(19,22,23)
 ├── ordering: -22
 └── project
      ├── columns: white_pct:22 black_pct:23 name:3 sum:19
      ├── immutable
      ├── key: (3)
      ├── fd: (3)-->(19,22,23)
      ├── group-by (hash)
      │    ├── columns: name:3 sum:19 sum:20 sum:21
      │    ├── grouping columns: name:3
      │    ├── immutable
      │    ├── key: (3)
      │    ├── fd: (3)-->(19-21)
      │    ├── inner-join (cross)
      │    │    ├── columns: neighborhoods.boroname:2!null name:3 neighborhoods.geom:4!null popn_total:9 popn_white:10 popn_black:11 census.geom:16!null
      │    │    ├── immutable
      │    │    ├── fd: ()-->(2)
      │    │    ├── scan nyc_census_blocks [as=census]
      │    │    │    └── columns: popn_total:9 popn_white:10 popn_black:11 census.geom:16
      │    │    ├── select
      │    │    │    ├── columns: neighborhoods.boroname:2!null name:3 neighborhoods.geom:4
      │    │    │    ├── fd: ()-->(2)
      │    │    │    ├── scan nyc_neighborhoods [as=neighborhoods]
      │    │    │    │    └── columns: neighborhoods.boroname:2 name:3 neighborhoods.geom:4
      │    │    │    └── filters
      │    │    │         └── neighborhoods.boroname:2 = 'Manhattan' [outer=(2), constraints=(/2: [/'Manhattan' - /'Manhattan']; tight), fd=()-->(2)]
      │    │    └── filters
      │    │         └── st_intersects(neighborhoods.geom:4, census.geom:16) [outer=(4,16), immutable, constraints=(/4: (/NULL - ]; /16: (/NULL - ])]
      │    └── aggregations
      │         ├── sum [as=sum:19, outer=(9)]
      │         │    └── popn_total:9
      │         ├── sum [as=sum:20, outer=(10)]
      │         │    └── popn_white:10
      │         └── sum [as=sum:21, outer=(11)]
      │              └── popn_black:11
      └── projections
           ├── (sum:20 * 100.0) / sum:19 [as=white_pct:22, outer=(19,20), immutable]
           └── (sum:21 * 100.0) / sum:19 [as=black_pct:23, outer=(19,21), immutable]

# 13.1c
opt
SELECT
    100.0 * sum(popn_white) / sum(popn_total) AS white_pct,
    100.0 * sum(popn_black) / sum(popn_total) AS black_pct,
    sum(popn_total) AS popn_total
FROM
    nyc_census_blocks AS census
    JOIN nyc_subway_stations AS subways ON
            st_dwithin(census.geom, subways.geom, 200)
WHERE
    strpos(subways.routes, 'A') > 0
----
project
 ├── columns: white_pct:34 black_pct:35 popn_total:32
 ├── cardinality: [1 - 1]
 ├── immutable
 ├── key: ()
 ├── fd: ()-->(32,34,35)
 ├── scalar-group-by
 │    ├── columns: sum:31 sum:32 sum:33
 │    ├── cardinality: [1 - 1]
 │    ├── immutable
 │    ├── key: ()
 │    ├── fd: ()-->(31-33)
 │    ├── inner-join (cross)
 │    │    ├── columns: popn_total:3 popn_white:4 popn_black:5 census.geom:10!null routes:23 subways.geom:28!null
 │    │    ├── immutable
 │    │    ├── scan nyc_census_blocks [as=census]
 │    │    │    └── columns: popn_total:3 popn_white:4 popn_black:5 census.geom:10
 │    │    ├── select
 │    │    │    ├── columns: routes:23 subways.geom:28
 │    │    │    ├── immutable
 │    │    │    ├── scan nyc_subway_stations [as=subways]
 │    │    │    │    └── columns: routes:23 subways.geom:28
 │    │    │    └── filters
 │    │    │         └── strpos(routes:23, 'A') > 0 [outer=(23), immutable]
 │    │    └── filters
 │    │         └── st_dwithin(census.geom:10, subways.geom:28, 200.0) [outer=(10,28), immutable, constraints=(/10: (/NULL - ]; /28: (/NULL - ])]
 │    └── aggregations
 │         ├── sum [as=sum:31, outer=(4)]
 │         │    └── popn_white:4
 │         ├── sum [as=sum:32, outer=(3)]
 │         │    └── popn_total:3
 │         └── sum [as=sum:33, outer=(5)]
 │              └── popn_black:5
 └── projections
      ├── (sum:31 * 100.0) / sum:32 [as=white_pct:34, outer=(31,32), immutable]
      └── (sum:33 * 100.0) / sum:32 [as=black_pct:35, outer=(32,33), immutable]

# 13.2
# The optimal plan for this query is to join census with subways and
# then with lines.
opt
SELECT
    lines.route,
    100.0 * sum(popn_white) / sum(popn_total) AS white_pct,
    100.0 * sum(popn_black) / sum(popn_total) AS black_pct,
    sum(popn_total) AS popn_total
FROM
    nyc_census_blocks AS census
    JOIN nyc_subway_stations AS subways ON
            st_dwithin(census.geom, subways.geom, 200)
    JOIN subway_lines AS lines ON
            strpos(subways.routes, lines.route) > 0
GROUP BY
    lines.route
ORDER BY
    black_pct DESC
----
sort
 ├── columns: route:31 white_pct:38 black_pct:39 popn_total:36
 ├── immutable
 ├── key: (31)
 ├── fd: (31)-->(36,38,39)
 ├── ordering: -39
 └── project
      ├── columns: white_pct:38 black_pct:39 route:31 sum:36
      ├── immutable
      ├── key: (31)
      ├── fd: (31)-->(36,38,39)
      ├── group-by (hash)
      │    ├── columns: route:31 sum:35 sum:36 sum:37
      │    ├── grouping columns: route:31
      │    ├── immutable
      │    ├── key: (31)
      │    ├── fd: (31)-->(35-37)
      │    ├── inner-join (cross)
      │    │    ├── columns: popn_total:3 popn_white:4 popn_black:5 census.geom:10!null routes:23 subways.geom:28!null route:31
      │    │    ├── immutable
      │    │    ├── inner-join (cross)
      │    │    │    ├── columns: popn_total:3 popn_white:4 popn_black:5 census.geom:10!null routes:23 subways.geom:28!null
      │    │    │    ├── immutable
      │    │    │    ├── scan nyc_census_blocks [as=census]
      │    │    │    │    └── columns: popn_total:3 popn_white:4 popn_black:5 census.geom:10
      │    │    │    ├── scan nyc_subway_stations [as=subways]
      │    │    │    │    └── columns: routes:23 subways.geom:28
      │    │    │    └── filters
      │    │    │         └── st_dwithin(census.geom:10, subways.geom:28, 200.0) [outer=(10,28), immutable, constraints=(/10: (/NULL - ]; /28: (/NULL - ])]
      │    │    ├── scan subway_lines [as=lines]
      │    │    │    └── columns: route:31
      │    │    └── filters
      │    │         └── strpos(routes:23, route:31) > 0 [outer=(23,31), immutable]
      │    └── aggregations
      │         ├── sum [as=sum:35, outer=(4)]
      │         │    └── popn_white:4
      │         ├── sum [as=sum:36, outer=(3)]
      │         │    └── popn_total:3
      │         └── sum [as=sum:37, outer=(5)]
      │              └── popn_black:5
      └── projections
           ├── (sum:35 * 100.0) / sum:36 [as=white_pct:38, outer=(35,36), immutable]
           └── (sum:37 * 100.0) / sum:36 [as=black_pct:39, outer=(36,37), immutable]

# 14.1a
opt
SELECT
    s.name, s.routes
FROM
    nyc_subway_stations AS s
    JOIN nyc_neighborhoods AS n ON
            st_contains(n.geom, s.geom)
WHERE
    n.name = 'Little Italy'
----
project
 ├── columns: name:4 routes:11
 ├── immutable
 └── inner-join (cross)
      ├── columns: s.name:4 routes:11 s.geom:16!null n.name:21!null n.geom:22!null
      ├── immutable
      ├── fd: ()-->(21)
      ├── scan nyc_subway_stations [as=s]
      │    └── columns: s.name:4 routes:11 s.geom:16
      ├── select
      │    ├── columns: n.name:21!null n.geom:22
      │    ├── fd: ()-->(21)
      │    ├── scan nyc_neighborhoods [as=n]
      │    │    └── columns: n.name:21 n.geom:22
      │    └── filters
      │         └── n.name:21 = 'Little Italy' [outer=(21), constraints=(/21: [/'Little Italy' - /'Little Italy']; tight), fd=()-->(21)]
      └── filters
           └── st_contains(n.geom:22, s.geom:16) [outer=(16,22), immutable, constraints=(/16: (/NULL - ]; /22: (/NULL - ])]

# 14.2b
opt
SELECT
    DISTINCT n.name, n.boroname
FROM
    nyc_subway_stations AS s
    JOIN nyc_neighborhoods AS n ON
            st_contains(n.geom, s.geom)
WHERE
    strpos(s.routes, '6') > 0
ORDER BY
    n.name, n.boroname
----
sort
 ├── columns: name:21 boroname:20
 ├── immutable
 ├── key: (20,21)
 ├── ordering: +21,+20
 └── distinct-on
      ├── columns: boroname:20 n.name:21
      ├── grouping columns: boroname:20 n.name:21
      ├── immutable
      ├── key: (20,21)
      └── inner-join (cross)
           ├── columns: routes:11 s.geom:16!null boroname:20 n.name:21 n.geom:22!null
           ├── immutable
           ├── select
           │    ├── columns: routes:11 s.geom:16
           │    ├── immutable
           │    ├── scan nyc_subway_stations [as=s]
           │    │    └── columns: routes:11 s.geom:16
           │    └── filters
           │         └── strpos(routes:11, '6') > 0 [outer=(11), immutable]
           ├── scan nyc_neighborhoods [as=n]
           │    └── columns: boroname:20 n.name:21 n.geom:22
           └── filters
                └── st_contains(n.geom:22, s.geom:16) [outer=(16,22), immutable, constraints=(/16: (/NULL - ]; /22: (/NULL - ])]

# 14.2c
opt
SELECT
    sum(popn_total)
FROM
    nyc_neighborhoods AS n
    JOIN nyc_census_blocks AS c ON
            st_intersects(n.geom, c.geom)
WHERE
    n.name = 'Battery Park'
----
scalar-group-by
 ├── columns: sum:19
 ├── cardinality: [1 - 1]
 ├── immutable
 ├── key: ()
 ├── fd: ()-->(19)
 ├── inner-join (cross)
 │    ├── columns: name:3!null n.geom:4!null popn_total:9 c.geom:16!null
 │    ├── immutable
 │    ├── fd: ()-->(3)
 │    ├── scan nyc_census_blocks [as=c]
 │    │    └── columns: popn_total:9 c.geom:16
 │    ├── select
 │    │    ├── columns: name:3!null n.geom:4
 │    │    ├── fd: ()-->(3)
 │    │    ├── scan nyc_neighborhoods [as=n]
 │    │    │    └── columns: name:3 n.geom:4
 │    │    └── filters
 │    │         └── name:3 = 'Battery Park' [outer=(3), constraints=(/3: [/'Battery Park' - /'Battery Park']; tight), fd=()-->(3)]
 │    └── filters
 │         └── st_intersects(n.geom:4, c.geom:16) [outer=(4,16), immutable, constraints=(/4: (/NULL - ]; /16: (/NULL - ])]
 └── aggregations
      └── sum [as=sum:19, outer=(9)]
           └── popn_total:9

# 14.3c
opt
SELECT
    n.name,
    sum(c.popn_total) / (st_area(n.geom) / 1000000.0)
        AS popn_per_sqkm
FROM
    nyc_census_blocks AS c
    JOIN nyc_neighborhoods AS n ON
            st_intersects(c.geom, n.geom)
WHERE
    n.name = 'Upper West Side' OR n.name = 'Upper East Side'
GROUP BY
    n.name, n.geom
ORDER BY
    n.name
----
sort
 ├── columns: name:15!null popn_per_sqkm:20
 ├── immutable
 ├── ordering: +15
 └── project
      ├── columns: popn_per_sqkm:20 name:15!null
      ├── immutable
      ├── group-by (hash)
      │    ├── columns: name:15!null n.geom:16!null sum:19
      │    ├── grouping columns: name:15!null n.geom:16!null
      │    ├── immutable
      │    ├── key: (15,16)
      │    ├── fd: (15,16)-->(19)
      │    ├── inner-join (cross)
      │    │    ├── columns: popn_total:3 c.geom:10!null name:15!null n.geom:16!null
      │    │    ├── immutable
      │    │    ├── scan nyc_census_blocks [as=c]
      │    │    │    └── columns: popn_total:3 c.geom:10
      │    │    ├── select
      │    │    │    ├── columns: name:15!null n.geom:16
      │    │    │    ├── scan nyc_neighborhoods [as=n]
      │    │    │    │    └── columns: name:15 n.geom:16
      │    │    │    └── filters
      │    │    │         └── (name:15 = 'Upper West Side') OR (name:15 = 'Upper East Side') [outer=(15), constraints=(/15: [/'Upper East Side' - /'Upper East Side'] [/'Upper West Side' - /'Upper West Side']; tight)]
      │    │    └── filters
      │    │         └── st_intersects(c.geom:10, n.geom:16) [outer=(10,16), immutable, constraints=(/10: (/NULL - ]; /16: (/NULL - ])]
      │    └── aggregations
      │         └── sum [as=sum:19, outer=(3)]
      │              └── popn_total:3
      └── projections
           └── sum:19 / (st_area(n.geom:16) / 1e+06) [as=popn_per_sqkm:20, outer=(16,19), immutable]

# 15.0
opt
SELECT
    blocks.blkid
FROM
    nyc_census_blocks AS blocks
    JOIN nyc_subway_stations AS subways ON
            st_contains(blocks.geom, subways.geom)
WHERE
    subways.name = 'Broad St'
----
project
 ├── columns: blkid:2
 ├── immutable
 └── inner-join (cross)
      ├── columns: blkid:2 blocks.geom:10!null name:16!null subways.geom:28!null
      ├── immutable
      ├── fd: ()-->(16)
      ├── scan nyc_census_blocks [as=blocks]
      │    └── columns: blkid:2 blocks.geom:10
      ├── select
      │    ├── columns: name:16!null subways.geom:28
      │    ├── fd: ()-->(16)
      │    ├── scan nyc_subway_stations [as=subways]
      │    │    └── columns: name:16 subways.geom:28
      │    └── filters
      │         └── name:16 = 'Broad St' [outer=(16), constraints=(/16: [/'Broad St' - /'Broad St']; tight), fd=()-->(16)]
      └── filters
           └── st_contains(blocks.geom:10, subways.geom:28) [outer=(10,28), immutable, constraints=(/10: (/NULL - ]; /28: (/NULL - ])]
