คุณควรเลือกประเภทข้อมูล MONEY หรือ DECIMAL (x, y) ใน SQL Server หรือไม่


442

ฉันอยากรู้ว่าmoneyประเภทข้อมูลและสิ่งที่ต้องการแตกต่างกันหรือไม่decimal(19,4)(ซึ่งเป็นสิ่งที่เงินใช้ภายในฉันเชื่อ)

ฉันรู้ว่าmoneyเฉพาะเซิร์ฟเวอร์ SQL ฉันต้องการทราบว่ามีเหตุผลที่น่าสนใจที่จะเลือกหนึ่งมากกว่าอื่น ๆ ; ตัวอย่าง SQL Server ส่วนใหญ่ (เช่นฐานข้อมูล AdventureWorks) ใช้moneyและไม่ใช้decimalสำหรับข้อมูลต่าง ๆ เช่นราคา

ฉันควรใช้ประเภทข้อมูลเงินต่อไปหรือจะมีประโยชน์ในการใช้ทศนิยมแทนหรือไม่ เงินเป็นตัวอักษรที่พิมพ์น้อยลง แต่นั่นไม่ใช่เหตุผลที่ถูกต้อง :)


12
DECIMAL(19, 4) เป็นทางเลือกที่นิยมตรวจสอบนี้ตรวจสอบที่นี่โลกรูปแบบสกุลเงินที่จะตัดสินใจว่าหลายสถานที่ที่จะใช้ทศนิยมหวังช่วย
shaijut

ฉันสงสัยว่าเพราะเหตุใดประเภทข้อมูลเงินจึงมีทศนิยม 4 ตำแหน่ง .. และไม่ใช่ 2 นั่นคือ 100 เซ็นต์ในหนึ่งดอลลาร์ดังนั้นจำเป็นต้องมีทศนิยมเพียง 2 ตำแหน่งเท่านั้น สำหรับการจัดเก็บระเบียนเงินจำนวนน้อยกว่า $ 9999.99 ฉันจะไปกับชนิดข้อมูลทศนิยม (6,2) ฉันไม่ได้กังวลเกี่ยวกับการหารหรือการคำนวณแบบทวีคูณเพียงแค่จัดเก็บและสรุปผล ..
อัลลัน F

คำตอบ:


326

คุณไม่ควรใช้เงิน มันไม่แม่นยำและเป็นขยะที่บริสุทธิ์ ใช้ทศนิยม / ตัวเลขเสมอ

เรียกใช้สิ่งนี้เพื่อดูว่าฉันหมายถึงอะไร:

DECLARE
    @mon1 MONEY,
    @mon2 MONEY,
    @mon3 MONEY,
    @mon4 MONEY,
    @num1 DECIMAL(19,4),
    @num2 DECIMAL(19,4),
    @num3 DECIMAL(19,4),
    @num4 DECIMAL(19,4)

    SELECT
    @mon1 = 100, @mon2 = 339, @mon3 = 10000,
    @num1 = 100, @num2 = 339, @num3 = 10000

    SET @mon4 = @mon1/@mon2*@mon3
    SET @num4 = @num1/@num2*@num3

    SELECT @mon4 AS moneyresult,
    @num4 AS numericresult

เอาท์พุท: 2949.0000 2949.8525

สำหรับบางคนที่บอกว่าคุณไม่แบ่งเงินเป็นเงิน:

นี่คือหนึ่งในคำถามของฉันในการคำนวณความสัมพันธ์และการเปลี่ยนเป็นเงินจะให้ผลลัพธ์ที่ผิด

select t1.index_id,t2.index_id,(avg(t1.monret*t2.monret)
    -(avg(t1.monret) * avg(t2.monret)))
            /((sqrt(avg(square(t1.monret)) - square(avg(t1.monret))))
            *(sqrt(avg(square(t2.monret)) - square(avg(t2.monret))))),
current_timestamp,@MaxDate
            from Table1 t1  join Table1 t2  on t1.Date = traDate
            group by t1.index_id,t2.index_id

61
"ไม่เคย" เป็นคำที่แข็งแกร่ง "เงิน" มีประโยชน์สำหรับการแสดงผลลัพธ์เป็นประเภทนั้นเพื่อแสดงต่อผู้ใช้ในแบบที่มีความอ่อนไหวทางวัฒนธรรม แต่คุณพูดถูกว่าการใช้ตัวคำนวณนั้นไม่ดีนัก
Joel Coehoorn

36
ตัวอย่างของคุณไม่มีความหมายเนื่องจากไม่มีใครจะพิมพ์เงินสองประเภท หากคุณต้องการพิสูจน์จุดของคุณคุณจะต้องเปรียบเทียบการคูณเงินด้วยทศนิยมเพื่อคูณทศนิยมด้วยทศนิยม
ไบรอัน

27
.. แต่ก็ยังคงทำให้งงว่าทำไมเงิน * เงินจะไม่มีความแม่นยำของเงิน
เรียนรู้

4
@ การเรียนรู้: มันมีความแม่นยำของเงิน อย่างไรก็ตามคุณยังคงพบข้อผิดพลาดในการปัดเศษที่สามารถสะสมได้ตลอดเวลา ประเภททศนิยมไม่ได้ใช้เลขคณิตแบบไบนารี: มันรับประกันว่าจะได้รับผลลัพธ์พื้นฐาน 10 เดียวกับที่คุณทำบนกระดาษ
Joel Coehoorn

