Creating a new view in BigQuery, with case statements - google-bigquery

I am trying to create a new view in BigQuery using some of the Google hosted data.
The data is for traffic collisions in New York.
For each unique day in the dataset, I want to find the borough and sum up some fields (people injured, killed, etc.)
Now, the dataset does have a borough field, but this is incomplete, and what I have seen is that there are also latitude and longitude fields. However these are also not complete.
So I see 3 scenarios.
The borough is set, use that
No borough, but there is lat and long, so use those in a sub query.
There is no lat, long or borough, so just enter an "unknown" here
to find the borough from lat and long, there is another public dataset, and I used this with a lat and long to check
SELECT UPPER(tz_loc.borough) FROM `bigquery-public-data.new_york_taxi_trips.taxi_zone_geom` tz_loc
WHERE (ST_DWithin(tz_loc.zone_geom, ST_GeogPoint(-73.94398, 40.680088),0))
and that seemed to work.
So I constructed my query like this:
CREATE VIEW `your-project-id.your_dataset_id.collisions_data_bourgh` AS
SELECT CAST(timestamp as DATE) as collision_date,
COUNT(CAST(timestamp as DATE)) as NUM_COLLISIONS,
CASE
WHEN ds.borough IS NOT NULL THEN CAST(borough as STRING) -- when the borough is set
WHEN ((ds.latitude IS NOT NULL or ds.longitude IS NOT NULL) AND ds.borough IS NULL) THEN (SELECT CAST(UPPER(tz_loc.borough)as STRING) FROM `bigquery-public-data.new_york_taxi_trips.taxi_zone_geom` tz_loc WHERE (ST_DWithin(tz_loc.zone_geom, ST_GeogPoint(CAST(ds.longitude AS FLOAT64), CAST(ds.latitude AS FLOAT64)),0))) -- when the borough is null and either lat or long is not null
WHEN (ds.latitude IS NULL OR ds.longitude IS NULL OR ds.borough IS NULL) THEN "Unknown"
END AS NEIGHBORHOOD,
SUM(CAST(number_of_cyclist_killed as INT64)) as CYCLISTS_KILLED,
SUM(CAST(number_of_cyclist_injured as INT64)) as CYCLISTS_INJURED,
SUM(CAST(number_of_motorist_killed as INT64)) as MOTORISTS_KILLED,
SUM(CAST(number_of_motorist_injured as INT64)) as MOTORISTS_INJURED,
SUM(CAST(number_of_pedestrians_killed as INT64)) as PEDS_KILLED,
SUM(CAST(number_of_pedestrians_injured as INT64)) as PEDS_INJURED,
SUM(CAST(number_of_persons_killed as INT64)) as PERSONS_KILLED,
SUM(CAST(number_of_persons_injured as INT64)) as PERSONS_INJURED,
FROM `bigquery-public-data.new_york_mv_collisions.nypd_mv_collisions` ds
GROUP BY collision_date, NEIGHBORHOOD;
And the query ran ok - however when I try to query the created view, I get an error
LEFT OUTER JOIN cannot be used without a condition that is an equality of fields from both sides of the join.
I cannot see where my error is, if it's in the logic, if it's in the way I've joined together things.

You encounter the LEFT OUTER JOIN error since you were not able to the two tables. You can join them in the WHERE clause of your subquery. Adding AND tz_loc.borough = ds.borough at your subquery should make it work.
See working query:
CREATE VIEW `your-project-id.your_dataset_id.collisions_data_bourgh` AS
SELECT
CAST(timestamp AS DATE) AS collision_date,
COUNT(CAST(timestamp AS DATE)) AS NUM_COLLISIONS,
CASE
WHEN ds.borough IS NOT NULL THEN CAST(borough AS STRING) -- when the borough is set
WHEN ((ds.latitude IS NOT NULL or ds.longitude IS NOT NULL) AND ds.borough IS NULL)
THEN (
SELECT CAST(UPPER(tz_loc.borough)as STRING)
FROM bigquery-public-data.new_york_taxi_trips.taxi_zone_geom tz_loc
WHERE
ST_DWithin(tz_loc.zone_geom,
ST_GeogPoint(CAST(ds.longitude AS FLOAT64),
CAST(ds.latitude AS FLOAT64)),0)
AND tz_loc.borough = ds.borough
)
WHEN (ds.latitude IS NULL
OR ds.longitude IS NULL
OR ds.borough IS NULL) THEN "Unknown"
END
AS NEIGHBORHOOD,
SUM(CAST(number_of_cyclist_killed AS INT64)) AS CYCLISTS_KILLED,
SUM(CAST(number_of_cyclist_injured AS INT64)) AS CYCLISTS_INJURED,
SUM(CAST(number_of_motorist_killed AS INT64)) AS MOTORISTS_KILLED,
SUM(CAST(number_of_motorist_injured AS INT64)) AS MOTORISTS_INJURED,
SUM(CAST(number_of_pedestrians_killed AS INT64)) AS PEDS_KILLED,
SUM(CAST(number_of_pedestrians_injured AS INT64)) AS PEDS_INJURED,
SUM(CAST(number_of_persons_killed AS INT64)) AS PERSONS_KILLED,
SUM(CAST(number_of_persons_injured AS INT64)) AS PERSONS_INJURED,
FROM
bigquery-public-data.new_york_mv_collisions.nypd_mv_collisions ds
GROUP BY
collision_date,
NEIGHBORHOOD
Snippet of the whole SELECT clause:
Created View:

Related

A question about how to select the value of a event_params.key in big query

Our project has some events recording how long the time that a user stay in a page. We add a event_params.key named time_ms, and its value shows the duration. How can I select the sum of 'time_ms'?
I tried to use SQL statements but failed.
SELECT *
FROM analytics_152426080.events_20190626
WHERE event_name = 'details_viewtime' AND
event_params.key = 'time_ms'
It shows the error message:
'Cannot access field key on a value with type ARRAY<STRUCT<key STRING, value STRUCT<string_value STRING, int_value INT64, float_value FLOAT64, ...>>> at [7:20]'.
I expect to get the sum of 'time_ms', but I should solve this question first.
I think you need unnest:
SELECT *
FROM analytics_152426080.events_20190626 e CROSS JOIN
UNNEST(event_params) ep
WHERE e.event_name = 'details_viewtime' AND
ep.key = 'time_ms';
I'm not sure where the actual value is located, but something like this:
SELECT SUM(ep.value.int_value)
FROM analytics_152426080.events_20190626 e CROSS JOIN
UNNEST(event_params) ep
WHERE ep.event_name = 'details_viewtime' AND
ep.key = 'time_ms';
Assuming the value you want to sum is an integer.
This assumes that the value column is a number of some sort. Otherwise, you need to convert it to one.
Or, if you want to sum the value per row:
SELECT e.*,
(SELECT SUM(ep.value.int_value)
FROM UNNEST(event_params) ep
WHERE ep.key = 'time_ms'
) as sum_ms
FROM analytics_152426080.events_20190626 e
WHERE e.event_name = 'details_viewtime'

Missing expression error when creating a view

