PostgreSQL: Datatype nào nên được sử dụng cho Tiền tệ?


128

Có vẻ như Moneyloại được khuyến khích như được mô tả ở đây

Ứng dụng của tôi cần lưu trữ tiền tệ, tôi sẽ sử dụng loại dữ liệu nào? Số, tiền hay FLOAT?


7
Nếu bạn đã đọc toàn bộ chủ đề, Numeric là cách để đi.
razpeitia

Đối với bất kỳ ai làm việc với nhiều loại tiền tệ và quan tâm đến việc lưu trữ mã tiền tệ ngoài số tiền, bạn có thể muốn xem mô hình tiền tệ trong cơ sở dữ liệu (SO) và ISO 4217 (Wikipedia). Câu trả lời ngắn gọn là bạn sẽ cần hai cột.
Fabien Snauwaert

Câu trả lời:


90

Số với độ chính xác 2 đơn vị. Không bao giờ sử dụng float hoặc float như datatype để đại diện cho tiền tệ bởi vì nếu bạn làm như vậy, mọi người sẽ không hài lòng khi số liệu cuối cùng của báo cáo tài chính không chính xác bằng + hoặc - một vài đô la.

Loại tiền chỉ còn lại vì những lý do lịch sử theo như tôi có thể nói.


9
Đó không phải là lý do tại sao bạn tránh điểm nổi. Ngay cả Numeric cũng sẽ có lỗi làm tròn nếu bạn chia cho bất kỳ thứ gì không chia thành lũy thừa mười, bất kể bạn sử dụng độ chính xác nào. (Chính xác là 2 dù sao cũng là một ý tưởng tồi ... hãy kiểm tra các tài liệu.)
Doradus

6
Nếu bạn muốn hỗ trợ các loại tiền tệ tùy ý, không bao giờ tạo tỷ lệ bằng 2 (theo thuật ngữ postgresql, độ chính xác là một số của tất cả các chữ số, ví dụ như trong 121.121, nó bằng 6). Có những loại tiền tệ, chẳng hạn như Bahrain Dinar, trong đó 1000 đơn vị con bằng một đơn vị và có những loại tiền tệ không có đơn vị phụ nào cả.
Nikolay Arhipov

@NikolayArhipov điểm tốt, vì vậy tối đa là thực sựscale - precision
Konrad

1
numeric(3,2)sẽ có thể lưu trữ tối đa9.99 3-2 = 1
Konrad

5
Đừng làm điều này! Trừ khi bạn có kế hoạch nhân 100 trước khi tải bất kỳ giá trị nào sang các ngôn ngữ khác và sau đó làm toán với số nguyên - bạn sẽ có kết quả sai. Lưu trữ mọi thứ bằng xu (đơn vị tiền tệ nhỏ nhất bạn đang giao dịch) và tiết kiệm cho mình một số rắc rối. Câu trả lời rất tệ trong nhiều trường hợp.
Avamander

111

Nguồn của bạn là không có cách nào chính thức. Nó có từ năm 2011 và tôi thậm chí không nhận ra các tác giả. Nếu loại tiền chính thức "nản lòng" thì PostgreSQL sẽ nói như vậy trong hướng dẫn - điều này không có .

Để có nguồn chính thức hơn , hãy đọc chủ đề này bằng pssql-general (chỉ từ tuần này!) , Với các tuyên bố từ các nhà phát triển cốt lõi bao gồm D'Arcy JM Cain (tác giả ban đầu của loại tiền) và Tom Lane:

Câu trả lời liên quan (và ý kiến!) Về những cải tiến trong các phiên bản gần đây:

Về cơ bản, moneycó công dụng (rất hạn chế) của nó. Các Postgres Wiki gợi ý cho phần lớn tránh nó, ngoại trừ đối với những số ít trường hợp. Lợi thế hơn numerichiệu suất .

decimalchỉ là một bí danh cho numericPostgres và được sử dụng rộng rãi cho dữ liệu tiền tệ, là một loại "độ chính xác tùy ý". Hướng dẫn sử dụng :

Loại numericcó thể lưu trữ số với số lượng chữ số rất lớn. Đặc biệt khuyến nghị lưu trữ số tiền và số lượng khác khi cần độ chính xác.

Cá nhân, tôi thích lưu trữ tiền tệ như là integerđại diện cho Cents nếu Cents phân đoạn không bao giờ xảy ra (về cơ bản là tiền có ý nghĩa). Đó là hiệu quả hơn bất kỳ tùy chọn khác được đề cập.


4
Có một số cuộc thảo luận về danh sách gửi thư mang lại ấn tượng rằng loại tiền ít nhất không được khuyến nghị, ví dụ: tại đây: postgresql.nabble.com/Money-type-todos-td1964190.html#a1964192 cộng với công bằng: hướng dẫn sử dụng cho phiên bản 8.2 đã gọi nó là không dùng nữa: postgresql.org/docs/8.2/static/datatype-money.html
a_horse_with_no_name