55
คูณและการหารของmoneyมากกว่าmoneyกันนี้ 'ภาพประกอบ' เป็นบิดเบือน มันเป็นข้อเท็จจริงที่บันทึกไว้ว่าmoneyมีความแม่นยำคงที่และ จำกัด มาก ในกรณีเช่นนี้ควรทวีคูณก่อนแล้วจึงแบ่ง เปลี่ยนลำดับของโอเปอเรเตอร์ในตัวอย่างนี้และคุณจะได้ผลลัพธ์เหมือนกัน โดยmoneyพื้นฐานแล้วคือ 64- บิต int และถ้าคุณต้องจัดการกับ ints คุณจะคูณก่อนหาร
Andriy M

272

SQLMenace กล่าวว่าเงินนั้นไม่แน่นอน แต่คุณไม่คูณ / หารเงินด้วยเงิน! เท่าไหร่ 3 ดอลลาร์คูณ 50 เซนต์ 150 ดอลล่าร์? คุณคูณ / หารเงินด้วยสเกลาร์ซึ่งควรเป็นทศนิยม

DECLARE
@mon1 MONEY,
@mon4 MONEY,
@num1 DECIMAL(19,4),
@num2 DECIMAL(19,4),
@num3 DECIMAL(19,4),
@num4 DECIMAL(19,4)

SELECT
@mon1 = 100,
@num1 = 100, @num2 = 339, @num3 = 10000

SET @mon4 = @mon1/@num2*@num3
SET @num4 = @num1/@num2*@num3

SELECT @mon4 AS moneyresult,
@num4 AS numericresult

ผลลัพธ์ในผลลัพธ์ที่ถูกต้อง:

ตัวเลขทางการเงิน
--------------------- ----------------------------- ----------
2949.8525 2949.8525

moneyดีตราบใดที่คุณไม่ต้องการตัวเลขทศนิยมมากกว่า 4 หลักและคุณต้องแน่ใจว่าสเกลาร์ของคุณซึ่งไม่แสดงถึงเงินdecimalนั้น


57
การเรียกเก็บเงินหนึ่งเหรียญสามารถรับคุณได้กี่เหรียญ คำตอบนี้ต้องใช้เงิน / เงิน
เรียนรู้

48
@ การเรียนรู้ในกรณีนั้นผลลัพธ์ไม่ใช่เงิน แต่เป็นแบบลอย (การวิเคราะห์มิติพื้นฐาน)
Richard

11
@Learning: คุณถามฐานข้อมูลกับดอลลาร์เป็นจำนวนเงินเท่าไหร่? อย่างไรก็ตามนั่นจะส่งคืนผลลัพธ์ที่ถูกต้อง ปัญหาของเขาคือเงิน / เงินมีความแม่นยำเพียงสี่หลัก (คือ 0.2949) จากนั้นเมื่อคูณด้วย 10,000 ก็กลายเป็น 2949.0000
กำหนดค่า

26
@ mson เขาถูกต้องและไม่วิจารณ์ส่วนตัวเหมือนที่คุณพูดจากคำพูดหนึ่งบรรทัด แต่ก็ไม่ได้เป็นประโยชน์ หากเขาไม่หารด้วย $ 0.01 แต่แทน 0.01 ผลลัพธ์ก็คือ $ 100 แทนที่จะเป็น 100 มี 100 เซนต์ในหนึ่งดอลลาร์ไม่ใช่ 100 เซนต์ หน่วยมีความสำคัญ! มีสถานที่ใหญ่สำหรับดำน้ำและอาจทวีคูณเงินเป็นเงิน
andrewb

19
หากฉันต้องการใช้จ่าย $ 1,000 เพื่อซื้อหุ้นที่ราคา $ 36 นั่นคือการใช้งานที่ไม่ถูกต้องตามกฎหมายmoney / moneyหรือไม่? คำตอบคือหน่วยทั้งหมดที่ไม่มีเครื่องหมายดอลลาร์ติดอยู่ซึ่งถูกต้อง! นี่เป็นสถานการณ์ที่เหมาะสมอย่างสมบูรณ์ซึ่งชี้ให้เห็นว่าเนื่องจากกรณีขอบแปลก ๆ เช่นนี้moneyไม่ใช่ชนิดข้อมูลที่ดีที่จะใช้เนื่องจากผลลัพธ์จะถูกตัดทอนกลับมาให้พอดีmoneyแทนที่จะทำตามกฎปกติเพื่อความแม่นยำและมาตราส่วน ถ้าคุณต้องการคำนวณค่าเบี่ยงเบนมาตรฐานของชุดของmoneyค่า ใช่แล้วSqrt(Sum(money * money))(เข้าใจง่าย) จริงแล้วสมเหตุสมผลแล้ว
ErikE

59

ทุกอย่างเป็นอันตรายถ้าคุณไม่รู้ว่าคุณกำลังทำอะไรอยู่

แม้แต่ประเภททศนิยมที่มีความแม่นยำสูงก็ไม่สามารถบันทึกวันได้:

declare @num1 numeric(38,22)
declare @num2 numeric(38,22)
set @num1 = .0000006
set @num2 = 1.0
select @num1 * @num2 * 1000000

1.000000 <- ควรเป็น 0.6000000


moneyประเภทเป็นจำนวนเต็ม

การนำเสนอข้อความของsmallmoneyและdecimal(10,4)อาจมีลักษณะเหมือนกัน แต่นั่นไม่ได้ทำให้พวกเขาแทนกันได้ คุณประจบประแจงเมื่อคุณเห็นวันที่จัดเก็บเป็นvarchar(10)? นี่คือสิ่งเดียวกัน

เบื้องหลัง, money/ smallmoneyเป็นเพียงแค่bigint/ int จุดทศนิยมในการแสดงข้อความmoneyเป็นปุยภาพเช่นเดียวกับขีดคั่นในวันที่ yyyy-mm-dd SQL ไม่ได้เก็บข้อมูลเหล่านั้นไว้ภายใน

