SQL Serverda Decimal ni ikkilikka aylantirish uchun Datalprostdan ortiq baytlarni talab qilish

Decimal (4,0) qiymati SQL Serverda saqlash uchun 5 bayt talab qiladi. Ushbu hujjat barcha hujjatlarga mos keladi: kasrli va raqamli 1-9 o'lchovlari bilan 5 baytni oladi. Bundan tashqari, DATALENGTH funktsiyasi tasdiqlaydi:

select DATALENGTH(convert(Decimal(4,0),256)) result

result
-----------
5

(1 row(s) affected)

Biroq, ikkilik (5) ga o'zgartirgandan so'ng, decimal (4,0) ga qaytib kelganida ma'lumotni qisqartiradi.

DECLARE @myval decimal (4, 0);  
SET @myval = 257;  
SELECT CONVERT(decimal(4,0), CONVERT(varbinary(5), @myval)) result

result
---------------------------------------
1

(1 row(s) affected)

Sonning oxirgi baytlari kesilgan. Ammo, ikkilik (6) yoki undan ko'prokka aylantirsam ... To'g'ri natijalarni olaman:

DECLARE @myval decimal (4, 0);  
SET @myval = 257;  
SELECT CONVERT(decimal(4,0), CONVERT(binary(6), @myval)) result  

result
---------------------------------------
257

(1 row(s) affected)

Nimalar bo'lyapti? Agar o'nlikning ikkilik vakili saqlanishim kerak bo'lsa, qancha bayta kerak bo'ladi? Ayniqsa, o'nli kasrlarni (p, s) ikkilik (x) ga aylantirish uchun zarur bo'lgan eng kichik sonni x belgilash uchun umumiy formulalar nima?

Ikki tomonlama ma'lumotni xizmat ko'rsatuvchi brokerining xabariga kiritishim kerak, shuning uchun men turli xil ma'lumotlarning ikkilik mag'lubiyatga aylantirilishi kerak. Olingan qiymatlarni ikkilikda saqlash/almashtirishdan foydalanishga nisbatan ancha ishonchli usul bormi?

7
Qiymatning ichki/ikkilamchi saqlash tartibi siz qila olmaydigan narsadir va kerak emas deb hisoblash uchun narsa deb hisoblang. . Bu XY-muammo ... Iltimos, ko'proq ma'lumotni taqdim eting, aslida nima qilishni istaysiz va Ishonchim komilki, yaxshiroq yondashuv bor ...
qo'shib qo'ydi muallif Shnugo, manba
Brian, men bunday qilmaslikka maslahat beraman! Ichki vakillik o'zgarishi mumkin, strings o'zgarishi mumkin (utf-16, kengaytirilgan ASCII, maxsus belgilar/harmanlama qaramligini kodlash), float saqlash o'z-o'zidan ancha murakkab, o'nlikda turli xil naqshlarni ichki foydalanadi ... Bir bit naqsh ... marshal bilan xizmat brokerligi xabari bilan nimani anglatadi?
qo'shib qo'ydi muallif Shnugo, manba
SQL-server qaysi versiyasini ishlatasiz?
qo'shib qo'ydi muallif Shnugo, manba
2016-yilda JSON-quvvatlash bor. U JSON (yoki kichik bir fatter XML) yaratish uchun yordam beradimi? Shunday qilib, siz barcha ma'lumotlarni string formatiga aylantirasiz ...
qo'shib qo'ydi muallif Shnugo, manba
Variant ma'lumotlarni saqlash uchun sql_variant foydalanishni ko'rib chiqdingizmi? Sql_variant turidagi ustun turli xil ma'lumotlar turlarini
qo'shib qo'ydi muallif Steve Ford, manba
Hozir 2008 yil R2, lekin 2014 yoki 2016 yiligacha tahmin qilaman.
qo'shib qo'ydi muallif Brian Pressler, manba
xizmat ko'rsatish vositachisi yordamida juda ko'p ma'lumot yuboraman va serverlar o'rtasida matn yoki XMLga qaraganda samaraliroq bo'lishi kerak, shuning uchun marshal ma'lumotlar.
qo'shib qo'ydi muallif Brian Pressler, manba
Men har xil turdagi ma'lumotlarni xizmat ko'rsatuvchi brokerining xabariga o'tkazishga harakat qilaman. Boshqa turlarni ikkilangan (varchar, integers, floats, va boshqalar) ga hech qanday muammosiz aylantirmoqchiman va boshqa turdagi ogohlantirishlar yo'qligini bilsam. Lekin shunga qaramay, kasr turlarini samarali va mustahkam tarzda aylantira olishim kerak.
qo'shib qo'ydi muallif Brian Pressler, manba

