SELECT BonusPercent FROM Employees ORDER BY BonusPercent DESC

Если необходимо поменять логику сортировки NULL значений, то используйте выражения, например:


SELECT BonusPercent FROM Employees ORDER BY ISNULL(BonusPercent,100)

В ORACLE для этой цели предусмотрены 2 опции NULLS FIRST и NULLS LAST (применяется по умолчанию). Например:


SELECT BonusPercent FROM Employees ORDER BY BonusPercent DESC NULLS LAST

Обращайте на это внимание при переходе на ту или иную БД.

TOP – возврат указанного числа записей

Вырезка из MSDN. TOP – ограничивает число строк, возвращаемых в результирующем наборе запроса до заданного числа или процентного значения. Если предложение TOP используется совместно с предложением ORDER BY, то результирующий набор ограничен первыми N строками отсортированного результата. В противном случае возвращаются первые N строк в неопределенном порядке.


Обычно данное выражение используется с предложением ORDER BY и мы уже смотрели примеры, когда нужно было вернуть N-первых строк из результирующего набора.

Без ORDER BY обычно данное предложение применяется, когда нужно просто посмотреть на неизвестную нам таблицу, в которой может быть очень много записей, в этом случае мы можем, для примера, попросить вернуть нам только первые 10 строк, но для наглядности мы скажем только 2:


SELECT TOP 2

*

FROM Employees

Так же можно указать слово PERCENT, для того чтобы вернулось соответствуй процент строк из результирующего набора:


SELECT TOP 25 PERCENT

*

FROM Employees

На моей практике чаше применяется именно выборка по количеству строк.

Так же с TOP можно использовать опцию WITH TIES, которая поможет вернуть все строки в случае неоднозначной сортировки, т.е. это предложение вернет все строки, которые равны по составу строкам, которые попадают в выборку TOP N, в итоге строк может быть выбрано больше чем N. Давайте для демонстрации добавим еще одного «Программиста» с окладом 1500:


INSERT Employees(ID,Name,Email,PositionID,DepartmentID,ManagerID,Salary)

VALUES(1004,N'Николаев Н.Н.','[email protected]',3,3,1003,1500)

и введем еще одного сотрудника без указания должности и отдела с окладом 2000:


INSERT Employees(ID,Name,Email,PositionID,DepartmentID,ManagerID,Salary)

VALUES(1005,N'Александров А.А.','[email protected]',NULL,NULL,1000,2000)

Теперь давайте выберем при помощи опции WITH TIES всех сотрудников, у которых оклад совпадает с окладами 3-х сотрудников, с самым маленьким окладом (надеюсь дальше будет понятно, к чему я клоню):


SELECT TOP 3 WITH TIES

ID,Name,Salary

FROM Employees

ORDER BY Salary

Здесь хоть и указано TOP 3, но запрос вернул 4 записи, т.к. значение Salary которое вернуло TOP 3 (1500 и 2000) оказалось у 4-х сотрудников. Наглядно это работает примерно следующим образом:

SELECT BonusPercent FROM Employees ORDER BY BonusPercent DESC - student2.ru

На заметку.
В разных БД TOP реализуется разными способами, в MySQL для этого есть предложение LIMIT, в котором дополнительно можно задать начальное смещение.

В ORACLE 12c, тоже ввели свой аналог совмещающий функциональность TOP и LIMIT – ищите по словам «ORACLE OFFSET FETCH». До версии 12c для этой цели обычно использовался псевдостолбец ROWNUM.


А что же будет если применить одновременно предложения DISTINCT и TOP? На такие вопросы легко ответить, проводя эксперименты. В общем, не бойтесь и не ленитесь экспериментировать, т.к. большая часть познается именно на практике. Порядок слов в операторе SELECT следующий, первым идет DISTINCT, а после него идет TOP, т.е. если рассуждать логически и читать слева-направо, то первым применится отброс дубликатов, а потом уже по этому набору будет сделан TOP. Что-ж проверим и убедимся, что так и есть:


SELECT DISTINCT TOP 2

Salary

FROM Employees

ORDER BY Salary

Salary