เกี่ยวกับdecimalvs moneyให้เลือกสิ่งที่เหมาะสมกับความต้องการของคุณ มีmoneyประเภทอยู่เนื่องจากการจัดเก็บค่าการบัญชีเป็นทวีคูณจำนวน 1/10000 ของหน่วยเป็นเรื่องธรรมดามาก นอกจากนี้หากคุณกำลังจัดการกับเงินจริงและการคำนวณนอกเหนือจากการเพิ่มและการลบแบบง่ายคุณไม่ควรทำเช่นนั้นในระดับฐานข้อมูล! ทำในระดับแอปพลิเคชันด้วยห้องสมุดที่รองรับการปัดเศษของธนาคาร (IEEE 754)


1
คุณช่วยอธิบายสิ่งที่เกิดขึ้นในตัวอย่างนี้ได้ไหม
Sergey Popov

4
สเกลล้น ที่สเกลเล็กตัวเลข (a, b) * ตัวเลข (c, d) ให้ผลเป็นตัวเลข (a-b + c-d + 1, สูงสุด (b, d)) อย่างไรก็ตามถ้า (a + b + c + d)> 38, SQL จะขยายขนาด, การปล้นความแม่นยำจากด้านเศษส่วนไปยังแผ่นด้านจำนวนเต็มทำให้เกิดข้อผิดพลาดในการปัดเศษ
Anon

การคำนวณเชิงตัวเลขทั้งหมดมีความอ่อนไหวต่อการสูญเสียความแม่นยำเนื่องจากการปรับขนาด: คำนวณแทนเลือก 1000000 * @ num1 * @ num2
Mitch Wheat

ตัวอย่างของอานนท์เป็นรุ่นและฐานข้อมูลเฉพาะ แต่ประเด็นที่ควรระวังนั้นถูกต้อง ใน sqlanywhere รุ่น 1.1 ตัวอย่างให้ 0.600000 อย่างถูกต้อง (ฉันรู้ว่าเรากำลังพูดถึง ms sql ที่นี่) อีกจุดหนึ่งเกี่ยวกับประเภทเงินที่ขาดหายไปจากฐานข้อมูลอื่นเครื่องมีวิธีเรียกทศนิยมหรือตัวเลขเป็นเงินเช่นการสร้างโดเมน
gg89

3
ฉันคิดว่านี่เป็นคำตอบที่แจ่มแจ้งที่สุดเพราะคุณได้อธิบายไม่ใช่เพียงแค่ข้อผิดพลาดที่แพร่กระจายออกไป แต่สิ่งที่เกิดขึ้นจริงเบื้องหลังและทำไมเงินถึงมีอยู่ในตอนแรก ฉันไม่แน่ใจว่าเหตุใดจึงใช้เวลา 4 ปีในการรับคำตอบนี้ แต่อาจเป็นเพราะมีการเน้นไปที่การคำนวณ "ปัญหา" มากเกินไปและไม่เกี่ยวกับเรื่องเงินและทศนิยมเทียบกับทศนิยม
Steve Sether

44

ฉันรู้ว่า WayneM แจ้งว่าเขารู้ว่าเงินนั้นเฉพาะกับ SQL Server อย่างไรก็ตามเขากำลังถามว่ามีเหตุผลใดที่จะใช้เงินมากกว่าทศนิยมหรือในทางกลับกันและฉันคิดว่าเหตุผลหนึ่งที่ชัดเจนควรจะระบุไว้และนั่นคือการใช้ทศนิยมหมายความว่ามันเป็นเรื่องที่ไม่ต้องกังวลหากคุณต้องเปลี่ยน DBMS ของคุณ - ซึ่งสามารถเกิดขึ้นได้

ทำให้ระบบของคุณยืดหยุ่นมากที่สุด!


1
อาจเป็นไปได้ แต่ในทางทฤษฎีคุณสามารถประกาศโดเมนที่ชื่อว่า Money ใน DBMS อื่น (ที่รองรับการประกาศโดเมน)
อภิปราย

ในขณะที่ใครบางคนกำลังแปลงฐานข้อมูล SQL Server เป็น Amazon Redshift ฉันสามารถรับรองได้ว่านี่เป็นปัญหาของแท้ พยายามหลีกเลี่ยงชนิดข้อมูลที่ใช้กับแพลตฟอร์มฐานข้อมูลเฉพาะเว้นแต่มีเหตุผลทางธุรกิจที่ดีในการใช้งาน
Nathan Griffiths

@Nathn ให้ระบบประเภทอ่อนแอของ Redshift เมื่อเทียบกับ PostgreSQL หรือ SQL Server เช่นไม่มีdateประเภทฉันจะบอกว่าคุณจะเสมอมีปัญหาดังกล่าว คุณควรจะพูดว่า "อย่าใช้ชนิดเลิกเมื่อฐานข้อมูลที่มีอยู่แล้วให้ดีกว่าประเภทตามมาตรฐานมากขึ้นและเตือนกับคนที่เลิกใช้"
Panagiotis Kanavos

@PanagiotisKanavos - เงินไม่ได้ถูกคัดค้าน
Martin Smith

@MartinSmith ตกใจที่เห็นสิ่งนี้เพราะมันไม่สามารถจัดการกับค่าเงินอย่าง BTC ในปี 2560 หรือแยกความแตกต่างระหว่างจำนวน GBP และ EUR - แม้ว่าพวกเขาจะย้ายไปสู่ความเท่าเทียมกัน: P อย่างไรก็ตามการย้ายไป Redshift แนะนำมากของความเจ็บปวด
Panagiotis Kanavos