6 javoblar

DATALENGTH dan boshlaylik. MSDN dan:

DATALENGTH ayniqsa, varchar, varbinary, matn, tasvir,   nvarchar va ntext ma'lumotlar turlari, chunki bu ma'lumotlar turlari saqlanishi mumkin   o'zgaruvchan uzunlikdagi ma'lumotlar.

Eng kichik kasr uzunligi 5 bayta, maksimal - 17 bayt. Decimal (p, s) o'zgaruvchan uzunlikdagi ma'lumotlar emas. Keskinlik bo'yicha aniq uzunligi bor. Misol uchun, raqamning uzunligi 1dan 9gacha bo'lsa, DATALENGTH har doim 5

select DATALENGTH(convert(Decimal(38,0), 1)) -- result 5
select DATALENGTH(convert(Decimal(38,0), 1234567890)) -- result 5

agar raqam uzunligi 10 dan 19 gacha bo'lsa, u holda DATALENGTH har doim 9 marta qaytadi

select DATALENGTH(convert(Decimal(38,0), 12345678901)) -- result 9
select DATALENGTH(convert(Decimal(38,0), 111111111111111)) -- result 9

shuning uchun DATALENGTH natijasi sonlarning uzunligiga bog'liq, lekin u haqiqiy uzunlik emas.

Agar siz decimal (4,0) kodini ikkilik (5) ga o'zgartirganingizda 0x04 00 00 01 00 Bunday holda, raqamingiz uchun faqat oxirgi bayt qoldiriladi. 1 baytda saqlanishi mumkin bo'lgan maksimal raqam 255 (HEX da 255 FF teng)

Shunday qilib, hamma narsa yaxshi ishlaydi:

DECLARE @myval decimal (4, 0);  
SET @myval = 255;  
SELECT CONVERT(decimal(4,0), CONVERT(binary(5), @myval)) result, CONVERT(binary(5), @myval)

result                                  HEX
--------------------------------------- ------------
255                                     0x04000001FF

Endi 255 o'rniga 256 raqamini sinab ko'ring. HEX da 256 100 teng bo'lsa, biz 1 baytda 100 ni saqlay olmaysiz (HEX 0x04 00 00 01 00 1 , lekin 1 ga joy yo'q)

DECLARE @myval decimal (4, 0);  
SET @myval = 256;  
SELECT CONVERT(decimal(4,0), CONVERT(binary(5), @myval)) result, CONVERT(binary(5), @myval) HEX
result                                  HEX
--------------------------------------- ------------
0                                       0x0400000100

Raqamlarni 0 dan 9999 gacha saqlamoqchi bo'lsangiz, unda kamida 6 bayta kerak bo'ladi. 257 ga qarang (HEX da 101 ga teng)

DECLARE @myval decimal (4, 0);  
SET @myval = 257;  
SELECT CONVERT(decimal(4,0), CONVERT(binary(6), @myval)) result, CONVERT(binary(6), @myval) HEX
result                                  HEX
--------------------------------------- --------------
256                                     0x040000010101

bu erda 6 baytda 0x04 00 00 01 01 01 va 01 01 bor. Keyin 9999 (HEX, 270F ga teng)

DECLARE @myval decimal (4, 0);  
SET @myval = 9999;  
SELECT CONVERT(decimal(4,0), CONVERT(binary(6), @myval)) result, CONVERT(binary(6), @myval) HEX
result                                  HEX
--------------------------------------- --------------
9999                                    0x040000010F27

6 baytda 0x04 00 00 01 0F 27 va 27 0F ni bosing. (o'ngdan chapga o'qish)