Т.е. в результате мы получили 2 самые маленькие зарплаты из всех. Конечно может быть случай что ЗП для каких-то сотрудников может быть не указанной (NULL), т.к. схема нам это позволяет. Поэтому в зависимости от задачи принимаем решение либо обработать NULL значения в предложении ORDER BY, либо просто отбросить все записи, у которых Salary равна NULL, а для этого переходим к изучению предложения WHERE.

WHERE – условие выборки строк


Данное предложение служит для фильтрации записей по заданному условию. Например, выберем всех сотрудников работающих в «ИТ» отделе (его ID=3):


SELECT ID,LastName,FirstName,Salary

FROM Employees

WHERE DepartmentID=3 -- ИТ

ORDER BY LastName,FirstName

ID LastName FirstName Salary
NULL NULL
Андреев Андрей
Петров Петр


Предложение WHERE пишется до команды ORDER BY.

Порядок применения команд к исходному набору Employees следующий:

1. WHERE – если указано, то первым делом из всего набора Employees идет отбор только удовлетворяющих условию записей

2. DISTINCT – если указано, то отбрасываются все дубликаты

3. ORDER BY – если указано, то делается сортировка результата

4. TOP – если указано, то из отсортированного результата возвращается только указанное число записей

Рассмотрим для наглядности пример:


SELECT DISTINCT TOP 1

Salary

FROM Employees

WHERE DepartmentID=3

ORDER BY Salary

Наглядно это будет выглядеть следующим образом:

SELECT BonusPercent FROM Employees ORDER BY BonusPercent DESC - student2.ru

Стоит отметить, что проверка на NULL делается не знаком равенства, а при помощи операторов IS NULL и IS NOT NULL. Просто запомните, что на NULL при помощи оператора «=» (знак равенства) сравнивать нельзя, т.к. результат выражения будет так же равен NULL.

Например, выберем всех сотрудников, у которых не указан отдел (т.е. DepartmentID IS NULL):


SELECT ID,Name

FROM Employees

WHERE DepartmentID IS NULL

ID Name
Александров А.А.


Теперь для примера посчитаем бонус для всех сотрудников у которых указано значение BonusPercent (т.е. BonusPercent IS NOT NULL):


SELECT ID,Name,Salary/100*BonusPercent AS Bonus

FROM Employees

WHERE BonusPercent IS NOT NULL

Да, кстати, если подумать, то значение BonusPercent может равняться нулю (0), а так же значение может быть внесено со знаком минус, ведь мы не накладывали на данное поле никаких ограничений.

Хорошо, рассказав о проблеме, нам пока сказали считать, что если (BonusPercent<=0 или BonusPercent IS NULL), то это означает что у сотрудника так же нет бонуса. Для начала, как нам сказали, так и сделаем, реализуем это при помощи логического оператора OR и NOT:


SELECT ID,Name,Salary/100*BonusPercent AS Bonus

FROM Employees

WHERE NOT(BonusPercent<=0 OR BonusPercent IS NULL)

Т.е. здесь мы начали изучать булевы операторы. Выражение в скобках «(BonusPercent<=0 OR BonusPercent IS NULL)» проверяет на то что у сотрудника нет бонуса, а NOT инвертирует это значение, т.е. говорит «верни всех сотрудников которые не сотрудники у которых нет бонуса».

Так же данное выражение можно переписать и сразу сказав сразу «верни всех сотрудников, у которых есть бонус» выразив это выражением (BonusPercent>0 и BonusPercent IS NOT NULL):


SELECT ID,Name,Salary/100*BonusPercent AS Bonus

FROM Employees

WHERE BonusPercent>0 AND BonusPercent IS NOT NULL

Также в блоке WHERE можно делать проверку разного рода выражений с применением арифметических операторов и функций. Например, аналогичную проверку можно сделать, использовав выражение с функцией ISNULL:


SELECT ID,Name,Salary/100*BonusPercent AS Bonus

FROM Employees

WHERE ISNULL(BonusPercent,0)>0

Булевы операторы и простые операторы сравнения


Да, без математики здесь не обойтись, поэтому сделаем небольшой экскурс по булевым и простым операторам сравнения.

Булевых операторов в языке SQL всего 3 – AND, OR и NOT:

AND логическое И. Ставится между двумя условиями (условие1 AND условие2). Чтобы выражение вернуло True, нужно, чтобы истинными были оба условия
OR логическое ИЛИ. Ставится между двумя условиями (условие1 OR условие2). Чтобы выражение вернуло True, достаточно, чтобы истинным было только одно условие
NOT инвертирует условие/логическое_выражение. Накладывается на другое выражение (NOT логическое_выражение) и возвращает True, если логическое_выражение = False и возвращает False, если логическое_выражение = True


Для каждого булева оператора можно привести таблицы истинности где дополнительно показано какой будет результат, когда условия могут быть равны NULL:

SELECT BonusPercent FROM Employees ORDER BY BonusPercent DESC - student2.ru

Есть следующие простые операторы сравнения, которые используются для формирования условий:

Условие Значение
= Равно
< Меньше
> Больше
<= Меньше или равно
>= Больше или равно
<> != Не равно


Плюс имеются 2 оператора для проверки значения/выражения на NULL:

IS NULL Проверка на равенство NULL
IS NOT NULL Проверка на неравенство NULL


Приоритет: 1) Все операторы сравнения; 2) NOT; 3) AND; 4) OR.

При построении сложных логических выражений используются круглые скобки:


((условие1 AND условие2) OR NOT(условие3 AND условие4 AND условие5)) OR (…)

Так же при помощи использования круглых скобок, можно изменить стандартную последовательность вычислений.

Здесь я постарался дать представление о булевой алгебре в достаточном для работы объеме. Как видите, чтобы писать условия посложнее без логики уже не обойтись, но ее здесь немного (AND, OR и NOT) и придумывали ее люди, так что все достаточно логично.

Идем к завершению второй части


Как видите даже про базовый синтаксис оператора SELECT можно говорить очень долго, но, чтобы остаться в рамках статьи, напоследок я покажу дополнительные логических операторы – BETWEEN, IN и LIKE.

BETWEEN – проверка на вхождение в диапазон


Этот оператор имеет следующий вид:


проверяемое_значение [NOT] BETWEEN начальное_ значение AND конечное_ значение

В роли значений могут выступать выражения.

Разберем на примере:


SELECT ID,Name,Salary

FROM Employees

WHERE Salary BETWEEN 2000 AND 3000 -- у кого ЗП в диапазоне 2000-3000

ID Name Salary
Сидоров С.С.
Андреев А.А.
Александров А.А.


Собственно, BETWEEN это упрощенная запись вида:


SELECT ID,Name,Salary

FROM Employees

WHERE Salary>=2000 AND Salary<=3000 -- все у кого ЗП в диапозоне 2000-3000

Перед словом BETWEEN может использоваться слово NOT, которое будет осуществлять проверку значения на не вхождение в указанный диапазон:


SELECT ID,Name,Salary

FROM Employees

WHERE Salary NOT BETWEEN 2000 AND 3000 -- аналогично выражению NOT(Salary>=2000 AND Salary<=3000)

Соответственно, в случае использования BETWEEN, IN, LIKE вы можете так же объединять их с другими условиями при помощи AND и OR:


SELECT ID,Name,Salary

FROM Employees

WHERE Salary BETWEEN 2000 AND 3000 -- у кого ЗП в диапазоне 2000-3000

AND DepartmentID=3 -- учитывать сотрудников только отдела 3

IN – проверка на вхождение в перечень значений


Этот оператор имеет следующий вид:


проверяемое_значение [NOT] IN (значение1, значение2, …)

Думаю, проще показать на примере:


SELECT ID,Name,Salary

FROM Employees

WHERE PositionID IN(3,4) -- у кого должность равна 3 или 4

ID Name Salary
Петров П.П.
Андреев А.А.
Николаев Н.Н.


Т.е. по сути это аналогично следующему выражению:


SELECT ID,Name,Salary

FROM Employees

WHERE PositionID=3 OR PositionID=4 -- у кого должность равна 3 или 4

