Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Microsoft SQL Server Новый топик    Ответить
 Форматирование decimal(31, 15) в строку на стороне сервера  [new]
WinterGraveyard
Member

Откуда:
Сообщений: 63
Ситуация: в клиентской программе на дельфи (RAD Studio 2010) нужно отображать значения типа decimal(31, 15), полностью отображая все знаки после запятой (все 15). Как выяснилось, сделать это, передавая на клиент значения как есть (именно decimal(31,15)) невозможно: при отключенной опции EnableBCD значения передаются как double, и ряд значений при заданной маске отображения 15 знаков приобретают в дробной части хвост, которого на самом деле нет - видимо, это связано с потерей точности при прееобразовании в double по IEEE-754. Если включить опцию EnableBCD, значения передаются как binary-coded decimal (собственно, BCD), но обычный BCD (TBCDField) производит округление до формата currency (до 3 знаков), что неприемлемо. А значения типа TFMTBCD (TFMTBСDField), где округление не производится, используемая версия библиотеки доступа к данным (DevArt SDAC) не поддерживает.
Ввиду всего вышеизложенного было принято решение форматировать decimal(31,15) в varchar на сервере. Требования: 15 знаков после запятой, разделитель дробной и целой частей запятая, разделитель разрядов целой части пробел. Сервер - 2005-й.
Была написана вот такая функция:
CREATE FUNCTION [DBO].[UF_FORMAT_DECIMAL](@D DECIMAL(31,15))
RETURNS TABLE
AS RETURN
WITH CTE AS (
	SELECT @D N
)
SELECT F1.Z+LTRIM(F3.D)+F2.A RESULT
FROM CTE E
CROSS APPLY (
	SELECT
		CASE WHEN CONVERT(VARCHAR, E.N) LIKE '-%' THEN '-' ELSE '' END,
		SUBSTRING(
			REPLACE(CONVERT(VARCHAR, E.N), '.', ','),
			CASE WHEN CONVERT(VARCHAR, E.N) LIKE '-%' THEN 2 ELSE 1 END,
			LEN(CONVERT(VARCHAR, N))
		)
) F1(Z,S)
CROSS APPLY (
	SELECT
		SUBSTRING(F1.S, 1, CHARINDEX(',', F1.S)-1),
		SUBSTRING(F1.S, CHARINDEX(',', F1.S), LEN(F1.S))
) F2(B, A)
CROSS APPLY (
	SELECT
		SUBSTRING(F2.B, LEN(F2.B)-17, 3)
		+' '+SUBSTRING(F2.B, LEN(F2.B)-14, 3)
		+' '+SUBSTRING(F2.B, LEN(F2.B)-11, 3)
		+' '+SUBSTRING(F2.B, LEN(F2.B)-8, 3)
		+' '+SUBSTRING(F2.B, LEN(F2.B)-5, 3)
		+' '+SUBSTRING(F2.B, LEN(F2.B)-2, 3)
) F3(D)
GO

Вроде бы работает вполне нормально, но, может, здесь можно что-то подкрутить в плане лаконичности, быстродействия, итд?
15 окт 18, 08:10    [21703842]     Ответить | Цитировать Сообщить модератору
 Re: Форматирование decimal(31, 15) в строку на стороне сервера  [new]
iiyama
Member

Откуда:
Сообщений: 642
WinterGraveyard,
Прошу прощения за небольшой оффтопик(тк без анализа ф-ции),
не проще ли просто 2-мя int-ами отдать (целая и дробная часть) и форматировать все же на клиенте?
15 окт 18, 14:03    [21704184]     Ответить | Цитировать Сообщить модератору
 Re: Форматирование decimal(31, 15) в строку на стороне сервера  [new]
iap
Member

