我创建了三个测试表:用户,团队和成员,它们涉及用户和yeams。 users表包含user_id列,该列是主键。 membership表包含user_id外键和另一个名为cost的列,带有十进制值。
然后我根据我读过的一些访谈问题提出了以下SQL挑战:
“编写所需的SQL代码,以便为用户提供最多N位小数的成本”
SQL代码必须使用SQL函数(我使用PostgreSQL)。
我写的实际代码是:
CREATE OR REPLACE FUNCTION GET_NTH_DEC_RESIDUE(NUMERIC, INTEGER) RETURNS NUMERIC AS
$function$
SELECT CAST(CAST($1 AS NUMERIC) * POW(10,$2) - FLOOR(CAST($1 AS NUMERIC)*POW(10,$2)) AS NUMERIC);
$function$
LANGUAGE SQL;
SELECT user_id, cost, nth_pos_dec
FROM (SELECT user_id, GET_NTH_DEC_RESIDUE(CAST(memberships.cost AS NUMERIC), 2) AS nth_pos_dec
FROM users
JOIN memberships
USING (user_id)) AS T
WHERE NOT (nth_pos >= 0.0 AND nth_pos < 1.0);
函数GET_NTH_DEC_RESIDUE
得到剩余的十进制数(例如,对于0.345和2个小数位,函数返回0.5,对于0.12345和3个小数位,它返回0.45)。我们要寻找的成本值是那些不在[0,1)范围内的成本值。
通过将函数“应用”到联接用户+成员资格视图,它会生成一个带有剩余十进制数的新列,并且可以选择右行。
这个解决方案似乎做得很好,但我并不完全满意。
我尝试将逻辑比较包装到另一个SQL函数中,以便简化主查询,但我没有设法实现它。
有人能够设计出更优雅的方式吗? (请注意,我对使用SQL函数感兴趣,而且我不想进行字符串转换。)
谢谢!
答案 0 :(得分:0)
你的功能很好。以下是我认为可以优化的一些内容:
您不需要将第一个参数转换为NAMELIST
- 它已经是此类型,因此首先优化可能是:
NUMERIC
当你检查函数的返回值时,不需要检查它是否小于1 - 它永远不会等于或大于1,所以你可以检查它是否等于0(我已经检查过该函数也能正常使用负值):
CREATE OR REPLACE FUNCTION GET_NTH_DEC_RESIDUE(NUMERIC, INTEGER) RETURNS NUMERIC AS
$function$
SELECT CAST($1 * POW(10, $2) - FLOOR($1 * POW(10, $2)) AS NUMERIC);
$function$
LANGUAGE SQL;
如果您不需要此函数返回的数值,您可以将其更改为返回...
WHERE nth_pos = 0
并仅在boolean
子句中使用它(注意,您不会'根本不需要演员表:
WHERE
您只能计算CREATE OR REPLACE FUNCTION GET_NTH_DEC_RESIDUE(NUMERIC, INTEGER) RETURNS BOOLEAN AS
$function$
SELECT $1 * POW(10, $2) - FLOOR($1 * POW(10, $2)) = 0;
$function$
LANGUAGE SQL;
SELECT
user_id,
cost
FROM
users
JOIN memberships
USING (user_id)
WHERE
GET_NTH_DEC_RESIDUE(CAST(memberships.cost AS NUMERIC), 2);
一次(我不确定查询计划程序是否也不会这样做):
POW(10, $2)