簡體   English   中英

如何在 plpgsql 中編寫 function 將日期與沒有時區的時間戳進行比較?

[英]How do I write a function in plpgsql that compares a date with a timestamp without time zone?

我想編寫一個 function ,它返回一個表,其中包含firstDatelastDate之間的所有行。 這些行具有數據類型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...

我不明白為什么它說“整數”,因為我清楚地將firstDatelastDate定義為類型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

我得到了同樣的錯誤信息。 所以有不止一個錯誤:

  1. 動態查詢無效 - 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'
  2. 但在這種情況下,使用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$
  3. 但最大的錯誤是在不需要時使用動態 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,如果你想將它持久化到數據庫中。 看:

關於:

包含firstDatelastDate之間的所有行

精確定義包含/排除的上限/下限以避免令人驚訝的極端情況結果。 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM