改进(工作)函数以获得十进制数具有一定小数位数的行

时间:2016-11-08 19:17:29

标签: sql postgresql

我创建了三个测试表:用户,团队和成员,它们涉及用户和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函数感兴趣,而且我不想进行字符串转换。)

谢谢!

1 个答案:

答案 0 :(得分:0)

你的功能很好。以下是我认为可以优化的一些内容:

  1. 您不需要将第一个参数转换为NAMELIST - 它已经是此类型,因此首先优化可能是:

    NUMERIC
  2. 当你检查函数的返回值时,不需要检查它是否小于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;
    
  3. 如果您不需要此函数返回的数值,您可以将其更改为返回... WHERE nth_pos = 0 并仅在boolean子句中使用它(注意,您不会'根本不需要演员表:

    WHERE
  4. 您只能计算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)