Откуда: Москва
Сообщений: 46954
iiyama
WinterGraveyard,
Прошу прощения за небольшой оффтопик(тк без анализа ф-ции),
не проще ли просто 2-мя int-ами отдать (целая и дробная часть) и форматировать все же на клиенте?
Простым intом не получится - у него не более 10 знаков
15 окт 18, 15:22    [21704273]     Ответить | Цитировать Сообщить модератору
 Re: Форматирование decimal(31, 15) в строку на стороне сервера  [new]
Владислав Колосов
Member

Откуда:
Сообщений: 7403
WinterGraveyard,

я бы написал CLR функцию для таких целей.
15 окт 18, 16:10    [21704321]     Ответить | Цитировать Сообщить модератору
 Re: Форматирование decimal(31, 15) в строку на стороне сервера  [new]
Shakill
Member

Откуда: мск
Сообщений: 1870
WinterGraveyard
форматировать decimal(31,15) в varchar на сервере. Требования: 15 знаков после запятой, разделитель дробной и целой частей запятая, разделитель разрядов целой части пробел. Сервер - 2005-й.


если вдруг окажется сервер 2012 :)
declare @d decimal(31,15) = -1234567890123456.123456789012345
select format(@d, 'n15', 'ru')  
15 окт 18, 16:12    [21704323]     Ответить | Цитировать Сообщить модератору
 Re: Форматирование decimal(31, 15) в строку на стороне сервера  [new]
Massa52
Member

Откуда:
Сообщений: 373
WinterGraveyard,
declare @d decimal(31,15) = -1234567890123456.543210987654321
select cast(@d AS bigint), cast((@d - cast(@d AS bigint)) * 1000000000000000 AS bigint) 
16 окт 18, 04:50    [21704679]     Ответить | Цитировать Сообщить модератору
 Re: Форматирование decimal(31, 15) в строку на стороне сервера  [new]
WinterGraveyard
Member

Откуда:
Сообщений: 63
Massa52
WinterGraveyard,
declare @d decimal(31,15) = -1234567890123456.543210987654321
select cast(@d AS bigint), cast((@d - cast(@d AS bigint)) * 1000000000000000 AS bigint) 

Нет, спасибо, передавать клиенту два значения вместо одного, и потом собирать из них одно в средствах отображения (DevExpress QuantunGrid/FastReports) - это намного больше возни, чем отформатировать значение на сервере (к тому же выборки в контексте данной задачи небольшие, и вряд ли такие преобразования сильно нагрузят сервер).
Владислав Колосов
WinterGraveyard,
я бы написал CLR функцию для таких целей.

Отпадает - по каким-то причинам администраторы не хотят возиться с хостингом CLR-сборок на сервере.
Shakill
если вдруг окажется сервер 2012 :)

Да, спасибо, я в курсе, но есть только то, что есть. И насколько я понимаю вот это
автор
FORMAT relies on the presence of the .NET Framework Common Language Runtime (CLR).

эта функция и является оберткой над дотнетовским форматированием (и использует дотнетовские форматы).
16 окт 18, 08:07    [21704718]     Ответить | Цитировать Сообщить модератору
 Re: Форматирование decimal(31, 15) в строку на стороне сервера  [new]
PizzaPizza
Member

Откуда:
Сообщений: 309
WinterGraveyard,

Практический вопрос: судя по тому, что вы разделяете разряды пробелом, это число требуется просто для отображения? Может проще его передавать строкой в клиент и там уже как строку форматировать? Сдается мне на Дельфи это гораздо короче получится (не уверен).
16 окт 18, 08:34    [21704732]     Ответить | Цитировать Сообщить модератору
 Re: Форматирование decimal(31, 15) в строку на стороне сервера  [new]
Massa52
Member

Откуда:
Сообщений: 373
WinterGraveyard,
Кстати - ваша функция не работает для значения -1234567890123456.543210987654321
16 окт 18, 08:50    [21704742]     Ответить | Цитировать Сообщить модератору
Все форумы / Microsoft SQL Server Ответить