В случае NOT это будет аналогично (получим всех кроме тех, кто из отдела 3 и 4):


SELECT ID,Name,Salary

FROM Employees

WHERE PositionID NOT IN(3,4) -- аналогично выражению NOT(PositionID=3 OR PositionID=4)

Так же запрос с NOT IN можно выразить и через AND:


SELECT ID,Name,Salary

FROM Employees

WHERE PositionID<>3 AND PositionID<>4 -- равносильно PositionID NOT IN(3,4)

Учтите, что искать NULL значения при помощи конструкции IN не получится, т.к. проверка NULL=NULL вернет так же NULL, а не True:


SELECT ID,Name,DepartmentID

FROM Employees

WHERE DepartmentID IN(1,2,NULL) -- NULL записи не войдут в результат

В этом случае разбивайте проверку на несколько условий:


SELECT ID,Name,DepartmentID

FROM Employees

WHERE DepartmentID IN(1,2) -- 1 или 2

OR DepartmentID IS NULL -- или NULL

Или же можно написать что-то вроде:


SELECT ID,Name,DepartmentID

FROM Employees

WHERE ISNULL(DepartmentID,-1) IN(1,2,-1) -- если вы уверены, что в нет и не будет департамента с ID=-1

Думаю, первый вариант, в данном случае будет более правильным и надежным. Ну ладно, это всего лишь пример, для демонстрации того какие еще конструкции можно строить.

Так же стоит упомянуть еще более коварную ошибку, связанную с NULL, которую можно допустить при использовании конструкции NOT IN. Для примера, давайте попробуем выбрать всех сотрудников, кроме тех, у которых отдел равен 1 или у которых отдел вообще не указан, т.е. равен NULL. В качестве решения напрашивается вариант:


SELECT ID,Name,DepartmentID

FROM Employees

WHERE DepartmentID NOT IN(1,NULL)

Но выполнив запрос, мы не получим ни одной строки, хотя мы ожидали увидеть следующее:

ID Name DepartmentID
Петров П.П.
Сидоров С.С.
Андреев А.А.
Николаев Н.Н.


Опять же шутку здесь сыграло NULL указанное в списке значений.

Разберем почему в данном случае возникла логическая ошибка. Разложим запрос при помощи AND:


SELECT ID,Name,DepartmentID

FROM Employees

WHERE DepartmentID<>1

AND DepartmentID<>NULL -- проблема из-за этой проверки на NULL - это условие всегда вернет NULL

Правое условие (DepartmentID<>NULL) нам всегда здесь даст неопределенность, т.е. NULL. Теперь вспомним таблицу истинности для оператора AND, где (TRUE AND NULL) дает NULL. Т.е. при выполнении левого условия (DepartmentID<>1) из-за неопределенного правого условия в результате мы получим неопределенное значение всего выражения (DepartmentID<>1 AND DepartmentID<>NULL), поэтому строка не войдет в результат.

Переписать условие правильно можно следующим образом:


SELECT ID,Name,DepartmentID

FROM Employees

WHERE DepartmentID NOT IN(1) -- или в данном случае просто DepartmentID<>1

AND DepartmentID IS NOT NULL -- и отдельно проверяем на NOT NULL

IN еще можно использовать с подзапросами, но к такой форме мы вернемся, уже в последующих частях данного учебника.

LIKE – проверка строки по шаблону


Про данный оператор я расскажу только в самом простом виде, который является стандартом и поддерживается большинством диалектов языка SQL. Даже в таком виде при помощи него можно решить много задач, которые требуют выполнить проверку по содержимому строки.

Этот оператор имеет следующий вид:


проверяемая_строка [NOT] LIKE строка_шаблон [ESCAPE отменяющий_символ]

В «строке_шаблон» могут применятся следующие специальные символы:

1. Знак подчеркивания «_» — говорит, что на его месте может стоять любой единичный символ

2. Знак процента «%» — говорит, что на его месте может стоять сколько угодно символов, в том числе и ни одного


Рассмотрим примеры с символом «%» (на практике, кстати он чаще применяется):


