2016-11-08 40 views
0

我已經創建了三個測試表格:用戶,團隊和成員資格,它們涉及用戶和團隊。 users表包含一個user_id列,它是主鍵。 成員資格表包含一個user_id外鍵和另一個稱爲cost的列,其中包含一個十進制值。改進一個(工作)函數以獲得十進制數字具有特定小數位數的行

然後我提出自己下面的SQL挑戰的基礎上,我看了一些面試問題:

「寫爲了讓用戶使用具有最多N次小數的成本所需要的SQL代碼」

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.45 0.12345和3小數位)。我們正在尋找的成本值是那些不在[0,1]範圍內的成本值。

通過將函數「應用」到連接的用戶+成員資格視圖,它會生成一個具有殘餘小數的新列,並且可以選擇正確的行。

這個解決方案似乎做得很好,但我並不完全滿意。

我試圖將邏輯比較封裝到另一個SQL函數中,以便主查詢得到簡化,但我沒有設法實現。

有沒有人能夠設計一個更優雅的方式來做到這一點? (請注意,我對使用SQL函數感興趣,並且我不想執行字符串轉換)。

謝謝!

+0

如果你想牛逼o提高整個查詢的性能,您需要創建一個索引列來存儲函數的結果;以避免每次運行查詢時都要對整個表進行數學運算。針對'where'子句中的列計算,無論多小,都會大大增加您的cpu使用量。 –

回答

0

你的功能是一個不錯的。這裏有一些事情,我認爲可以優化:

  1. 你不需要的第一個參數轉換爲NUMERIC - 這已經是這個類型的,所以首先優化可能是:

    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; 
    
  2. 當你檢查函數的返回值時,不需要檢查它是否小於1 - 它不可能等於或大於1,所以你可以檢查它是否等於0(我已經檢查過該功能也能正常使用負值):

    ... 
    WHERE nth_pos = 0 
    
  3. ,如果你不需要這個函數返回的數值,你可以改變它返回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); 
    
  4. 可以計算出POW(10, $2)只有一次(我不知道,如果查詢規劃不會做反正):

    CREATE OR REPLACE FUNCTION GET_NTH_DEC_RESIDUE(NUMERIC, INTEGER) RETURNS BOOLEAN AS 
        $function$ 
        WITH precalc AS (
         SELECT POW(10, $2) AS power 
        ) 
        SELECT 
         $1 * power - FLOOR($1 * power) = 0 
        FROM 
         precalc; 
        $function$ 
    LANGUAGE SQL; 
    
+0

非常感謝你@亞當。我擔心的不是速度,而是風格(希望探索SQL限制)。你的回答非常有幫助。 –