I have the following oracle query to create a view :
CREATE VIEW uvw_Dashboard_Templates
AS
SELECT LL.ID
,LL.LoadDate
,LL.FileName
,LL.TemplateType
,LL.AnalystName
,LL.RecDate
,LL.CompanyID
,LL.CompanyName
,LL.Recommendation
,LL.Loaded
,LL.ErrorText
,CASE
WHEN LL.NewCompany = 1 AND LL.Loaded = 0 THEN 0
ELSE LL.NewCompany END NewCompany
,RH.rec_date LastRecDate
,RH.rec_code LastRecCode
,CONVERT(NUMBER(1), CASE
WHEN LL.Loaded = 1 AND NVL(LL.Recommendation, 'Rec') <>
NVL(RH.rec_code,'LastRec') THEN 1
ELSE 0 END) RecChanged
FROM tblTemplates_LoadLog LL
LEFT JOIN (
SELECT company_id, rec_date, rec_code
FROM (
SELECT company_id
, rec_date
, UPPER(rec_code) rec_code
, ROW_NUMBER() OVER(PARTITION BY company_id ORDER BY rec_date DESC) RowNumber
FROM tblRecHist
) OrderedList
WHERE RowNumber = 2) RH
ON LL.CompanyID = RH.company_id
which is throwing a
ORA-00936: missing expression error on running.
What is the possible cause of this?
change this:
,CONVERT('1', CASE
The Oracle/PLSQL CONVERT function converts a string from one character set to another.
CONVERT( string1, char_set_to [, char_set_from] )
you gotta give in string there
It looks like you're trying to use the SQL Server convert() function. Oracle does have its own convert() function, but it's not related at all:
CONVERT converts a character string from one character set to another.
The closest equivalent I can see to the SQL Server function would be to cast it:
,CAST(CASE
WHEN LL.Loaded = 1 AND NVL(LL.Recommendation, 'Rec') <>
NVL(RH.rec_code,'LastRec') THEN 1
ELSE 0 END AS NUMBER(1)) RecChanged
Describing the view would show the column as NUMBER(1), which I assume is the point of converting/casting it in the first place, since you know it conforms to the scale/precision constraint already.

Splitting out a field in Big Query

I have searched around and can not find much on this topic (maybe bad search terms :). I have a table, Protopayload.resource, that gets Apache logging information. As a result the field I am interested in contains multiple values that I need to search against. The field is formatted in a php URL style.
i.e.
/?id=13242134123&ver=12&os_bits=64&os_type=mac&lng=EN
This makes all searches end up with really long regexes to get data. Then join statements to combine data.
Example search to combine mac/win stats
SELECT
t1.date, t1.wincount, COALESCE(t2.maccount, 0) AS maccount
FROM (
SELECT
DATE(metadata.timestamp) AS date,
INTEGER(COUNT(protoPayload.resource)) AS wincount
FROM (TABLE_DATE_RANGE(tablename, DATE_ADD(CURRENT_TIMESTAMP(), -30, 'DAY'), CURRENT_TIMESTAMP() ))
WHERE
(REGEXP_MATCH(protoPayload.resource, r'ver=[11,12'))
AND protoPayload.resource CONTAINS 'os=win' GROUP BY date ) t1
LEFT JOIN (
SELECT
DATE(metadata.timestamp) AS date,
INTEGER(COUNT(protoPayload.resource)) AS maccount
FROM (TABLE_DATE_RANGE(tablename, DATE_ADD(CURRENT_TIMESTAMP(), -30, 'DAY'), CURRENT_TIMESTAMP() ))
WHERE
(REGEXP_MATCH(protoPayload.resource, r'cv=[p,m][17,16,15,14]'))
AND protoPayload.resource CONTAINS 'os=mac' GROUP BY date ) t2
ON
t1.date = t2.date
ORDER BY t1.date
What I was thinking was to use similar regex searches. Create a new table. Then save the data to a new table with relation fields. Then fix future logging so it logs to the table correctly.
My questions are this valid solution, or is there a much easier way to accomplish this in Google BigQuery? Is there a better way to transform the data?
Thanks again for any input!
You can use a SQL function to parse the key-value pairs into an array, which will generally be faster than using JavaScript. For example,
#standardSQL
CREATE TEMPORARY FUNCTION ParseKeys(queryString STRING)
RETURNS ARRAY<STRUCT<key STRING, value STRING>> AS (
(SELECT
ARRAY_AGG(STRUCT(
entry[OFFSET(0)] AS key,
entry[OFFSET(1)] AS value))
FROM (
SELECT SPLIT(pairString, '=') AS entry
FROM UNNEST(SPLIT(REGEXP_EXTRACT(queryString, r'/\?(.*)'), '&')) AS pairString)
)
);
SELECT ParseKeys('/?foo=bar&baz=2');
Now you can build on this with a function that pivots the keys into struct fields:
#standardSQL
CREATE TEMP FUNCTION GetAttributes(queryString STRING) AS (
(SELECT AS STRUCT
MAX(IF(key = 'id', CAST(value AS INT64), NULL)) AS id,
MAX(IF(key = 'ver', CAST(value AS INT64), NULL)) AS ver,
MAX(IF(key = 'os_bits', CAST(value AS INT64), NULL)) AS os_bits,
MAX(IF(key = 'os_type', value, NULL)) AS os_type,
MAX(IF(key = 'lng', value, NULL)) AS lng
FROM UNNEST(ParseKeys(queryString)))
);
Putting everything together, you can try out the GetAttributes function with some sample input:
#standardSQL
CREATE TEMPORARY FUNCTION ParseKeys(queryString STRING)
RETURNS ARRAY<STRUCT<key STRING, value STRING>> AS (
(SELECT
ARRAY_AGG(STRUCT(
entry[OFFSET(0)] AS key,
entry[OFFSET(1)] AS value))
FROM (
SELECT SPLIT(pairString, '=') AS entry
FROM UNNEST(SPLIT(REGEXP_EXTRACT(queryString, r'/\?(.*)'), '&')) AS pairString)
)
);
CREATE TEMP FUNCTION GetAttributes(queryString STRING) AS (
(SELECT AS STRUCT
MAX(IF(key = 'id', CAST(value AS INT64), NULL)) AS id,
MAX(IF(key = 'ver', CAST(value AS INT64), NULL)) AS ver,
MAX(IF(key = 'os_bits', CAST(value AS INT64), NULL)) AS os_bits,
MAX(IF(key = 'os_type', value, NULL)) AS os_type,
MAX(IF(key = 'lng', value, NULL)) AS lng
FROM UNNEST(ParseKeys(queryString)))
);
SELECT url, GetAttributes(url).*
FROM UNNEST(['/?id=13242134123&ver=12&os_bits=64&os_type=mac&lng=EN',
'/?id=2343645745&ver=15&os_bits=32&os_type=linux&lng=FR']) AS url;
You can always use Javascript UDFs for maximum flexibility. They will be slower than a pure SQL solution, but you'll be able to code around its limitations.
For example:
#standardSQL
CREATE TEMPORARY FUNCTION parse(query STRING)
RETURNS STRUCT<id STRING, ver STRING, os_bits STRING, os_type STRING, lng STRING>
LANGUAGE js AS """
function parseQueryString(query) {
// http://codereview.stackexchange.com/a/10396
var map = {};
query.replace(/([^&=]+)=?([^&]*)(?:&+|$)/g, function(match, key, value) {
(map[key] = map[key] || []).push(value);
});
return map;
}
return parseQueryString(query)
""";
WITH urls AS
(SELECT 'id=13242134123&ver=12&os_bits=64&os_type=mac&lng=EN' query
UNION ALL
SELECT 'id=13242134124&ver=12&os_bits=64&os_type=mac&lng=EN1&lng=EN2' query
)
SELECT query, parse(query) as parsed
FROM urls;.
I see few issues in the query in your question
1. looks like regexp is not correct and will not capture what you expect
2. query is heavily over-engineered and can be quite simplified
Below is to address above points
SELECT
DATE(metadata.timestamp) AS date,
SUM(REGEXP_MATCH(protoPayload.resource, r'ver=(11|12)\b')
AND protoPayload.resource CONTAINS 'os_type=win'
) AS wincount,
SUM(REGEXP_MATCH(protoPayload.resource, r'cv=(p|m)(17|16|15|14)\b')
AND protoPayload.resource CONTAINS 'os_type=mac'
) AS maccount
FROM (TABLE_DATE_RANGE(tablename, DATE_ADD(CURRENT_TIMESTAMP(), -30, 'DAY'),
CURRENT_TIMESTAMP() ))
GROUP BY date
Please note: you query in question is written with BigQuery Legacy SQL, so I keep my answer in same dialect

Summary Stats and Corresponding Dates - SQL Server

I was hoping someone perhaps could help. This problem was presented to me recently and I thought it would be easy, but (personally) found it a bit of a struggle. I can do it in Excel and SSRS - but I was curious if I was able to do it in SQL Server...
I would like to create a set of summary statistics (Max, Min) for a dataset. Easy enough... But I wanted to associate the corresponding date with those values.
Here is what my data looks like:
I have yearly data (not exactly - but beside the point) and I produce a pivoted summary like this using a series of CASE WHEN statements. This is fine - the output is seen on the right (above).
Each time I output this data - I like to provide a summary of the all the historic data (I only show the most recent data for sake of brevity). So... The question is how do I take an output like the one shown below (on different dates) and provide a summary data set like the one I have on the right?
So - a little background. I have already managed to join the Min and Max values using a UNION and that bit is fine. The tricky bit (I think) is how to form an INNER JOIN, using a sub query, with the Max or Min result values to return the corresponding Max or Min date, for each Type? Now it is highly likely that I am being a bit of an idiot and missing something obvious....but... Would really appreciate any help from anyone...
Many thanks in advance
This query will do the job, and for all TYPE
SELECT
Description, [CAR], [CAT], [MAT], [EAT], [PAR], [MAR], [FAR], [MOT], [LOT], [COT], [ROT]
FROM
(SELECT
unpvt.TYPE
,unpvt.Description
,unpvt.value
FROM (
SELECT
t.TYPE
,CONVERT(sql_variant,MAX(maxResult.MAX_RESULT)) as MAX_RESULT
,CONVERT(sql_variant,MIN(minResult.MIN_RESULT)) as MIN_RESULT
,CONVERT(sql_variant,MAX(CASE WHEN maxResult.MAX_RESULT IS NOT NULL THEN t.DATE ELSE NULL END)) as MAX_DATE
,CONVERT(sql_variant,MIN(CASE WHEN minResult.MIN_RESULT IS NOT NULL THEN t.DATE ELSE NULL END)) as MIN_DATE
FROM
table_name t -- You need to set your table name
LEFT JOIN (SELECT
TYPE
,MIN(RESULT) as MIN_RESULT
FROM
table_name -- You need to set your table name
GROUP BY
TYPE) minResult
on minResult.TYPE = t.TYPE
and minResult.MIN_RESULT = t.RESULT
LEFT JOIN (SELECT
TYPE
,MAX(RESULT) as MAX_RESULT
FROM
table_name -- You need to set your table name
GROUP BY
TYPE) maxResult
on maxResult.TYPE = t.TYPE
and maxResult.MAX_RESULT = t.RESULT
GROUP BY
t.TYPE) U
unpivot (
value
for Description in (MAX_RESULT, MIN_RESULT, MAX_DATE, MIN_DATE)
) unpvt) P
PIVOT
(
MAX(value)
FOR TYPE IN ([CAR], [CAT], [MAT], [EAT], [PAR], [MAR], [FAR], [MOT], [LOT], [COT], [ROT])
)AS PVT
DEMO : SQLFIDDLE
CONVERT(sql_variant, is a cast for columns to a common data type. This is a requirement of the UNPIVOT operator when you are running with subquery FROM.
It is possible to use the PIVOT command if your SQLServer is 2005 or better, but the raw data for the pivot need to be in a specific format, and the query I came up with is ugly
WITH minmax AS (
SELECT TYPE, RESULT, [date]
, row_number() OVER (partition BY TYPE ORDER BY TYPE, RESULT) a
, row_number() OVER (partition BY TYPE ORDER BY TYPE, RESULT DESC) d
FROM t)
SELECT info
, cam = CASE charindex('date', info)
WHEN 0 THEN cast(cast(cam AS int) AS varchar(50))
ELSE cast(cam AS varchar(50))
END
, car = CASE charindex('date', info)
WHEN 0 THEN cast(cast(car AS int) AS varchar(50))
ELSE cast(cam AS varchar(50))
END
, cat = CASE charindex('date', info)
WHEN 0 THEN cast(cast(cat AS int) AS varchar(50))
ELSE cast(cam AS varchar(50))
END
FROM (SELECT TYPE, 'maxres' info, RESULT value FROM minmax WHERE 1 = d
UNION ALL
SELECT TYPE, 'minres' info, RESULT value FROM minmax WHERE 1 = a
UNION ALL
SELECT TYPE, 'maxdate' info , [date] value FROM minmax WHERE 1 = d
UNION ALL
SELECT TYPE, 'mindate' info , [date] value FROM minmax WHERE 1 = a) DATA
PIVOT
(max(value) FOR TYPE IN ([CAM], [CAR], [CAT])) pvt
It's only a proof of concept so in SQLFiddle I have used a reducet set of fake data (3 row per 3 Type)
After the data preparation
SELECT TYPE, 'maxres' info, RESULT value FROM minmax WHERE 1 = d
UNION ALL
SELECT TYPE, 'minres' info, RESULT value FROM minmax WHERE 1 = a
UNION ALL
SELECT TYPE, 'maxdate' info , [date] value FROM minmax WHERE 1 = d
UNION ALL
SELECT TYPE, 'mindate' info , [date] value FROM minmax WHERE 1 = a
the value column is implicitly casted to the more complex datatype, in this case DateTime (you cannot have different data type in the same column), to see the data in the intended way an explicit cast is in needed, and is done with the CASE and CAST in
, cam = CASE charindex('date', info)
WHEN 0 THEN cast(cast(cam AS int) AS varchar(50))
ELSE cast(cam AS varchar(50))
END
the CASE check the data type, looking for the substring 'date' in the info column, then cast the row value back to INT for the minres and maxres column and in any case cast the value to varchar(50) to have the same data type again
UPDATE
With the sql_variant the CASE CAST block is not needed, thanks Ryx5
WITH minmax AS (
SELECT TYPE, RESULT, [date]
, row_number() OVER (partition BY TYPE ORDER BY TYPE, RESULT) a
, row_number() OVER (partition BY TYPE ORDER BY TYPE, RESULT DESC) d
FROM table_name)
SELECT info
, [CAM], [CAR], [CAT]
FROM (SELECT TYPE, 'maxres' info, cast(RESULT as sql_variant) value
FROM minmax WHERE 1 = d
UNION ALL
SELECT TYPE, 'minres' info, cast(RESULT as sql_variant) value
FROM minmax WHERE 1 = a
UNION ALL
SELECT TYPE, 'maxdate' info , cast([date] as sql_variant) value
FROM minmax WHERE 1 = d
UNION ALL
SELECT TYPE, 'mindate' info , cast([date] as sql_variant) value
FROM minmax WHERE 1 = a) DATA
PIVOT
(max(value) FOR TYPE IN ([CAM], [CAR], [CAT])) pvt

First and last value aggregate functions in postgresql that work correctly with NULL values

I know there are aggregate functions for getting last and first value of rows in postgresql
My problem is, that they do not work as i need. And i could use the help of one postgresql wizard. I'm using postgresql 9.2 - in case the version makes offering solution easyer.
Query
select v.id, v.active, v.reg_no, p.install_date, p.remove_date
from vehicle v
left join period p on (v.id = p.car_id)
where v.id = 1
order by v.id, p.install_date asc
Returns 6 rows:
id, active, reg_no, install_date, remove_date
1, TRUE, something, 2008-08-02 11:13:39, 2009-02-09 10:32:32
....
1, TRUE, something, 2010-08-15 21:16:40, 2012-08-25 07:44:30
1, TRUE, something, 2012-09-10 17:05:12, NULL
But when i use aggregating query:
select max(id) as id, last(active) as active, first(install_date) as install_date, last(remove_date) as remove_date
from (
select v.id, v.active, v.reg_no, p.install_date, p.remove_date
from vehicle v
left join period p on (v.id = p.car_id)
where v.id = 1
order by v.id, p.install_date asc
) as bar
group by id
Then i get
id, active, install_date, remove_date
1, TRUE, 2008-08-02 11:13:39, 2012-08-25 07:44:30
not
id, active, install_date, remove_date
1, TRUE, 2008-08-02 11:13:39, NULL
as i expected
Is it possible to change the aggregate functions somehow to yield NULL if the value of last row is null, not last existing value?
EDIT1
Roman Pekar offered alternative solution to my problem, but that does not fit my needs. The reason is - i simplified the original query. But the query i run is more complex. I realise that there might be alternative solutions to my problem - this why is update the post to include the original, more complex, query. Which is:
select partner_id, sum(active) as active, sum(installed) as installed, sum(removed) as removed
from (
select
pc.partner_id as partner_id,
v.id,
CASE WHEN v.active = TRUE THEN 1 ELSE 0 END as active,
CASE WHEN first(p.install_date) BETWEEN '2013-12-01' AND '2014-01-01' THEN 1 ELSE 0 END as installed,
CASE WHEN last(p.remove_date) BETWEEN '2013-12-01' AND '2014-01-01' THEN 1 ELSE 0 END as removed
from vehicle v
left join period p on (v.id = p.car_id)
left join partner_clients pc on (pc.account_id = v.client_id)
group by pc.partner_id, v.id, v.active
) as foo group by partner_id
As you can see, i actually need to get first and last value of several vehicles not one and in the end aggregate the counts of those vehicles by the owners of those vehicles.
/EDIT1
You could use window functions lead() and lag() to check first and last record, for example:
select
max(a.id) as id,
max(a.first) as first,
max(a.last) as last
from (
select
v.id,
case when lag(v.id) over(order by v.id, p.install_date) is null then p.install_date end as first,
case when lead(v.id) over(order by v.id, p.install_date) is null then p.remove_date end as last
from vehicle v
left join period p on (v.id = p.car_id)
where v.id = 1
) as a
sql fiddle demo
Thanks to Damien i went reading postgresql documentation about creating functions (source) and fiddled with the function changing it from:
CREATE OR REPLACE FUNCTION public.last_agg ( anyelement, anyelement )
RETURNS anyelement LANGUAGE sql IMMUTABLE STRICT AS $$
SELECT $2;
$$;
CREATE AGGREGATE public.last (
sfunc = public.last_agg,
basetype = anyelement,
stype = anyelement
);
to:
CREATE OR REPLACE FUNCTION public.last_agg ( anyelement, anyelement )
RETURNS anyelement LANGUAGE sql IMMUTABLE CALLED ON NULL INPUT AS $$
SELECT $2;
$$;
CREATE AGGREGATE public.last (
sfunc = public.last_agg,
basetype = anyelement,
stype = anyelement
);
and it seems to have fixed my troubles.
Thanks for reading.