4
qo'shib qo'ydi
Kechirasiz, men avvalgiday bo'lgani kabi aniq bo'lmaganman. Menga qarab turganingiz uchun tashakkur!
qo'shib qo'ydi muallif Brian Pressler, manba
Bu mening savolimga javob bermaydi. Men kasrni saqlash uchun 6 blyudani talab qilishini bilaman (4,0), men bu savolda aytgan edim. Mening savolim kasr (p, s) uchun minimal ikkilik hajmni aniqlashning umumiy qoidasi. Hech bo'lmasa, qanday qilib ikkilik rejimida o'nlik qiymatlarni mustahkamlash mumkin?
qo'shib qo'ydi muallif Brian Pressler, manba
Kechirasiz, lekin men javob berganimda ma'lumotlarning marshaling haqidagi savoliga hech qanday ma'lumot berilmadi. Siz "Nima sodir bo'lyapti?" Deb so'radingiz. va "qancha bayta kerakligini qanday bilaman?". Bu erda qanday ishlashini aytib berishga harakat qildim. O'zgargan savolingizga qarayman va keyinroq yordam berishga harakat qilaman
qo'shib qo'ydi muallif Roman Marusyk, manba
3
qo'shib qo'ydi
@BrianPressler, oddiy, ammo nozik yo'l decimal ni qattiq kodli ikkilik o'rniga varbinary (ba'zi bir katta qiymat) ga aylantirishdir. Server zarur bo'lgan qancha baytni hal qiladi. Bu oson usul sizda eng kichik ikkilik qiymatni kafolatlamaydi. Serverning har xil turdagi qiymatlarni (var) ikkilikiga qanday o'zgartirganligi haqida hech qanday hujjatlar yo'q deb o'ylamayman.) . Siz allaqachon ushbu ayirboshlash emas diskda saqlangan bir xil qiymat hosil qilgandir. Ma'lumotlaringiz va uning hajmiga qarab, yuborishdan oldin uni gzip kabi bir narsa bilan siqib chiqishni ko'rib chiqing
qo'shib qo'ydi muallif Vladimir Baranov, manba
@BrianPressler, har doim ham etarli varbinary dan foydalanishingiz mumkin, ya'ni 50 ni kasr qiymatining ikkilik vakili kesilmasligini kafolatlashingiz mumkin. Faqat varbinary kodiga bir necha decimal qiymatlarini o'zingiz o'zgartiring va siz qiymatning aniqligi va miqyosini ta'riflaydigan 3 baytli oldik kabi ko'rinadi. Amalda siz ishlab chiqarishda bunga tayanmasligingiz kerak va decimal qiymatlari decimal () ning ustuniga saqlanishi kerak (var) ikkilik Yozuv turi.
qo'shib qo'ydi muallif Vladimir Baranov, manba
@BrianPressler, ma'lumotni marshal qilish uchun decimal kodini STR dan foydalanib, to'g'ri uzunlikdagi varchar ga o'zgartirdim. Agar siz faqat decimal (4,0) dan foydalansangiz, oddiy char (4) 5 ikkilik baytdan yaxshiroqdir. Yoki ondalık qiymatining o'zaro ikkilanishini kashf qilib, qiymatlarni to'plash va ochish uchun ba'zi maxsus kodlarni yozishingiz mumkin. Bunday holatda ikkilik qiymatlarning formatini bilib olasiz va hujjatsiz qayta ishlab chiqilgan taxminlarga tayanmaysiz va SQL Server versiyasi o'zgarganda kutilmaganda o'zgarmaydi.
qo'shib qo'ydi muallif Vladimir Baranov, manba
Jo'natishga aylantirish past darajali aniqlikka ega bo'lgan raqamlar uchun yaxshi ishlaydi, ammo boshqacha tarzda samarali emas. Turli turdagi ma'lumotlarni ko'p marshallashtirishim kerak, shuning uchun konvertatsiya imkon qadar tez va ixcham bo'lishi kerak. Qaytish funktsiyasi bunga erishish kabi ko'rinadi, ammo hujjatning o'nli qiymatlar uchun ovozi porloqligini yoqtirmaydi. Hali ham eng yaxshi yondashuv nima ekanligiga amin emas.
qo'shib qo'ydi muallif Brian Pressler, manba
Xizmat brokerining xabariga ba'zi bir ikkilik ma'lumotlarni olib borayapman, shuning uchun qadriyatlarni iloji boricha kamroq ushlab turishim kerak. Bu qoida bo'ladimi ... Ikki tomonlama konvertatsiya qilish uchun har doim 3 bayta oldik bormi? Buning manbai bormi?
qo'shib qo'ydi muallif Brian Pressler, manba
Men ushbu yozuvni ko'rdim va uning yuqorisida u maqolada aynan nima qilish kerakligi haqida gap ketadi. Ammo, bu ikkilik ma'lumotni boshqa serverda testdan o'tkazmasdan saqlash uchun foydalanmayotgan ekanman, men buni bajarish uchun yaxshi deb o'ylayman. Ammo qanday qilib o'nli baytni ushrni saqlash kerakligini bilsam bo'ladi?
qo'shib qo'ydi muallif Brian Pressler, manba
Muammo shundaki, u hujjatsiz. Tajriba prefiksni ko'rsatadi va hujjatlashtirilgan ichki xotira ma'lumotlarining uzunligi nima uchun uzoqroq bo'lganligi haqidagi asl savolni ochib beradi. Agar ushbu tajriba asosida kod yozsangiz, Microsoft hujjatida format o'zgarishi mumkinligi haqida ogohlantirdi, bu esa uzunligi o'zgarishi mumkinligini bildiradi.
qo'shib qo'ydi muallif Tim, manba

You should not rely on a bit pattern unless you can control its format!

Agar sizni to'g'ri tushunsam, katta hajmdagi ma'lumotlarni uzatishingizni xohlaysiz va buni imkon qadar tez va xavfsiz bajarishni xohlaysiz.

Ishonchli bit naqshini olishni biladigan yagona usul, stringga aniqlik kiritish dir FOR xml (va 2016 bilan boshlangan for JSON ), turdagi xavfsizlikni ta'minlash uchun yaxshi belgilangan string formatlarini ishlatadi.

Quyidagi misol, har xil turdagi xml hosil qiladi, buni NVARCHAR (MAX) uchun yuboradi va bu oraliq mag'lubiyatni ikkilikka o'tkazadi. Ushbu ikkitomonlama oddiy unicode va deyarli barcha tizimlar tomonidan to'g'ri interpretatsiya qilinadi.

Faqatgina bu bit naqshini ishonchli ravishda qayta tiklash va yoziladigan natijalarga qayta o'qilishi mumkinligini ko'rsatish uchun, o'qish jarayonini ham ko'rsataman:

DECLARE @Source NVARCHAR(MAX)=
CAST(
(
    SELECT 'Some varchar string' AS SimpleString
          ,N'Some nvarchar string with foreign characters: слов в тексте' AS WideString
          ,CAST(100.0/3.0 AS DECIMAL(10,4)) AS Decimal_10_4
          ,CAST(100.0/3.0 AS FLOAT(12)) AS Float_12
          ,CAST(100.0/3.0 AS FLOAT(53)) AS Float_53
          ,GETDATE() AS SimpleDateTime
          ,CAST(GETDATE() AS DATETIME2) AS ExtDateTime
    FOR xml RAW,ELEMENTS
) AS NVARCHAR(MAX));

SELECT @Source;

- bu qidiruv XML

/*

  Some varchar string
  Some nvarchar string with foreign characters: слов в тексте
  33.3333
  3.3333332e+001
  3.333333300000000e+001
  2017-03-20T09:28:10.873
  2017-03-20T09:28:10.8730000

*/

--Shuning uchun uni VARBINARY ga yuborishimiz mumkin

DECLARE @binaryToMarshal VARBINARY(MAX)=CAST(@Source AS VARBINARY(MAX));

--Qolgan narsa turdagi xavfsiz! o'qish tarafidir:

DECLARE @Target NVARCHAR(MAX)=CAST(@binaryToMarshal AS NVARCHAR(MAX));

DECLARE @ReCastes XML=CAST(@Target AS XML);

SELECT @ReCastes.value('(/row/SimpleString)[1]','varchar(max)') AS SimpleString
      ,@ReCastes.value('(/row/WideString)[1]','nvarchar(max)') AS WideString
      ,@ReCastes.value('(/row/Decimal_10_4)[1]','decimal(10,4)') AS Decimal_10_4
      ,@ReCastes.value('(/row/Float_12)[1]','float(12)') AS Float_12
      ,@ReCastes.value('(/row/Float_53)[1]','float(53)') AS Float_53
      ,@ReCastes.value('(/row/SimpleDateTime)[1]','datetime') AS SimpleDateTime
      ,@ReCastes.value('(/row/ExtDateTime)[1]','datetime2') AS ExtDateTime

Maslahat 1

Inline diagramma yaratish uchun xml RAW, ELEMENTS, XMLSCHEMA uchun foydalanishingiz mumkin. Ushbu sxema maqsadli tizim uchun barcha kerakli tafsilotlarni o'rganish imkonini beradi.

Maslahat 2

Baytlarni saqlashni xohlaganingizdek, eng kam element nomlari va JSON dan foydalanishingiz mumkin.

Va - agar siz maxsus belgilarni kiritmasangiz - CAST ... AS VARCHAR (MAX) dan foydalaning. Bunday holatda siz biron bir kodni talab qilmaguningizcha har qanday tizimda o'qilishi kerak bo'lgan utf-16 but 1-byte-codes * (kengaytirilgan ASCII) * sahifa, harmanlama, nima bo'lishidan qat'i nazar ...

2
qo'shib qo'ydi

SQLda ikkilik ma'lumotlar bazasi manba ma'lumotlariga bog'liq bo'lgan kodlash sxemasini aniq ishlatadi. Quyidagilarni boshqarishga urinib ko'ring va qanday natijalarga erishganingizni ko'ring:

SELECT CAST(256 AS Binary(8)), 
       CAST(CAST(256 as decimal(4,0)) AS Binary(8)),  
       CAST(CAST(256 as decimal(9,6)) AS Binary(8)), 
       CAST(CAST(256 as float(2)) AS Binary(8))

Quyidagi natijalarga e'tibor bering:

0x0000000000000100  0x0400000100010000  0x090600010040420F  0x0000000043800000

Bu shuni anglatadiki, kasrli ikkilikka aylantirilganda ikkilik kvadrat qancha vaqt va qancha kasrli joyni ko'rsatadigan kodlashni o'z ichiga oladi. Bundan tashqari, siz ularga qarab turli xil ikkilik vakillikni qabul qilishingiz mumkin. Bundan tashqari, tamsayılar va suzuvchi narsalar boshqacha tarzda kodlanganligi aniq. O'ylaymanki, bu sizning maqsadingiz nima uchun kesiladi?

Nima uchun sizning jadvalingizni o'zgartirishi mumkin bo'lsa, unda siz sql_variant sifatida ustunni belgilashingiz mumkin:

declare @test sql_variant


SET @test = 'I''m a string'


SELECT  @test As Col,
         SQL_VARIANT_PROPERTY(@test,'BaseType') AS 'Base Type',  
         SQL_VARIANT_PROPERTY(@test,'Precision') AS 'Precision',  
         SQL_VARIANT_PROPERTY(@test,'Scale') AS 'Scale'  

Natijada

Col             Base Type   Precision   Scale
I'm a string    varchar     0           0

Endi dekals bilan harakat qiling:

SET @test = 1.2

SELECT  @test As Col,
         SQL_VARIANT_PROPERTY(@test,'BaseType') AS 'Base Type',  
         SQL_VARIANT_PROPERTY(@test,'Precision') AS 'Precision',  
         SQL_VARIANT_PROPERTY(@test,'Scale') AS 'Scale'  

Natijada

Col Base Type   Precision   Scale
1.2 numeric     2           1
2
qo'shib qo'ydi

according to MSDN: https://msdn.microsoft.com/en-us//library/ms188362.aspx

varbinary [(n | max)]:   Argumentlar uzunlikdagi ikkilik ma'lumotlar. n qiymati 1 dan 8000 gacha bo'lishi mumkin. Maksimal maksimal xotira hajmi 2 ^ 31-1 bayt ekanligini bildiradi. Saqlash hajmi - kiritilgan ma'lumotlarning haqiqiy uzunligi + 2 bayt . Kiritilgan ma'lumotlar 0 bayt uzunlikda bo'lishi mumkin. Varbinary uchun ANSI SQL sinonimi ikkilik turlicha.

Siz e'lon qilganda

DECLARE @myval decimal (4, 0);  

siz 9999 dan 9999 gacha saqlaydigan to'rtta raqamli kasr raqamini aniqlaysiz. uni/varbinary'dan aylantirish uchun/varbinary turida +2 qo'shimcha baytni ishlatishingiz kerak:

DECLARE @myval decimal (4, 0);  
SET @myval = 9999;  
SELECT CONVERT(decimal(4,0), CONVERT(varbinary(6), @myval)) result

decimal precision parametrini ikki tomonlama raqamlarning maksimal umumiy sonini belgilaydi va ikkilik maksimal qiymatni belgilaydi.

2
qo'shib qo'ydi

(faqat sizning holatingizda 5 ta baytni olishi mumkin) qiymatini emas, balki sizning ma'lumotingiz haqidagi ma'lumotni ham keltirib chiqarsangiz, sizning holatlaringizga mos keladigan qo'shimcha baytga ehtiyoj bor.

Agar manba turini kasr (4,0) dan kasr (5,0) ga o'zgartirsangiz, siz turli varbinary natija oladi.

Hammasi bilan varbinary bilan o'ynashni tavsiya qilaman :)

1
qo'shib qo'ydi