SELECT ID,Name

FROM Employees

WHERE Name LIKE 'Пет%' -- у кого имя начинается с букв "Пет"

SELECT ID,LastName

FROM Employees

WHERE LastName LIKE '%ов' -- у кого фамилия оканчивается на "ов"

SELECT ID,LastName

FROM Employees

WHERE LastName LIKE '%ре%' -- у кого фамилия содержит сочетание "ре"

Рассмотрим примеры с символом «_»:


SELECT ID,LastName

FROM Employees

WHERE LastName LIKE '_етров' -- у кого фамилия состоит из любого первого символа и последующих букв "етров"

SELECT ID,LastName

FROM Employees

WHERE LastName LIKE '____ов' -- у кого фамилия состоит из четырех любых символов и последующих букв "ов"

При помощи ESCAPE можно задать отменяющий символ, который отменяет проверяющее действие специальных символов «_» и «%». Данное предложение используется, когда в строке нужно непосредственно проверить наличие знака процента или знака подчеркивания.

Для демонстрации ESCAPE давайте занесем в одну запись мусор:


UPDATE Employees

SET

FirstName='Это_мусор, содержащий %'

WHERE ID=1005

И посмотрим, что вернут следующие запросы:


SELECT *

FROM Employees

WHERE FirstName LIKE '%!%%' ESCAPE '!' -- строка содержит знак "%"

SELECT *

FROM Employees

WHERE FirstName LIKE '%!_%' ESCAPE '!' -- строка содержит знак "_"

В случае, если требуется проверить строку на полное совпадение, то вместо LIKE лучше использовать просто знак «=»:


SELECT *

FROM Employees

WHERE FirstName='Петр'

На заметку.
В MS SQL в шаблоне оператора LIKE так же можно задать поиск по регулярным выражениям, почитайте о нем в интернете, в том случае, если вам станет недостаточно стандартных возможностей данного оператора.

В ORACLE для поиска по регулярным выражениям применяется функция REGEXP_LIKE.

Немного о строках


В случае проверки строки на наличие Unicode символов, нужно будет ставить перед кавычками символ N, т.е. N'…'. Но так как у нас в таблице все символьные поля в формате Unicode (тип nvarchar), то для этих полей можно всегда использовать такой формат. Пример:


SELECT ID,Name

FROM Employees

WHERE Name LIKE N'Пет%'

SELECT ID,LastName

FROM Employees

WHERE LastName=N'Петров'

Если делать правильно, при сравнении с полем типа varchar (ASCII) нужно стараться использовать проверки с использованием '…', а при сравнении поля с типом nvarchar (Unicode) нужно стараться использовать проверки с использованием N'…'. Это делается для того, чтобы избежать в процессе выполнения запроса неявных преобразований типов. То же самое правило используем при вставке (INSERT) значений в поле или их обновлении (UPDATE).

При сравнении строк стоит учесть момент, что в зависимости от настройки БД (collation), сравнение строк может быть, как регистро-независимым (когда 'Петров'='ПЕТРОВ'), так и регистро-зависимым (когда 'Петров'<>'ПЕТРОВ').
В случае регистро-зависимой настройки, если требуется сделать поиск без учета регистра, то можно, например, сделать предварительное преобразование правого и левого выражения в один регистр – верхний или нижний:


SELECT ID,Name

FROM Employees

WHERE UPPER(Name) LIKE UPPER(N'Пет%') -- или LOWER(Name) LIKE LOWER(N'Пет%')

SELECT ID,LastName

FROM Employees

WHERE UPPER(LastName)=UPPER(N'Петров') -- или LOWER(LastName)=LOWER(N'Петров')

Немного о датах


При проверке на дату, вы можете использовать, как и со строками одинарные кавычки '…'.

Вне зависимости от региональных настроек в MS SQL можно использовать следующий синтаксис дат 'YYYYMMDD' (год, месяц, день слитно без пробелов). Такой формат даты MS SQL поймет всегда:


SELECT ID,Name,Birthday

FROM Employees

WHERE Birthday BETWEEN '19800101' AND '19891231' -- сотрудники 80-х годов

