在一个关于文字游戏的PostgreSQL表格中,我会跟踪按照vip_until
列或未来有效日期的grand_until
列付费玩家:
create table users (
uid serial primary key,
vip_until timestamp null, -- date in future indicates paying customer
grand_until timestamp null -- date in future indicates paying customer
);
我写了一个简短的存储过程来检查:
create or replace function is_vip(
IN in_uid integer,
OUT out_vip boolean
) as $BODY$
BEGIN
out_vip := exists(select 1 from users
where uid = in_uid and
greatest(vip_until, grand_until) > current_timestamp);
END;
$BODY$ language plpgsql;
然后我试图在另一个存储过程中使用上面的函数:
create or replace function join_new_game(
IN in_uid integer,
IN in_letters varchar(130),
IN in_style integer,
OUT out_gid integer
) as $BODY$
BEGIN
/* maybe there is a new game already, just waiting for the player's 1st move*/
select gid into out_gid from games
where (player1 = in_uid and stamp1 is null)
or (player2 = in_uid and stamp2 is null) limit 1;
IF not found THEN
/* try to find games having just 1 player (with different uid) */
select gid into out_gid from games
where (player1 != in_uid and stamp1 is not null
and player2 is null) limit 1;
IF not found THEN
/* only allow board style 1 for non-paying customers */
IF not select is_vip(in_uid) THEN
in_style := 1; -- the above line fails
END IF;
/* create new game with player1 = uid and stamp1 = null */
insert into games (
created,
player1,
stamp1,
stamp2,
letters1,
letters2,
letters,
board,
style
) values (
current_timestamp,
in_uid,
null,
null,
substring(in_letters, 1, 7),
substring(in_letters, 8, 7),
substring(in_letters, 15),
rpad('', 225), -- fill 15x15 board
in_style
) returning gid into out_gid;
ELSE
update games set player2 = in_uid where gid = out_gid;
END IF;
END IF;
END;
$BODY$ language plpgsql;
但是我得到了这个语法错误:
ERROR: syntax error at or near "select" LINE 21: IF not select is_vip(in_uid) TH... ^
如何正确使用is_vip()
功能?
答案 0 :(得分:3)
name := "TimetableAPI"
version := "1.0"
lazy val `timetableapi` = (project in file(".")).enablePlugins(PlayScala)
scalaVersion := "2.11.7"
libraryDependencies ++= Seq(cache, ws, specs2 % Test, evolutions,
"mysql" % "mysql-connector-java" % "5.1.34",
"com.typesafe.play" %% "play-slick" % "1.1.0",
"com.typesafe.play" %% "play-slick-evolutions" % "1.1.0")
unmanagedResourceDirectories in Test <+= baseDirectory(_ / "target/web/public/test")
resolvers += "scalaz-bintray" at "https://dl.bintray.com/scalaz/releases"
routesGenerator := InjectedRoutesGenerator
是一个返回Boolean的函数。你可以直接打电话:
is_vip(in_uid)
或者,如果你想使用select:
IF not is_vip(in_uid) THEN
如果要将标量查询用作值表达式,则查询必须加括号。
答案 1 :(得分:2)
is_vip()
功能?您的功能可以更高效。使其成为STABLE
SQL函数,因此它可以是inlined。
CREATE OR REPLACE FUNCTION is_vip(in_uid integer)
RETURNS boolean AS
$func$
SELECT EXISTS (
SELECT 1 FROM users
WHERE uid = in_uid
AND (vip_until > current_timestamp OR
grand_until > current_timestamp)
)
$func$ LANGUAGE sql STABLE;
理想情况下,您有一个多列索引,允许仅进行索引扫描:
(uid, vip_until, grand_until)
uid
必须是第一列。
How to use the is_vip function properly?
@kin already provided a fix表示基本语法错误:使用自己的函数就像任何其他Postgres函数一样。但这里有更多:
我格式化了最重要的部分粗体:
CREATE OR REPLACE FUNCTION join_new_game(
IN in_uid integer,
IN in_letters varchar(130),
IN in_style integer,
OUT out_gid integer) AS
$func$
BEGIN
/* maybe there is a new game already, just waiting for the player's 1st move*/
SELECT gid INTO out_gid
FROM games
WHERE (player1 = in_uid AND stamp1 IS NULL)
OR (player2 = in_uid AND stamp2 IS NULL)
LIMIT 1;
IF NOT FOUND THEN
/* try to find games having just 1 player (with different uid) */
/* and UPDATE immediately using a smart locking strategy */
UPDATE games g
SET player2 = in_uid
FROM (
SELECT gid
FROM games
WHERE player1 <> in_uid
AND stamp1 IS NOT NULL
AND player2 IS NULL
LIMIT 1
FOR UPDATE SKIP LOCKED -- see link below !!
) g1
WHERE g.gid = g1.gid
RETURNING g.gid
INTO out_gid;
IF NOT FOUND THEN
/* create new game with player1 = uid and stamp1 = null */
INSERT INTO games (created, player1, stamp1, stamp2, letters1, letters2, letters, board, style)
VALUES (current_timestamp, in_uid, null, null
, left(in_letters, 7)
, substring(in_letters, 8, 7)
, right(in_letters, -15) -- guessing you want to start at pos 16!
, rpad('', 225) -- fill 15x15 board
/* only allow board style 1 for non-paying customers */
, CASE WHEN NOT is_vip(in_uid) THEN 1 END -- defaults to NULL
)
RETURNING gid
INTO out_gid;
END IF;
END IF;
END
$func$ LANGUAGE plpgsql;
幸运的是,您使用的是最新版本的Postgres(9.5),它引入了用于排队FOR UPDATE SKIP LOCKED
的新智能锁定策略。详细解释:
我还使用is_vip()
表达式将INSERT
调用CASE
调用到jQuery(function($) {
$('input[name="type"]').change(function(event) {
$('.2_details').toggle(this.value == 2)
});
$('form').validate({
rules: {
title: {
required: true,
minlength: 10
},
3: {
required: true,
number: true
},
4: {
required: true,
number: true,
min: function() {
return +$('#3').val() || 0;
}
}
}
});
});
查询中。这样效率更高。
其他一些小的优化。
答案 2 :(得分:1)
尝试使用schema.function_name进行更改,如下所示:
IF (<schema>.is_vip(in_uid) = 'false')
THEN
...
END IF;