32

ฉันชอบMONEY! มันเป็นไบต์ที่ถูกกว่าDECIMALและการคำนวณนั้นเร็วกว่าเพราะ (ภายใต้ที่กำบัง) การเพิ่มและการลบเป็นการดำเนินการจำนวนเต็ม @ ตัวอย่างของ SQLMenace ซึ่งเป็นคำเตือนที่ยอดเยี่ยมสำหรับผู้ที่ไม่รู้ตัวสามารถนำไปประยุกต์ใช้กับINTegers ได้โดยที่ผลลัพธ์จะเป็นศูนย์ แต่นั่นเป็นเหตุผลที่จะไม่ใช้ integers- ไม่มีความเหมาะสม

ดังนั้นจึง 'ปลอดภัย' อย่างสมบูรณ์และเหมาะสมที่จะใช้MONEYเมื่อสิ่งที่คุณกำลังเผชิญอยู่MONEYและใช้งานตามกฎทางคณิตศาสตร์ที่ตามมา (เช่นเดียวกับINTeger)

มันจะดีกว่าไหมถ้า SQL Server เลื่อนระดับการหารและการคูณของMONEYเป็นDECIMALs (หรือFLOAT?) - อาจเป็นไปได้ แต่พวกเขาไม่ได้เลือกที่จะทำเช่นนี้ และพวกเขาไม่เลือกที่จะโปรโมตINTอีเกอร์กับFLOATเมื่อแบ่งพวกมัน

MONEYไม่มีปัญหาความแม่นยำ สิ่งนั้นDECIMALจะได้รับการใช้สื่อกลางที่มีขนาดใหญ่กว่าในระหว่างการคำนวณเป็นเพียง 'คุณสมบัติ' ของการใช้งานประเภทนั้น (และฉันไม่แน่ใจจริงๆ

หากต้องการตอบคำถามเฉพาะ "เหตุผลที่น่าสนใจ" ดีถ้าคุณต้องการประสิทธิภาพสูงสุดแน่นอนในSUM(x)ที่ที่xอาจจะเป็นอย่างใดอย่างหนึ่งDECIMALหรือMONEYแล้วMONEYจะมีขอบ

นอกจากนี้อย่าลืมว่ามันเป็นลูกพี่ลูกน้องที่เล็กกว่าSMALLMONEY- เพียงแค่ 4 ไบต์ แต่มันออกมาได้สูงสุด214,748.3647- ซึ่งค่อนข้างเล็กสำหรับเงิน - และดังนั้นจึงไม่เหมาะ

หากต้องการพิสูจน์จุดโดยใช้ชนิดสื่อกลางที่มีขนาดใหญ่กว่าถ้าคุณกำหนดสื่อกลางให้กับตัวแปรอย่างชัดเจนคุณจะพบDECIMALปัญหาเดียวกัน:

declare @a decimal(19,4)
declare @b decimal(19,4)
declare @c decimal(19,4)
declare @d decimal(19,4)

select @a = 100, @b = 339, @c = 10000

set @d = @a/@b

set @d = @d*@c

select @d

ผลิต 2950.0000 (โอเคดังนั้นอย่างน้อยDECIMALปัดเศษแทนที่จะMONEYถูกตัด - เช่นเดียวกับจำนวนเต็ม)


21
MONEYเป็นหนึ่งไบต์น้อยกว่าขนาดใหญ่ที่ DECIMALมีความแม่นยำสูงถึง 19 หลัก อย่างไรก็ตามการคำนวณทางการเงินส่วนใหญ่ในโลกแห่งความจริง (สูงถึง $ 9.99 M) สามารถใส่ลงใน a DECIMAL(9, 2)ซึ่งต้องการเพียงห้าไบต์ คุณสามารถประหยัดขนาดกังวลน้อยลงเกี่ยวกับข้อผิดพลาดในการปัดเศษและทำให้โค้ดของคุณพกพาได้มากขึ้น

ในขณะที่ @JonofAllTrades ถูกต้องคุณสามารถรับประสิทธิภาพจำนวนเต็มกลับมาได้ง่ายๆโดยใช้จำนวนเต็มที่มี pennies หรือเซนต์และบันทึกไบต์พิเศษที่เข้ารหัสตำแหน่งของจุดทศนิยมและต้องตรวจสอบเมื่อเพิ่มทศนิยมเข้าด้วยกัน
dsz

"ดังนั้นมัน 'ปลอดภัย' อย่างสมบูรณ์แบบและเหมาะสมที่จะใช้เงินเมื่อสิ่งที่คุณกำลังจัดการคือเงินและใช้ตามกฎทางคณิตศาสตร์ที่ตามมา" -> อย่างไรก็ตามดูคำตอบของ Anon: "ทุกอย่างเป็นอันตรายถ้าคุณไม่ รู้ว่าคุณกำลังทำอะไรอยู่ ". คุณไม่สามารถคาดเดาได้ว่าผู้คนจะค้นหาระบบที่คุณสร้างขึ้นอย่างไรดีที่สุดเพื่อหลีกเลี่ยงความเป็นไปได้ในการใช้งานผิดเมื่อคุณทำได้ การเพิ่มขนาดจิ๋วในที่เก็บข้อมูลไม่คุ้มกับ IMHO
Nathan Griffiths

1
ลองจัดเก็บค่า Bitcoin มันมี 8 ทศนิยม
Panagiotis Kanavos

14

เราเพิ่งเจอปัญหาที่คล้ายกันมากและตอนนี้ฉัน +1 มากสำหรับการไม่ใช้เงินยกเว้นในการนำเสนอระดับสูงสุด เรามีหลายตาราง (ใบยืนยันการขายและใบแจ้งหนี้การขายอย่างมีประสิทธิภาพ) ซึ่งแต่ละแห่งมีเขตข้อมูลอย่างน้อยหนึ่งรายการสำหรับเหตุผลทางประวัติศาสตร์และเราจำเป็นต้องทำการคำนวณตามสัดส่วนเพื่อให้ทราบว่าใบแจ้งหนี้รวมภาษีมีความเกี่ยวข้องกับแต่ละรายการเท่าใด บรรทัดบนบัตรกำนัลขาย การคำนวณของเราคือ

vat proportion = total invoice vat x (voucher line value / total invoice value)

ส่งผลให้เกิดการคำนวณเงิน / เงินในโลกแห่งความเป็นจริงซึ่งทำให้เกิดข้อผิดพลาดในส่วนของการแบ่งซึ่งจะทวีคูณเป็นสัดส่วนภาษีมูลค่าเพิ่มที่ไม่ถูกต้อง เมื่อเพิ่มค่าเหล่านี้ในภายหลังเราจะสรุปผลรวมของสัดส่วน vat ที่ไม่ได้รวมกับมูลค่าใบแจ้งหนี้ทั้งหมด มีค่าใดค่าหนึ่งในวงเล็บเป็นทศนิยม (ฉันกำลังจะโยนหนึ่งในนั้น) สัดส่วน vat จะถูกต้อง

เมื่อวงเล็บไม่เคยมีสิ่งนี้มาใช้ในการทำงานฉันเดาว่าเพราะมีค่ามากกว่าที่เกี่ยวข้องมันเป็นการจำลองระดับที่สูงขึ้นอย่างมีประสิทธิภาพ เราเพิ่มวงเล็บเพราะมันทำการคูณก่อนซึ่งในบางกรณีที่ไม่ค่อยมีความแม่นยำสำหรับการคำนวณ แต่ในตอนนี้สิ่งนี้ทำให้เกิดข้อผิดพลาดทั่วไปมากขึ้น


8
แต่ทำได้เพียงเพราะนักพัฒนาที่เพิกเฉยเพิกเฉยกฎสำหรับการคำนวณ VAT และข้อ จำกัด เรื่องเงินที่มีความแม่นยำ
TomTom

1
@TomTom แต่ประเด็นที่คุณทำเกี่ยวกับความเป็นไปได้ของการใช้ข้อมูลประเภทนี้ในทางที่ผิดนั้นเป็นข้อโต้แย้งเพื่อหลีกเลี่ยงการใช้งานเลย
Nathan Griffiths

@ นาธานไม่ฉันกำลังชี้ให้เห็นว่าการโต้แย้งที่ให้เป็นเหตุผลที่จะไม่ใช้มันเป็นนักพัฒนาที่ไร้ความสามารถโดยทั่วไปดังนั้นการโต้แย้งจึงเป็นการหลอกลวง
TomTom

1
@TomTom น่าเศร้าที่มีผู้พัฒนาที่ไร้ความสามารถมากมาย (รวมถึงคนที่ยอดเยี่ยมมาก) และสำหรับฉันถ้าฉันไม่สามารถรับประกันได้ว่าข้อมูลนี้จะถูกสอบถามในอนาคตได้อย่างไรและไม่มีใครทำผิดแบบที่อธิบายไว้ที่นี่ ดีกว่าที่จะผิดด้านความระมัดระวังและใช้ทศนิยม ที่กล่าวว่าเมื่ออ่านคำตอบทั้งหมดเหล่านี้ฉันสามารถเห็นว่ามีบางกรณีการใช้งานเฉพาะที่เงินจะเป็นประเภทที่ดีที่สุดที่จะใช้ฉันจะไม่ใช้มันเว้นแต่จะมีกรณีการใช้งานที่ดีมากสำหรับมัน (เช่นคอลัมน์จะเคย รวบรวมโดย SUM การโหลดข้อมูลทางการเงินจำนวนมาก)
Nathan Griffiths

@TomTom มันไม่ได้เป็นเพียงข้อ จำกัด ด้านความแม่นยำ การเป็นตัวแทนภายในอาจทำให้เกิดปัญหาได้เช่นกัน เงินเป็นประเภทความสะดวกสบายในการจับนักพัฒนาที่ไม่รู้ไม่ได้เป็นประเภทที่ยอดเยี่ยมที่นักพัฒนาซอฟต์แวร์ใช้ในทางที่ผิด ตัวอย่างภาษีมูลค่าเพิ่มโดยไม่คำนึงถึงกฎสำหรับการคำนวณควรชัดเจนว่าจะทำให้นักพัฒนาที่ไม่เขินอายออกไปเมื่อพวกเขาใช้งานเฉพาะกับคนทั่วไป
เจอราร์ดโอนีล

12

ในฐานะที่เป็นเคาน์เตอร์ชี้ไปที่แรงขับทั่วไปของคำตอบอื่น ๆ ดูประโยชน์มากมายของเงิน ... ประเภทข้อมูล! ในSQLCAT's Guide to Relational Engine

โดยเฉพาะฉันจะชี้ให้เห็นต่อไปนี้

เมื่อทำงานกับการใช้งานของลูกค้าเราพบว่ามีตัวเลขประสิทธิภาพที่น่าสนใจเกี่ยวกับประเภทข้อมูลการเงิน ตัวอย่างเช่นเมื่อ Analysis Services ถูกตั้งค่าเป็นชนิดข้อมูลสกุลเงิน (จากสองครั้ง) เพื่อให้ตรงกับชนิดข้อมูลเงินของ SQL Server มีการปรับปรุงความเร็วในการประมวลผล 13% (แถว / วินาที) เพื่อให้ได้ประสิทธิภาพที่เร็วขึ้นภายในบริการการรวมเซิร์ฟเวอร์ของ SQL (SSIS) โหลด 1.18 TB ภายในเวลาสามสิบนาทีดังที่ระบุไว้ใน SSIS 2008 - ประสิทธิภาพ ETL ที่บันทึกสถิติโลกพบว่าการเปลี่ยนคอลัมน์สี่ทศนิยม (9,2) ด้วยขนาด 5 ไบต์ในตาราง TPC-H LINEITEM เพื่อเงิน (8 ไบต์) ปรับปรุงความเร็วในการแทรกจำนวนมาก 20% ... เหตุผลสำหรับการปรับปรุงประสิทธิภาพเป็นเพราะโปรโตคอล SQL Data Data Stream (TDS) ของ SQL Server ซึ่งมีหลักการออกแบบที่สำคัญในการถ่ายโอนข้อมูลในรูปแบบไบนารีขนาดกะทัดรัดและใกล้เคียงกับรูปแบบการจัดเก็บข้อมูลภายในของ SQL Server สังเกตุเห็นได้ว่านี่เป็นข้อสังเกตในช่วงการทดสอบประสิทธิภาพ ETL ของ SSIS 2008 - สถิติโลกโดยใช้ Kernrate; โปรโตคอลลดลงอย่างมีนัยสำคัญเมื่อชนิดข้อมูลถูกเปลี่ยนเป็นเงินจากทศนิยม ทำให้การถ่ายโอนข้อมูลมีประสิทธิภาพมากที่สุด ชนิดข้อมูลที่ซับซ้อนต้องการการแยกวิเคราะห์เพิ่มเติมและรอบการทำงานของ CPU เพื่อจัดการมากกว่าชนิดความกว้างคงที่

ดังนั้นคำตอบของคำถามคือ "ขึ้นอยู่กับ" คุณต้องระมัดระวังมากขึ้นกับการดำเนินการเกี่ยวกับคณิตศาสตร์เพื่อรักษาความแม่นยำ แต่คุณอาจพบว่าการพิจารณาประสิทธิภาพทำให้สิ่งนี้คุ้มค่า


คุณจะเก็บบิทคอยน์อย่างไร?
Panagiotis Kanavos

@PanagiotisKanavos - ฉันไม่มีความคิด ฉันไม่เคยดูเป็น bitcoin และไม่รู้จริงเกี่ยวกับมันเลย!
Martin Smith

6

ฉันต้องการที่จะให้มุมมองที่แตกต่างกันของเงินกับตัวเลขโดยส่วนใหญ่ขึ้นอยู่กับความเชี่ยวชาญและประสบการณ์ของฉันเอง ... มุมมองของฉันที่นี่คือเงินเพราะฉันได้ทำงานกับมันมาเป็นเวลานานและไม่เคยใช้จริงมาก .. .

MONEY Pro:

  • พื้นเมืองชนิดข้อมูล ใช้ชนิดข้อมูลดั้งเดิม ( จำนวนเต็ม ) เหมือนกับการลงทะเบียน CPU (32 หรือ 64 บิต) ดังนั้นการคำนวณไม่จำเป็นต้องมีค่าใช้จ่ายที่ไม่จำเป็นดังนั้นจึงมีขนาดเล็กลงและเร็วขึ้น ... MONEY ต้องการ 8 ไบต์และตัวเลข (19, 4) ) ต้องการ 9 ไบต์ (ใหญ่กว่า 12.5%) ...

    MONEY นั้นเร็วขึ้นตราบใดที่เงินนั้นถูกใช้เพื่อให้เป็นเงิน เร็วแค่ไหน? SUMการทดสอบอย่างง่ายของฉันเกี่ยวกับข้อมูล 1 ล้านแสดงให้เห็นว่าเงินเป็น 275 มิลลิวินาทีและ NUMERIC 517 มิลลิวินาที ... นั่นเกือบจะเร็วกว่าสองเท่า ... ทำไมต้องทดสอบ SUM ดูจุดโปรถัดไป

  • ดีที่สุดสำหรับเงิน เงินเป็นวิธีที่ดีที่สุดสำหรับการเก็บเงินและการดำเนินการเช่นในบัญชี รายงานเดียวสามารถเรียกใช้การเพิ่ม (SUM) นับล้านและการคูณซ้ำหลังจากการดำเนินการ SUM เสร็จสิ้น สำหรับแอปพลิเคชันบัญชีขนาดใหญ่มากมันเร็วเป็นสองเท่าและมีความสำคัญอย่างยิ่ง ...
  • ความแม่นยำต่ำของเงิน เงินในชีวิตจริงไม่จำเป็นต้องแม่นยำมากนัก ฉันหมายถึงคนจำนวนมากอาจสนใจประมาณ 1 เซ็นต์ USD แต่ประมาณ 0.01 เซ็นต์ USD ในความเป็นจริงในประเทศของฉันธนาคารไม่สนใจเซนต์อีกต่อไป (หลักหลังจากจุลภาคทศนิยม) ฉันไม่รู้เกี่ยวกับธนาคารสหรัฐหรือประเทศอื่น ...

ข้อต่อเงิน:

  • จำกัด แม่นยำ เงินมีความแม่นยำเพียงสี่หลัก (หลังเครื่องหมายจุลภาค) ดังนั้นจึงต้องมีการแปลงก่อนทำการดำเนินการเช่นการหาร ... แต่จากนั้นmoneyไม่จำเป็นต้องแม่นยำอีกต่อไปและถูกนำมาใช้เป็นเงินไม่ใช่แค่ จำนวน...

แต่ ... ใหญ่ แต่ที่นี่เป็นแอปพลิเคชันของคุณที่เกี่ยวข้องกับเงินจริง แต่อย่าใช้ในการดำเนินการ SUM มากมายเช่นในการบัญชี หากคุณใช้งานดิวิชั่นและการคูณจำนวนมากแทนคุณไม่ควรใช้เงิน ...


1
หากคุณกำลังจะบอกว่าเงินเร็วกว่าทศนิยมคุณต้องบอกเราว่าอะไรคือ rdbms บนฮาร์ดแวร์ใดใช้ระบบปฏิบัติการอะไรกับข้อมูลเฉพาะที่เรียกใช้การสืบค้นเฉพาะที่คุณกำลังพูดถึง นอกจากนี้ถ้ามันไม่ถูกต้องโดยสมบูรณ์ฉันจะไม่ดำเนินการทางการเงินใด ๆ กับมันเพราะคนภาษีจะค่อนข้างไม่พอใจที่ได้รับตัวเลขที่ไม่ดีกลับมา คุณสนใจร้อยละหนึ่งพันหากคุณจัดการกับสกุลเงินบาท ถ้าเงินไม่ทำอย่างนั้นก็ใช้ไม่ได้
Haakon Løtveit