11
@a_horse_with_no_name: Liên kết của bạn là một chuỗi từ năm 2007, đó cũng là khi 8.2 là phiên bản hiện tại và moneytrên thực tế, loại này không được dùng nữa. Các vấn đề đã được khắc phục và loại đã được thêm lại trong các phiên bản sau. Cá nhân tôi thích lưu trữ tiền tệ như integerđại diện cho Cents.
Erwin Brandstetter

1
Erwin, bạn có thể suy nghĩ đúng từ góc độ cơ sở dữ liệu một mình. Tuy nhiên, nếu bạn kết hợp Postgresql + Java, thì KHÔNG tốt chút nào (theo kinh nghiệm của tôi). Đọc bình luận của bạn, tôi đã sử dụng TIỀN cho hầu hết các trường tiền tệ của mình và bây giờ tôi nhận được ngoại lệ Java này: " Xảy ra lỗi: org.postgresql.util.PSQLException: Giá trị xấu cho loại double: 2.500.00 ". Tôi đã googled và không tìm thấy giải pháp tốt, vì vậy tôi đang ở trong nhiệm vụ nhàm chán là thay đổi tất cả chúng thành SỐ LƯỢNG hoặc KHAI THÁC ngay bây giờ !!!
MD

1
@MD: Xin lỗi khi nghe điều đó, nhưng rõ ràng tôi không nói được Java (mà tôi không thể). Thông báo lỗi là lẻ. "gấp đôi"? Và dải phân cách hàng ngàn cũng có thể là một vấn đề. Bạn có thể muốn bắt đầu một câu hỏi mới về điều đó.
Erwin Brandstetter

2
@PirateApp: Vâng, yêu thích cá nhân của tôi. Bạn có thể đã bỏ lỡ câu cuối cùng trong câu trả lời của tôi, chỉ nói vậy thôi.
Erwin Brandstetter

68

Lựa chọn của bạn là:

  1. bigint: lưu trữ số tiền tính bằng xu. Đây là những gì giao dịch EFTPOS sử dụng.
  2. decimal(12,2): lưu trữ số tiền với chính xác hai chữ số thập phân. Đây là những gì hầu hết các phần mềm sổ cái sử dụng.
  3. float: ý tưởng khủng khiếp - độ chính xác không đầy đủ. Đây là những gì các nhà phát triển ngây thơ sử dụng.

Tùy chọn 2 là phổ biến nhất và dễ làm việc nhất. Làm cho độ chính xác (12 trong ví dụ của tôi, có nghĩa là tất cả 12 chữ số) lớn hoặc nhỏ là phù hợp nhất với bạn.

Lưu ý rằng nếu bạn đang tổng hợp nhiều giao dịch là kết quả của phép tính (ví dụ: liên quan đến tỷ giá hối đoái) thành một giá trị có ý nghĩa kinh doanh, thì độ chính xác phải cao hơn để cung cấp giá trị vĩ mô chính xác; xem xét sử dụng một cái gì đó như thế decimal(18, 8)để tổng là chính xác và các giá trị riêng lẻ có thể được làm tròn đến cent chính xác để hiển thị.


10
Nếu bạn đang làm việc với bất kỳ loại tính thuế ngược hoặc ngoại hối nào, bạn cần ít nhất 4 chữ số thập phân, nếu không bạn sẽ mất dữ liệu. Vì vậy, numeric(15,4)hoặc numeric(15,6)là một ý tưởng tốt.
Petrus Theron

3
Có một tùy chọn thứ 4 - đó là sử dụng Chuỗi và sử dụng loại thập phân không mất tương đương trong ngôn ngữ máy chủ.
ioquatix

2
Điều gì về một số nguyên được chia tỷ lệ, chắc chắn lưu trữ 10000.045 sẽ không bị tổn thương nếu nó được lưu trữ là 10000045 với hệ số tỷ lệ 1000x?
PirateApp

26

Tôi giữ tất cả các lĩnh vực tiền tệ của mình như:

numeric(15,6)

Có vẻ như có quá nhiều vị trí thập phân, nhưng nếu có cơ hội nhỏ nhất bạn sẽ phải giao dịch với nhiều loại tiền tệ, bạn sẽ cần độ chính xác cao đó để chuyển đổi. Bất kể tôi đang trình bày người dùng là gì, tôi luôn lưu trữ bằng Đô la Mỹ. Bằng cách đó, tôi có thể dễ dàng chuyển đổi sang bất kỳ loại tiền tệ nào khác, với tỷ lệ chuyển đổi cho ngày liên quan.

Nếu bạn không bao giờ làm bất cứ điều gì ngoại trừ một loại tiền tệ, điều tồi tệ nhất ở đây là bạn đã lãng phí một chút không gian để lưu trữ một số số không.