ORDER BY Birthday

В некоторых случаях, дату удобнее задавать при помощи функции DATEFROMPARTS:


SELECT ID,Name,Birthday

FROM Employees

WHERE Birthday BETWEEN DATEFROMPARTS(1980,1,1) AND DATEFROMPARTS(1989,12,31)

ORDER BY Birthday

Так же есть аналогичная функция DATETIMEFROMPARTS, которая служит для задания Даты и Времени (для типа datetime).

Еще вы можете использовать функцию CONVERT, если требуется преобразовать строку в значение типа date или datetime:


SELECT

CONVERT(date,'12.03.2015',104),

CONVERT(datetime,'2014-11-30 17:20:15',120)

Значения 104 и 120, указывают какой формат даты используется в строке. Описание всех допустимых форматов вы можете найти в библиотеке MSDN задав в поиске «MS SQL CONVERT».

Функций для работы с датами в MS SQL очень много, ищите «ms sql функции для работы с датами».

Примечание. Во всех диалектах языка SQL свой набор функций по работе с датами и применяется свой подход по работе с ними.

Немного о числах и их преобразованиях


Информация этого раздела наверно больше будет полезна ИТ-специалистам. Если вы таковым не являетесь, а ваша цель просто научится писать запросы для получения из БД необходимой вам информации, то такие тонкости вам возможно и не понадобятся, но в любом случае можете бегло пройтись по тексту и взять что-то на заметку, т.к. если вы взялись за изучение SQL, то вы уже приобщаетесь к ИТ.

В отличие от функции преобразования CAST, в функции CONVERT можно задать третий параметр, который отвечает за стиль преобразования (формат). Для разных типов данных может использоваться свой набор стилей, которые могут повлиять на возвращаемый результат. Использование стилей мы уже затрагивали при рассмотрении преобразования строки функцией CONVERT в типы date и datetime.

Подробней про функции CAST, CONVERT и стили можно почитать в MSDN – «Функции CAST и CONVERT (Transact-SQL)»: msdn.microsoft.com/ru-ru/library/ms187928.aspx

Для упрощения примеров здесь будут использованы инструкции языка Transact-SQL – DECLARE и SET.

Конечно, в случае преобразования целого числа в вещественное (которое я привел вначале данного урока, в целях демонстрации разницы между целочисленным и вещественным делением), знание нюансов преобразования не так критично, т.к. там мы делали преобразование целого числа в вещественное (диапазон которого намного больше диапазона целых):


DECLARE @min_int int SET @min_int=-2147483648

DECLARE @max_int int SET @max_int=2147483647

SELECT

-- (-2147483648)

@min_int,CAST(@min_int AS float),CONVERT(float,@min_int),

-- 2147483647

@max_int,CAST(@max_int AS float),CONVERT(float,@max_int),

-- numeric(16,6)

@min_int/1., -- (-2147483648.000000)

@max_int/1. -- 2147483647.000000

Возможно не стоило указывать способ неявного преобразования, получаемого делением на (1.), т.к. желательно стараться делать явные преобразования, для большего контроля типа получаемого результата. Хотя, в случае, если мы хотим получить результат типа numeric, с указанным количеством цифр после запятой, то мы можем в MS SQL применить трюк с умножением целого значения на (1., 1.0, 1.00 и т.д):


DECLARE @int int SET @int=123

SELECT

@int*1., -- numeric(12, 0) - 0 знаков после запятой

@int*1.0, -- numeric(13, 1) - 1 знак

@int*1.00, -- numeric(14, 2) - 2 знака

-- хотя порой лучше сделать явное преобразование

CAST(@int AS numeric(20, 0)), -- 123

CAST(@int AS numeric(20, 1)), -- 123.0

CAST(@int AS numeric(20, 2)) -- 123.00

В некоторых случаях детали преобразования могут быть действительно важны, т.к. они влияют на правильность полученного результата, например, в случае, когда делается преобразование числового значения в строку (varchar). Рассмотрим примеры по преобразованию значений типа money и float в varchar:


-- поведение при преобразовании money в varchar

