在一星期中存储星期几的最佳方法

时间:2018-11-04 19:47:03

标签: sql postgresql dayofweek

我有一个通知表,有人可以在其中设置要在一周中的哪几天进行通知:

sun mon tue wed thu fri sat

现在我可以将它们创建为bool类型的单个列。

是否可以将它们存储在单个列中?

我将需要查询以下信息:

select * from notifications where start_date >= now and is_monday = true

创建一个独立的列会更明智还是某种某种整数/位掩码?不知道如何在db列中完成此操作,以及这是否一个好主意。

3 个答案:

答案 0 :(得分:2)

tl; dr

  

从通知中选择*,其中start_date> = now,并且is_monday = true

SELECT * 
FROM event_ 
WHERE 
    when_ >= DATE '2018-01-22' 
    AND
    EXTRACT( isodow FROM when_ )  -- Access portion of date-value in the `isodow` field meaning the day-of-week as defined by the ISO 8601 standard.
    = 2                           -- 2 = Tuesday in ISO 8601 definition of a week.
;

如果要检查多个星期几的值,请使用一个数组。

SELECT * 
FROM event_ 
WHERE 
    when_ >= DATE '2018-01-22' 
    AND
    EXTRACT( isodow FROM when_ )  -- Access portion of date-value in the `isodow` field meaning the day-of-week as defined by the ISO 8601 standard.
    = 
    ANY( ARRAY[ 2 , 3 , 4 ] )     -- Define numbers for day-of-week per ISO 8601 where Monday-Sunday is 1-7. 
;

详细信息

  

存储星期几的最佳方式

不要。

Answer by Barbaros Özhan中的建议,除日期外,还存储星期几是多余的,因此违反了规范化。

Postgres提供EXTRACT函数以返回代表日期时间值某些方面的值(“字段”)。 Ingres样式的等效项date_part也可用。

Postgres offers two fields确定星期几:

  • isodow
    Sunday-Monday as defined标准返回一周中ISO 8601的1-7。

  • dow
    返回周日(周日)至周六的一天中的0-6(在美国等某些地方定义为一周)。

我建议您坚持第一个标准定义。

让它们使用。

enter image description here

首先,获取日期。

SELECT DATE '2018-01-23' ;
  

2018-01-23

获取该日期的星期几。

SELECT EXTRACT ( isodow FROM DATE '2018-01-23' ) ;

返回一个双精度值2,即星期二。

  

2

array中定义您的目标星期几值。例如,ISO 8601星期几编号中的Tuesday-Wednesday-Thursday,2-4。

ARRAY[ 2 , 3 , 4 ]

询问数组是否包含特定数字。

SELECT ARRAY[ 2 , 3 , 4 ] @> ARRAY[ 2 ] ;
  

true

或者,另一种获得相同结果的方法。

SELECT 2 = ANY ( ARRAY[ 2 , 3 , 4 ] ) ;
  

true

在该声明中应用特定日期。

SELECT EXTRACT ( isodow FROM DATE '2018-01-23' )  = ANY ( ARRAY[ 2 , 3 , 4 ] ) ;
  

true

我们可以将所有内容放在一起。我们将创建一个表,插入3行,并进行查询。我们希望在ISO 8601每周第2-4天找到第二行和第三行,而1月22日的第一行是星期一(第1天),因此应省略。

DROP TABLE IF EXISTS event_
;

CREATE TABLE IF NOT EXISTS event_ (
    pkey_       INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY ,
    when_       DATE NOT NULL
);

INSERT INTO event_ ( when_ ) 
VALUES ( DATE '2018-01-22' ) , ( DATE '2018-01-23' ) , ( DATE '2018-01-24' )
;

SELECT * FROM event_ 
WHERE EXTRACT( isodow FROM when_ ) = ANY( ARRAY[ 2 , 3 , 4 ] )
;

运行时,很确定,第一行被省略。

  

2“ 2018-01-23”

     

3“ 2018-01-24”

此演示版使用pgAdmin 3.5和x86_64-apple-darwin上的PostgreSQL 11.0,由Apple LLVM 6.0版(clang-600.0.54)(基于LLVM 3.5svn)(64位)编译。

存储星期几的值列表

您的问题不太清楚。

也许您还问过如何存储一周的七个天中的哪个值是每个用户的偏好。

首先为一周中的七个日值定义一个枚举。这样可以提供类型安全性并确保有效值。

CREATE TYPE ISO_DOW_ AS ENUM ( 
    'MONDAY' , 'TUESDAY' , 'WEDNESDAY' , 'THURSDAY' , 'FRIDAY' , 'SATURDAY' , 'SUNDAY' 
) ;

定义一个表来存储用户首选项。我们添加一列类型为array的整数数组。

CREATE TABLE user_pref_ (
    pkey_       INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY ,
    dows_       ISO_DOW_[] NOT NULL -- User can select 0, 1, or up to 7 day-of-week values to be used for searching notifications.
) ;

请务必使用Postgres中的精美COMMENT功能来记录我们的意图。我将其留给读者练习。

插入一些行。

INSERT INTO user_pref_ ( dows_ )
VALUES ( ARRAY[]::ISO_DOW_[] ) , ( ARRAY[ 'TUESDAY' , 'WEDNESDAY' , 'THURSDAY' ]::ISO_DOW_[] ) , ( ARRAY[ 'FRIDAY' ]::ISO_DOW_[] )
;

最后一步,我还不能完全完成。我尚未确定在子查询中检索要传递给外部查询的ANY函数的数组的方法。应该是这样的:

SELECT * FROM event_
WHERE EXTRACT( isodow FROM when_ ) = ANY(
    SELECT dows_ FROM user_pref_ WHERE pkey_ = 2 ;
) ;

答案 1 :(得分:1)

您可以创建枚举类型:

create type day_of_week as enum (
    'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'
);

并将其用作列类型,例如:

create table notifications (
    id serial primary key,
    event text,
    start_date date,
    day_of_week day_of_week
);

您的查询可能如下所示:

select * 
from notifications 
where start_date >= current_date
and day_of_week = 'mon'

请注意,该类型的值是有序的,因此您可以例如:

select * 
from notifications 
where day_of_week between 'mon' and 'thu'

使用函数to_char()从日期中获取day_of_week

select to_char(current_date, 'dy')::day_of_week as dow

 dow 
-----
 sun
(1 row) 

更新。如果每个事件可能有更多通知,请使用day_of_week数组,例如:

create table notifications (
    id serial primary key,
    event text,
    start_date date,
    days_of_week day_of_week[]
);

insert into notifications values
(default, 'some event', '2018-10-01', '{mon, wed}');

select * 
from notifications 
where current_date >= start_date
and 'mon' = any(days_of_week);

答案 2 :(得分:1)

我认为您不需要存储另一列,而是使用取决于start_date列的排序规则,并将您的day参数设置为sunmontue。 ..等

with notifications(start_date) as
(
  select date'2018-11-04'      
)
select *
  from notifications
 where start_date <= now() 
   and substring(to_char(start_date, 'day'),1,3) = 'sun' -- 'mon', 'tue' ...

Rextester Demo