6
Điều này có nguy cơ kết quả sai vì thiếu cắt ngắn. Ví dụ, nếu các giá trị khác không vô tình rò rỉ vào các vị trí thập phân còn lại, ví dụ, trường giá chứa 0,333333 đô la, thì bạn có thể gặp tình huống hệ thống hiển thị kết quả của việc ai đó mua 3 mặt hàng với giá 0,33 đô la mỗi cái, tổng cộng lên tới 1 đô la thay vì 0,99 đô la.
Peteris

1
Perteris, vậy bạn đề nghị gì thay thế? Không có vấn đề chính xác bao nhiêu bạn ném vào vòng này có thể là một vấn đề. Tôi chỉ đơn giản là không tìm thấy một cách tốt hơn, ngay cả khi cách này không lý tưởng.
Michael Collette

3
Điểm cố định và cắt ngắn bất cứ nơi nào thích hợp. Ngay khi bạn đạt được giá trị tiền "có thể lưu trữ", ví dụ như giá được đưa ra cho khách hàng, thì đó phải là số liệu thích hợp, trong hầu hết các trường hợp sẽ là toàn bộ xu trong môi trường bán lẻ tiêu chuẩn. Nếu bạn có nhu cầu kinh doanh khác nhau (ví dụ: giá của hàng hóa khối lượng lớn trên mỗi đơn vị), có thể có một cài đặt chính xác khác nhau, nhưng bạn phải xử lý bản trình bày cùng với lưu trữ - nếu bạn hiển thị số tiền với x số thập phân (hoặc ngược lại, ví dụ trong toàn bộ hàng ngàn) thì bạn cũng phải lưu trữ nó với độ chính xác đó , không kém nhưng cũng không hơn.
Peteris

Đối với nhiều trang web liên quan đến bán lẻ có thể làm việc. Dự án chính mà tôi làm việc có thể có một bên cần xem cùng một chi phí bằng một loại tiền tệ, với một khách hàng bằng loại tiền khác, cho một nhà cung cấp ở lần thứ 3.
Michael Collette

19

Sử dụng số nguyên 64 bit được lưu dưới dạng bigint

Tôi khuyên bạn nên sử dụng đô la vi mô (hoặc tiền tệ chính tương tự). Micro có nghĩa là 1 triệu nên 1 micro-đô la = $ 0,000001.

  • Sử dụng đơn giản và tương thích với mọi ngôn ngữ.
  • Đủ chính xác để xử lý các phân số của một xu.
  • Hoạt động với giá rất nhỏ trên mỗi đơn vị (như hiển thị quảng cáo hoặc phí API).
  • Kích thước dữ liệu nhỏ hơn để lưu trữ hơn chuỗi hoặc số.
  • Dễ dàng duy trì độ chính xác thông qua các tính toán và áp dụng làm tròn ở đầu ra cuối cùng.

1
Đây là câu trả lời đúng nhất và bất kỳ giao dịch phần mềm hợp lý nào với đơn vị tiền tệ nhỏ nhất trong tay. Làm tất cả các phép toán trên các số nguyên có nghĩa là bạn không phải đối phó với bất kỳ việc xáo trộn ngôn ngữ cụ thể nào.
Avamander

1
Sẽ được sử dụng quá. Cảm ơn

Tại sao điều này hơn numeric(15,6)đề nghị trong một câu trả lời khác?
Juliusz Gonera

@JuliuszGonera Vì những lý do được liệt kê trong câu trả lời. Các số nguyên nhỏ hơn và được hỗ trợ ở mọi nơi và tránh tất cả các bài toán rút gọn. Về cơ bản, nó sử dụng số nhưng thay đổi số thập phân để bạn có toàn bộ số tương thích hơn nhiều.
Mani Gandham

1
Ah, phải, tôi đã bỏ lỡ phần về lưu trữ. Cảm ơn! Về "Đơn giản để sử dụng và tương thích với mọi ngôn ngữ", thật không may, JavaScript hỗ trợ các số nguyên lên tới 9007199254740991 nhỏ hơn 1000 lần so với giá trị tối đa bigint. Có developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ mẹo nhưng nó đi kèm với sự hỗ trợ hạn chế (hiện tại) và hãy cẩn thận (ví dụ: bạn không thể nhân nó một cách dễ dàng khi thực hiện chuyển đổi tiền tệ) . Cho rằng số tiền tối đa bạn có thể lưu trữ trong một số nguyên JS bằng cách sử dụng vi mô là 9 tỷ đô la, điều đó có lẽ vẫn tốt cho hầu hết các trường hợp.
Juliusz Gonera

3

Sử dụng BigIntđể lưu trữ tiền tệ dưới dạng số nguyên dương biểu thị giá trị tiền tệ theo đơn vị tiền tệ nhỏ nhất (ví dụ: 100 xu để lưu trữ $ 1 hoặc 100 để lưu trữ ¥ 100 (Yên Nhật, tiền tệ thập phân 0). Đây là những gì Stripe thực hiện - một các công ty dịch vụ tài chính quan trọng nhất cho thương mại điện tử toàn cầu.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.