DECLARE @money money

SET @money = 1025.123456789 -- произойдет неявное преобразование в 1025.1235, т.к. тип money хранит только 4 цифры после запятой

SELECT

@money, -- 1025.1235

-- по умолчанию CAST и CONVERT ведут себя одинаково (т.е. грубо говоря применяется стиль 0)

CAST(@money as varchar(20)), -- 1025.12

CONVERT(varchar(20), @money), -- 1025.12

CONVERT(varchar(20), @money, 0), -- 1025.12 (стиль 0 - без разделителя тысячных и 2 цифры после запятой (формат по умолчанию))

CONVERT(varchar(20), @money, 1), -- 1,025.12 (стиль 1 - используется разделитель тысячных и 2 цифры после запятой)

CONVERT(varchar(20), @money, 2) -- 1025.1235 (стиль 2 - без разделителя и 4 цифры после запятой)


-- поведение при преобразовании float в varchar

DECLARE @float1 float SET @float1 = 1025.123456789

DECLARE @float2 float SET @float2 = 1231025.123456789

SELECT

@float1, -- 1025.123456789

@float2, -- 1231025.12345679

-- по умолчанию CAST и CONVERT ведут себя одинаково (т.е. грубо говоря применяется стиль 0)

-- стиль 0 - Не более 6 разрядов. По необходимости используется экспоненциальное представление чисел

-- при преобразовании в varchar здесь творятся действительно страшные вещи

CAST(@float1 as varchar(20)), -- 1025.12

CONVERT(varchar(20), @float1), -- 1025.12

CONVERT(varchar(20), @float1, 0), -- 1025.12

CAST(@float2 as varchar(20)), -- 1.23103e+006

CONVERT(varchar(20), @float2), -- 1.23103e+006

CONVERT(varchar(20), @float2, 0), -- 1.23103e+006

-- стиль 1 - Всегда 8 разрядов. Всегда используется экспоненциальное представление чисел.

-- этот стиль для float тоже не очень точен

CONVERT(varchar(20), @float1, 1), -- 1.0251235e+003

CONVERT(varchar(20), @float2, 1), -- 1.2310251e+006

-- стиль 2 - Всегда 16 разрядов. Всегда используется экспоненциальное представление чисел.

-- здесь с точностью уже получше

CONVERT(varchar(30), @float1, 2), -- 1.025123456789000e+003 - OK

CONVERT(varchar(30), @float2, 2) -- 1.231025123456789e+006 - OK

Как видно из примера, плавающие типы float, real в некоторых случаях действительно могут создать большую погрешность, особенно при перегонке в строку и обратно (такое может быть при разного рода интеграциях, когда данные, например, передаются в текстовых файлах из одной системы в другую).

Если нужно явно контролировать точность до определенного знака, более 4-х, то для хранения данных, порой лучше использовать тип decimal/numeric. Если хватает 4-х знаков, то можно использовать и тип money – он примерно соотвествует numeric(20,4).


-- decimal и numeric

DECLARE @money money SET @money = 1025.123456789 -- 1025.1235

DECLARE @float1 float SET @float1 = 1025.123456789

DECLARE @float2 float SET @float2 = 1231025.123456789

DECLARE @numeric numeric(28,9) SET @numeric = 1025.123456789

SELECT

CAST(@numeric as varchar(20)), -- 1025.12345679

CONVERT(varchar(20), @numeric), -- 1025.12345679

CAST(@money as numeric(28,9)), -- 1025.123500000

CAST(@float1 as numeric(28,9)), -- 1025.123456789

CAST(@float2 as numeric(28,9)) -- 1231025.123456789

Примечание.
С версии MS SQL 2008, можно использовать вместо конструкции:


DECLARE @money money

SET @money = 1025.123456789

Более короткий синтаксис инициализации переменных:


DECLARE @money money = 1025.123456789

Заключение второй части


В этой части, я постарался вспомнить и отразить наиболее важные моменты, касающиеся базового синтаксиса. Базовая конструкция – это костяк, без которого нельзя приступать к изучению более сложных конструкций языка SQL.

Наши рекомендации