PostgreSQL PL/pgSQL:存储在表中的查询(营业时间)

Posted

技术标签:

【中文标题】PostgreSQL PL/pgSQL:存储在表中的查询(营业时间)【英文标题】:PostgreSQL PL/pgSQL : query stored within a table (opening hours) 【发布时间】:2018-07-23 14:11:28 【问题描述】:

我有一个应用程序(postgresql 9.6 将在 10 上迁移),我想在其中检索表中的结果,并在同一时间匹配存储在此表中的营业时间。

让我们用一个虚构的例子来解释: 我有一张商店表:

store_name       |  opening_hours
-----------------+-----------------------------
storeA           | ((wday between 1 and 5) and (hour between 10 and 20))
storeB           | ((wday between 2 and 5) and (hour between 9 and 18)) OR (wday in (6,7) and (hour between 9 and 12))

我想查询此表并从查询时获取已打开的商店(不考虑时区)。 (谁在乎:在我的国家,一周的第一天是星期一,但在这个例子中我们不在乎):

如果我的查询询问在星期三 19 小时(晚上 7 点)开设的商店,它将仅返回 storeA。 如果查询在午夜启动,则不会选择任何查询 如果查询发生在星期四 11 点(上午 11 点),两个商店都会被选中....

你能帮我搞定这个小东西吗?我想我只是错过了正确的写法。

编辑:“营业时间”只是记录我想要解决这个问题的方式。无论如何,我会在这个数据库中添加一些新表。 此处搜索的唯一答案是评估存储在表中的表达式的方法。

【问题讨论】:

您的数据是这样存储的吗?带有数据“((wday 1 到 5 之间)和(hour 10 到 20 之间))的字符串,或者你想举个例子吗? 是的,这正是它在表格中的书写方式。但我愿意接受任何最简单的方式来做这样的事情.. Best way to store working hours and query it efficiently的可能重复 【参考方案1】:

由于您愿意接受建议,我建议您查看此问题的公认答案:Best way to store working hours and query it efficiently

您当前的表结构将很难管理。如果您更改了表的结构以匹配上面接受的答案,这正是您所需要的,并且查询起来非常简单。

编辑:为完整起见,链接中建议的表结构为:

要存储正常营业时间,您需要存储一些记录,其中包含:

商店 - INTEGER DayOfWeek - 整数 (0-6) 开放时间 - 时间 关闭时间 - 时间

【讨论】:

如果没有找到更聪明的方法来做这件事,我会留到以后,但我很确定 postgresql 有一个聪明的机制来以优雅的方式做这样的事情。 @urbain 很公平,您可能会发现一种我没有考虑过的方法,但我认为您不会得到比这更简单或更有用的答案。【参考方案2】:

我对下面的回答并不完全满意,但它以我想要的方式工作,而不是 mysql 低技术的方式。 我下面的工作是基于How to execute a string result of a stored procedure in postgres。

如果有帮助的话,那就是:

-- push message to debug, to 'RAISE' usefull things
SET client_min_messages TO DEBUG;
\set VERBOSITY terse

-- must return a SETOF to evaluate my test (see RETURN QUERY EXECUTE below)
-- so here is a dirty simple [temporary] table.
CREATE TEMP TABLE stupid_bool_table (opened BOOLEAN);
INSERT INTO stupid_bool_table VALUES (true),(false);

CREATE OR REPLACE FUNCTION grab_worker_test_opening_hour(shopNametext)
  RETURNS SETOF stupid_bool_table AS
$BODY$
  DECLARE
  -- $Id: batch_workers.psql,v 1.15 2018/07/25 08:08:49 calyopea Exp $
    openhour text;
  BEGIN

  --TODO: materialized view refreshed each hours or halfs OR clever query
  SELECT INTO openhour description
         FROM shop_flat_table
        WHERE shop_id IN (select id from workers where shop=shopName)
          AND flat_txt='openhour';

  IF ( NOT FOUND ) THEN

    RAISE DEBUG 'opening_hour for % is null',shopName;
    RETURN QUERY EXECUTE 'SELECT opened FROM stupid_bool_table WHERE opened=true';   -- by DEFAULT
    -- RAISE EXCEPTION 'cant be here';   -- could be !

  ELSE

    RAISE DEBUG 'opening_hour for % is % (before replace)',shopName,openhour;

    openhour:=REPLACE(openhour,'dow', extract(dow  from NOW())::text);
    openhour:=REPLACE(openhour,'hour',extract(hour from NOW())::text);

    RAISE DEBUG 'opening_hour for % is % (after replace)',shopName,openhour;

    RETURN QUERY EXECUTE 'SELECT opened FROM stupid_bool_table WHERE opened=' || openhour;

  END IF;

END;
$BODY$
LANGUAGE plpgsql IMMUTABLE
COST 100;

所以现在: 与数据:

shop  | opening_hours
------+------------------------------------------------------
ShopA | ((dow between 1 and 5) and (hour between 9 and 16)))
ShowB | ((dow between 1 and 5) and (hour between 9 and 17)))

SELECT * FROM grab_worker_test_opening_hour('ShopB');
psql:batch_workers.psql:124: DEBUG:  opening_hour for ShopB is ((dow between 1 and 5) and (hour between 9 and 17)) OR (dow in (6,7)) (before replace)
psql:batch_workers.psql:124: DEBUG:  opening_hour for ShopB is ((3 between 1 and 5) and (17 between 9 and 17)) OR (3 in (6,7)) (after replace)
 opened
--------
 t
(1 ligne)

(同时为shopA开了=f:2018-07-25 17:15:00(iso时间))。

【讨论】:

以上是关于PostgreSQL PL/pgSQL:存储在表中的查询(营业时间)的主要内容,如果未能解决你的问题,请参考以下文章

PL/pgSQL 从表中动态复制数据

PL/pgSQL INSERT INTO 表

在 PL/pgSQL 块中运行 SELECT

使用 PL/pgSQL 将查询结果存储在变量中

PostgreSQL:列名到数组 PL/pgSQL

PostgreSQL-PL/pgSQL控制结构