我有一个通知表,有人可以在其中设置要在一周中的哪几天进行通知:
sun mon tue wed thu fri sat
现在我可以将它们创建为bool类型的单个列。
是否可以将它们存储在单个列中?
我将需要查询以下信息:
select * from notifications where start_date >= now and is_monday = true
创建一个独立的列会更明智还是某种某种整数/位掩码?不知道如何在db列中完成此操作,以及这是否一个好主意。
答案 0 :(得分:2)
从通知中选择*,其中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(在美国等某些地方定义为一周)。
我建议您坚持第一个标准定义。
让它们使用。
首先,获取日期。
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参数设置为sun
或mon
或tue
。 ..等
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' ...