Postgresql

從 count() 確定百分比而沒有強制轉換問題

  • July 4, 2020

我正在嘗試執行以下查詢以提供patients表中具有該refinst列值的行的百分比。我一直得到0的結果。

select (count (refinst) / (select count(*) from patients) * 100) as "Formula" 
from patients;

該表有 15556 行,並select count(refinst) from patients告訴我其中 1446 行在列中有值refinst。我想從查詢中得到的響應是 30.62(1446/15556*100=30.62XXXXX四捨五入到小數點後兩位)。

我很確定它與計數結果的數據類型有關(我假設是整數)。如果我將一個整數除以一個整數並且結果小於 0,它會被截斷為 0 對嗎?如果是這種情況,有人可以告訴我如何將計數結果轉換為小數點後 2 位的數字,以便結果也將四捨五入到小數點後 2 位?

我確信有比多個計數語句更好的方法來編寫此程式碼。我正在尋找一種處理器效率更高的方法來編寫這個查詢。

SELECT (count(refinst) * 100)::numeric / NULLIF(count(*), 0) AS refinst_pct
   -- count(refinst) * 100.0 / NULLIF(count(*), 0) AS refinst_pct  -- simpler
FROM   patients;
  • 不要使用子選擇。兩個聚合都可以從同一個查詢派生。更便宜。
  • 此外,這不是視窗函式的情況,因為您想要計算單個結果,而不是每行一個結果。
  • 轉換為任何支持小數位數的數字類型,例如@a_horse 已經解釋過

因為你想要round()兩個小數位數,我建議**numeric**(這與 Postgres 中的相同decimal)。在計算中輸入一個
值 就足夠了,最好是第一個。Postgres 自動選擇不失去資訊的類型。 或者,更簡單:因為我們無論如何都要相乘,所以使用一個由於小數點()而自動強制轉換為的數字常量。
numeric``100.0

  • 在除法之前乘以通常是個好主意。這通常可以最大限度地減少舍入誤差並且更便宜。

在這種情況下,第一個乘法 ( count(refinst) * 100) 可以用廉價而精確的integer算術計算。只有這樣我們才能轉換numeric為下一個並除以下一個integer(我們不會額外轉換)。

  • NULLIF(count(*), 0)防止被零除(引發異常)。如果根本沒有行,我們將 NULL 作為(未知)百分比。

四捨五入為兩位小數:

SELECT round((count(refinst) * 100)::numeric / NULLIF(count(*), 0), 2) AS refinst_pct
FROM   patients;

您需要將除法中涉及的每個數字轉換為支持小數的類型:

select (count(refinst)::decimal / (select count(*) from patients)::decimal) * 100  as "Formula" 
from patients;

您可能還想嘗試使用視窗函式而不是標量子查詢。它可能會更快:

select (count(refinst)::decimal / (count(*) over ())::decimal) * 100 as "Formula" 
from patients;

引用自:https://dba.stackexchange.com/questions/69108