3
@ HaakonLøtveitคำถาม & คำตอบทั้งหมดนี้เกี่ยวกับ SQL data ชนิด "money" ดังนั้นฉันคิดว่าพวกเขาไม่จำเป็นต้องระบุในคำตอบ เงินสามารถใช้เร็วกว่าทศนิยมในบางสถานการณ์ (เช่นการโหลดข้อมูล) ดูคำตอบของ Martin Smith สำหรับรายละเอียดเพิ่มเติม ฉันเห็นด้วยกับส่วนใหญ่ของคำตอบนี้ในกรณีที่มีบางกรณีการใช้งานที่อาจทำให้เงินเป็นทางเลือกที่มีประสิทธิภาพมากกว่าทศนิยมอย่างไรก็ตามถ้ามีกรณีที่น่าสนใจมากสำหรับการใช้มันฉันคิดว่ามันควรหลีกเลี่ยง
Nathan Griffiths

1
Bitcoin มี 8 ทศนิยม คุณไม่สามารถแม้แต่เก็บไว้ในmoneyคอลัมน์
Panagiotis Kanavos

4

โพสต์ก่อนหน้าทั้งหมดนำคะแนนที่ถูกต้อง แต่บางคนไม่ตอบคำถามอย่างแม่นยำ

คำถามคือทำไมคนที่ต้องการเงินเมื่อเรารู้แล้วว่ามันเป็นชนิดข้อมูลที่แม่นยำน้อยกว่าและอาจทำให้เกิดข้อผิดพลาดหากใช้ในการคำนวณที่ซับซ้อน?

คุณใช้เงินเมื่อคุณจะไม่ทำการคำนวณที่ซับซ้อนและสามารถแลกเปลี่ยนความแม่นยำนี้สำหรับความต้องการอื่น ๆ

ตัวอย่างเช่นเมื่อคุณไม่ต้องทำการคำนวณเหล่านั้นและจำเป็นต้องนำเข้าข้อมูลจากสตริงข้อความสกุลเงินที่ถูกต้อง การแปลงอัตโนมัตินี้ทำงานเฉพาะกับประเภทข้อมูล MONEY:

SELECT CONVERT(MONEY, '$1,000.68')

ฉันรู้ว่าคุณสามารถทำตามขั้นตอนการนำเข้าของคุณเอง แต่บางครั้งคุณไม่ต้องการสร้างรูทีนการนำเข้าอีกครั้งด้วยรูปแบบโลแคลเฉพาะทั่วโลก

อีกตัวอย่างหนึ่งเมื่อคุณไม่ต้องทำการคำนวณเหล่านั้น (คุณเพียงแค่ต้องเก็บค่า) และจำเป็นต้องบันทึก 1 ไบต์ (เงินใช้เวลา 8 ไบต์และทศนิยม (19,4) ใช้เวลา 9 ไบต์) ในบางแอพพลิเคชั่น (CPU ความเร็วสูง, RAM ขนาดใหญ่, IO ช้า) เช่นเดียวกับการอ่านข้อมูลจำนวนมากซึ่งอาจเร็วขึ้นเช่นกัน


2

คุณไม่ควรใช้เงินเมื่อคุณต้องทำการคูณ / ดิวิชั่นกับมูลค่า เงินจะถูกจัดเก็บในลักษณะเดียวกับที่เป็นจำนวนเต็มในขณะที่ทศนิยมจะถูกเก็บไว้เป็นจุดทศนิยมและหลักทศนิยม ซึ่งหมายความว่าเงินจะลดลงความแม่นยำในกรณีส่วนใหญ่ในขณะที่ทศนิยมจะทำเมื่อแปลงกลับไปเป็นระดับเดิม เงินเป็นจุดคงที่ดังนั้นขนาดจะไม่เปลี่ยนแปลงระหว่างการคำนวณ อย่างไรก็ตามเนื่องจากเป็นจุดคงที่เมื่อได้รับการพิมพ์เป็นสตริงทศนิยม (ตรงข้ามกับตำแหน่งคงที่ในสตริงฐาน 2) ค่าสูงสุดถึงสเกลของ 4 จะถูกแทนอย่างแน่นอน ดังนั้นสำหรับการบวกและการลบเงินก็ดี

