[英]PostgreSQL: How do I join two tables based on same start and end time (timestamp without time zone)?
[英]How do I write a function in plpgsql that compares a date with a timestamp without time zone?
我想編寫一個 function ,它返回一個表,其中包含firstDate
和lastDate
之間的所有行。 這些行具有數據類型timestamp 沒有時區它們還必須具有特定的節點 ID。
這是我的 function:
CREATE OR REPLACE FUNCTION get_measurements_by_node_and_date(nodeID INTEGER, firstDate date, lastDate date)
RETURNS TABLE (measurement_id INTEGER, node_id INTEGER, carbon_dioxide DOUBLE PRECISION,
hydrocarbons DOUBLE PRECISION, temperature DOUBLE PRECISION,
humidity DOUBLE PRECISION,
air_pressure DOUBLE PRECISION,
measurement_timestamp timestamp without time zone ) AS
$$
DECLARE
sql_to_execute TEXT;
BEGIN
SELECT 'SELECT measurements_lora.id,
measurements_lora.node_id,
measurements_lora.carbon_dioxide,
measurements_lora.hydrocarbons,
measurements_lora.temperature,
measurements_lora.humidity,
measurements_lora.air_pressure,
measurements_lora.measurement_timestamp AS measure
FROM public.measurements_lora
WHERE measurements_lora.measurement_timestamp <= '||lastDate||'
AND measurements_lora.measurement_timestamp >= '||firstDate||'
AND measurements_lora.node_id = '||nodeID||' '
INTO sql_to_execute;
RETURN QUERY EXECUTE sql_to_execute;
END
$$ LANGUAGE plpgsql;
列 measure_timestamp 的類型為 timestamp without time zone,格式為 yy-mm-dd hh-mm-ss
當我運行SELECT * FROM get_measurements_by_node_and_date(1, '2020-5-1', '2020-5-24')
我收到以下錯誤:
ERROR: operator does not exist: timestamp without time zone <= integer LINE 10: ... WHERE measurements_lora.measurement_timestamp <= 2020-05...
我不明白為什么它說“整數”,因為我清楚地將firstDate
和lastDate
定義為類型date
。
在這種情況下,最好先編寫已執行的查詢。 我試圖減少你的例子:
CREATE OR REPLACE FUNCTION public.foo(d date)
RETURNS TABLE(o integer)
LANGUAGE plpgsql
AS $function$
declare q text;
begin
q := 'select 1 from generate_series(1,100) where current_timestamp <= ' || d ;
raise notice '%', q;
return query execute q;
end;
$function$
postgres=# select * from foo('2020-05-25');
NOTICE: 00000: select 1 from generate_series(1,100) where current_timestamp <= 2020-05-25
LOCATION: exec_stmt_raise, pl_exec.c:3826
ERROR: 42883: operator does not exist: timestamp with time zone <= integer
LINE 1: ...om generate_series(1,100) where current_timestamp <= 2020-05...
^
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
QUERY: select 1 from generate_series(1,100) where current_timestamp <= 2020-05-25
CONTEXT: PL/pgSQL function foo(date) line 6 at RETURN QUERY
我得到了同樣的錯誤信息。 所以有不止一個錯誤:
動態查詢無效 - where 子句看起來像
where current_timestamp <= 2020-05-25
你可以看到,它是無效的——沒有引號。 當您手動使用引號時,您可以修復它(但這是一個嚴重錯誤並且不要這樣做,或者您可以使用 function quote_literal
like where current_timestamp <= ' || quote_literal(d)
。
現在產生的查詢是正確的:
select 1 from generate_series(1,100) where current_timestamp <= '2020-05-25'
但在這種情況下,使用EXECUTE USING
會好得多。 當變量用作查詢參數(而不是表名或列名)時,可以使用USING
子句。 然后你不需要使用引用:
CREATE OR REPLACE FUNCTION public.foo(d date) RETURNS TABLE(o integer) LANGUAGE plpgsql AS $function$ declare q text; begin q:= 'select 1 from generate_series(1,100) where current_timestamp <= $1'; return query execute q using d; end; $function$
但最大的錯誤是在不需要時使用動態 SQL (如您的示例)。 使用RETURN QUERY EXECUTE
語句沒有任何明顯的原因。 您可以只使用RETURN QUERY
:
CREATE OR REPLACE FUNCTION get_measurements_by_node_and_date(nodeID INTEGER, firstDate date, lastDate date) RETURNS TABLE (measurement_id INTEGER, node_id INTEGER, carbon_dioxide DOUBLE PRECISION, hydrocarbons DOUBLE PRECISION, temperature DOUBLE PRECISION, humidity DOUBLE PRECISION, air_pressure DOUBLE PRECISION, measurement_timestamp timestamp without time zone ) AS $$ BEGIN RETURN QUERY SELECT measurements_lora.id, measurements_lora.node_id, measurements_lora.carbon_dioxide, measurements_lora.hydrocarbons, measurements_lora.temperature, measurements_lora.humidity, measurements_lora.air_pressure, measurements_lora.measurement_timestamp AS measure FROM public.measurements_lora WHERE measurements_lora.measurement_timestamp <= lastDate AND measurements_lora.measurement_timestamp >= firstDate AND measurements_lora.node_id = nodeID; END $$ LANGUAGE plpgsql;
更重要的是,沒有任何跡象表明需要 PL/pgSQL 開始。 一個普通的(准備好的) SELECT
語句可以做到這一點。 或者 SQL function,如果你想將它持久化到數據庫中。 看:
關於:
包含
firstDate
和lastDate
之間的所有行
精確定義包含/排除的上限/下限以避免令人驚訝的極端情況結果。 將timestamp
列與date
進行比較時,后者被強制轉換為表示當天第一個實例的時間戳: YYYY.MM.DD 00:00:00
。
您的查詢說:
measurement_timestamp <= lastDate AND measurement_timestamp >= firstDate
...這將包括所有firstDate
,但排除所有lastDate
,除了 00:00 的第一個(常見)實例。 通常不是你想要的。 鑒於您的表述,我想這是您真正想要的:
CREATE OR REPLACE FUNCTION get_measurements_by_node_and_date(node_id integer
, firstDate date
, lastDate date)
RETURNS TABLE (measurement_id integer
, node_id integer
, carbon_dioxide float8
, hydrocarbons float8
, temperature float8
, humidity float8
, air_pressure float8
, measurement_timestamp timestamp)
LANGUAGE sql STABLE AS
$func$
SELECT m.id
, m.node_id
, m.carbon_dioxide
, m.hydrocarbons
, m.temperature
, m.humidity
, m.air_pressure
, m.measurement_timestamp -- AS measure -- just documentation
FROM public.measurements_lora m
WHERE m.node_id = _node_id
AND m.measurement_timestamp >= firstDate::timestamp
AND m.measurement_timestamp < (lastDate + 1)::timestamp -- ①!
$func$;
① 這包括所有lastDate
,並且有效。 您可以在date
中添加/減去integer
值以添加/減去天數。 顯式轉換為::timestamp
是可選的,因為日期將在表達式中自動強制轉換。 但是,由於我們試圖在這里消除混亂......
有關的:
除了1:
列
measurement_timestamp
的類型為timestamp without time zone
,格式為yy-mm-dd hh-mm-ss
不, timestamp
值未格式化,句號。 它們只是時間戳值(內部存儲為自紀元以來的微秒數)。 顯示與數值完全分離,可以在不改變數值的情況下進行一百零一種調整。 擺脫這種誤解,以更好地了解正在發生的事情。 看:
除了2:
關於 SQL BETWEEN
的狡猾性質:
除了3:
考慮 Postgres 中合法的小寫標識符。 first_date
而不是firstDate
。 看:
有關的:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.