ทศนิยมจะแสดงในฐาน 10 ภายในดังนั้นตำแหน่งของจุดทศนิยมจะขึ้นอยู่กับตัวเลขฐาน 10 ซึ่งทำให้ส่วนที่เป็นเศษส่วนแทนค่าของมันอย่างแน่นอนเช่นเดียวกับเงิน ความแตกต่างคือค่าทศนิยมกลางสามารถรักษาความแม่นยำได้สูงสุด 38 หลัก

ด้วยจำนวนจุดลอยตัวค่าจะถูกเก็บไว้ในไบนารีราวกับว่ามันเป็นจำนวนเต็มและตำแหน่งของจุดทศนิยม (หรือไบนารี, ahem) จะสัมพันธ์กับบิตที่เป็นตัวแทนของตัวเลข เนื่องจากเป็นทศนิยมฐานสองตัวเลขฐาน 10 จึงสูญเสียความแม่นยำหลังจากจุดทศนิยม 1 / 5th หรือ 0.2 ไม่สามารถแสดงอย่างแม่นยำด้วยวิธีนี้ ทั้งเงินและทศนิยมไม่ได้รับผลกระทบจากข้อ จำกัด นี้

มันง่ายพอที่จะแปลงเงินเป็นทศนิยมดำเนินการคำนวณแล้วเก็บค่าผลลัพธ์ลงในเขตข้อมูลหรือตัวแปร

จากมุมมองของฉันฉันต้องการสิ่งที่เกิดขึ้นกับตัวเลขที่จะเกิดขึ้นโดยไม่ต้องคิดมากเกินไปกับพวกเขา หากการคำนวณทั้งหมดจะได้รับการแปลงเป็นทศนิยมจากนั้นสำหรับฉันฉันแค่ต้องการใช้ทศนิยม ฉันจะบันทึกช่องเงินเพื่อวัตถุประสงค์ในการแสดงผล

ฉลาดขนาดฉันไม่เห็นความแตกต่างพอที่จะเปลี่ยนความคิดของฉัน เงินใช้เวลา 4 - 8 ไบต์ในขณะที่ทศนิยมสามารถเป็น 5, 9, 13 และ 17 ไบต์ 9 สามารถครอบคลุมช่วงทั้งหมดที่ 8 ไบต์ของเงินสามารถ ดัชนีฉลาด (การเปรียบเทียบและการค้นหาควรเปรียบเทียบได้)


ขอบคุณสำหรับการโหวต ฉันดูที่นี้และในขณะที่ได้รับสิ่งที่ฉันพยายามจะพูดฉันก็สามารถเห็นว่ามันสับสนมาก บางทีฉันจะเขียนมันใหม่ในภายหลังด้วยตัวอย่างบิตเทียบกับทศนิยม
เจอราร์ดโอนีล

2

ฉันพบเหตุผลเกี่ยวกับการใช้ทศนิยมมากกว่าเงินในเรื่องความถูกต้อง

DECLARE @dOne   DECIMAL(19,4),
        @dThree DECIMAL(19,4),
        @mOne   MONEY,
        @mThree MONEY,
        @fOne   FLOAT,
        @fThree FLOAT

 SELECT @dOne   = 1,
        @dThree = 3,    
        @mOne   = 1,
        @mThree = 3,    
        @fOne   = 1,
        @fThree = 3

 SELECT (@dOne/@dThree)*@dThree AS DecimalResult,
        (@mOne/@mThree)*@mThree AS MoneyResult,
        (@fOne/@fThree)*@fThree AS FloatResult

DecimalResult> 1.000000

MoneyResult> 0.9999

FloatResult> 1

เพียงทดสอบและตัดสินใจ


2
เราอยากจะฟัง (อ่านนั่นคือ) ข้อสรุปของคุณ
Peter Mortensen

@PeterMortensen ฉันคิดว่าถ้าฉันต้องการความสมบูรณ์และความถูกต้องระหว่างประเภทเงินและทศนิยมการตัดสินใจของฉันควรเป็นเลขทศนิยม
QMaster

1
ลองอัปเดตคำตอบของคุณด้วยผลลัพธ์ตามจริงด้านบน จากนั้นข้อสรุปของคุณจะชัดเจนทันทีสำหรับทุกคนที่อ่าน :-)
Agreax

1
@Ageax ผลลัพธ์ถูกเพิ่มเข้าไปในคำตอบ
QMaster

1

ฉันเพิ่งเห็นเข้าบล็อกนี้: เงินกับทศนิยมใน SQL Server

ซึ่งโดยทั่วไปบอกว่าเงินมีปัญหาความแม่นยำ ...

declare @m money
declare @d decimal(9,2)

set @m = 19.34
set @d = 19.34

select (@m/1000)*1000
select (@d/1000)*1000

สำหรับmoneyประเภทคุณจะได้รับ 19.30 แทน 19.34 ฉันไม่แน่ใจว่ามีสถานการณ์แอปพลิเคชั่นที่แบ่งเงินเป็น 1,000 ส่วนเพื่อการคำนวณหรือไม่ แต่ตัวอย่างนี้แสดงข้อ จำกัด บางอย่าง


15
ไม่ใช่ "ปัญหา" เป็น "ตามข้อกำหนด": « ประเภทmoneyและsmallmoneyข้อมูลมีความแม่นยำต่อหนึ่งในหมื่นของหน่วยเงินที่พวกเขาเป็นตัวแทน» ดังที่ได้กล่าวไว้ในmsdn.microsoft.com/en-us/library/ms179882.aspxดังนั้นใครก็ตามที่พูดว่า "มันเป็นขยะ" ไม่ทราบว่าเขากำลังพูดถึงอะไร
Albireo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.