Оценка 45 Оценить ![]() ![]() ![]() ![]() ![]() ![]()
|
Еще недавно в моей памяти отложился анонс SQL Server 2016, которую лично презентовал Сатья Наделла. И вдруг, как снег на голову, стали одна за одной выходить свежие Community Technology Preview (на данный момент самая свежая версия – CTP3.1). По мере знакомства с новой версией, все больше хотелось поделиться впечатлениями…
Далее обзор новых синтаксических фишек SQL Server 2016: JSON, GZIP, DROP IF EXISTS,TRUNCATE TABLE по секциям, новые функции…
CREATE
TABLE dbo.tbl (
a INT, b INT,
CONSTRAINT ck CHECK (a > 1),
INDEX ix CLUSTERED (a)
) |
Если раньше перед удалением объекта приходилось делать проверку:
IF OBJECT_ID(N'dbo.tbl', 'U') ISNOTNULLDROPTABLE dbo.tbl |
То сейчас появился более компактный синтаксис:
DROP
TABLE
IF
EXISTS dbo.tbl |
Добавлена возможность удаление дочерних элементов:
ALTER
TABLE dbo.tbl DROPCOLUMNIFEXISTS b
ALTERTABLE dbo.tbl DROPCONSTRAINTIFEXISTS ck
ALTERTABLE dbo.tbl DROPCONSTRAINTIFEXISTS ix |
Для одного родительского объекта конструкции можно объединять:
ALTER
TABLE dbo.tbl DROPCOLUMNIFEXISTS b,
CONSTRAINTIFEXISTS ck,
CONSTRAINTIFEXISTS ix |
Поддерживается DROP IF EXISTS практически для всех объектов (с полным списком можно ознакомиться тут):
DROP
TABLE
IF
EXISTS #temp
DROPTABLEIFEXISTS ##temp
DROPVIEWIFEXISTS dbo.view1
DROPPROCEDUREIFEXISTS dbo.proc1
DROPDATABASEIFEXISTS db |
В моей практике возникали задачи по расшариванию параметров в рамках пользовательской сессии. Раньше для этого приходилось использовать CONTEXT_INFO, размер которой был ограничен 128 байтами:
DECLARE
@UserID SMALLINT = 1
, @LocaleID INT = 123
DECLARE @ctn VARBINARY(128)
SET @ctn = CAST(@UserID AS BINARY(2)) + CAST(@LocaleID AS BINARY(4))
SET CONTEXT_INFO @ctn
|
Теперь все стало чуточку удобнее за счет новой функции SESSION_CONTEXT, в которой разрешили хранить 256Кб на сессию:
EXEC sys.sp_set_session_context @key = N'UserID', @value = 1
EXEC sys.sp_set_session_context @key = N'LocaleID', @value = 123
SELECT
UserID = SESSION_CONTEXT(N'UserID')
, LocaleID = SESSION_CONTEXT(N'LocaleID') |
По умолчанию, при выполнении DBCC CHECKDB используется количество потоков равное числу логических ядер. Теперь количество потоков можно ограничить, чтобы не снижать производительность сервера в целом:
DBCC CHECKDB(N'AdventureWorks2016CTP3') WITH MAXDOP = 4 |
Аналогичная функциональность добавлена в DBCC CHECKTABLE и DBCC CHECKFILEGROUP:
USE AdventureWorks2016CTP3
GO
DBCC CHECKTABLE('HumanResources.Employee') WITH MAXDOP = 4
DBCC CHECKFILEGROUP(1) WITH MAXDOP = 4 |
Для DBCC CHECKDB сделали некоторые оптимизации при проверке фильтрованных индексов и COMPUTED столбцов с опцией PERSISTED. Еще сократили время проверки для таблиц, которые содержат большое количество секций.
В предыдущих версия функция FORMATMESSAGE могла использовать только ранее добавленные пользовательские сообщения:
EXEC sys.sp_addmessage
@msgnum = 66667,
@severity = 16,
@msgtext = N'param1: %s, param2: %s'
DECLARE @msg NVARCHAR(2048) = FORMATMESSAGE(66667, N'one', N'two')
SELECT @msg |
теперь появилась возможность указывать произвольную маску:
SELECT FORMATMESSAGE('val1: %+i, val2: %+d', 5, -6) |
Многие рутинные операции квотирования или конкатенации строк можно сделать элегантнее:
SELECT FORMATMESSAGE('SELECT * FROM [%s].[%s]', SCHEMA_NAME([schema_id]), name)
FROM sys.objects
WHERE [type] = 'U' |
В новой редакции появилась встроенная поддержка GZIP: COMPRESS и DECOMPRESS. При декодировании важно следить за правильныостью типа данных, в который преобразуется
DECLARE @a VARBINARY(MAX) = COMPRESS('test test test')
SELECT
@a
, DECOMPRESS(@a)
, CAST(DECOMPRESS(@a) AS NVARCHAR(MAX)) -- печаль
, CAST(DECOMPRESS(@a) AS VARCHAR(MAX)) |
Кодируем ANSI строку и пытаемся декодировать полученное значение:
----------------------------------------------------------- -------------------------------- ---------------- -----------------
0x1F8B08000000000004002B492D2E5128811100026A5B230E000000 0x7465737420746573742074657374 整瑳琠獥⁴整瑳 test test test |
В SQL Server 2008 для функции DATEDIFF появились новые параметры: MICROSECOND и NANOSECOND, но когда задавался слишком большой диапазон дат:
SELECT DATEDIFF(NANOSECOND, '20000101', '20160101') |
это могло привести к ошибке:
Msg 535, Level 16, State 0, Line 1 The datediff function resulted in an overflow. The number of dateparts separating two date/time instances is too large. Try to use datediff with a less precise datepart. |
SELECT DATEDIFF_BIG(NANOSECOND, '20000101', '20160101') |
SELECT name, current_utc_offset, is_currently_dst
FROM sys.time_zone_info |
в котором можно получить список часовых зон:
name current_utc_offset is_currently_dst
-------------------------- ------------------ ----------------
Dateline Standard Time -12:00 0
UTC-11 -11:00 0
...
Central Standard Time -06:00 0
...
Pacific SA Standard Time -03:00 0
UTC-02 -02:00 0
...
UTC +00:00 0
GMT Standard Time +00:00 0
Greenwich Standard Time +00:00 0
...
Belarus Standard Time +03:00 0
Russian Standard Time +03:00 0
... |
SELECT
CONVERT(DATETIME2, GETDATE()) AT TIME ZONE N'Belarus Standard Time'
----------------------------------
2015-12-02 14:51:02.1366667 +03:00 |
который можно параметризировать:
DECLARE @tz NVARCHAR(256) = N'Belarus Standard Time'
SELECT
GETDATE() AT TIME ZONE @tz
, CONVERT(DATETIME2, GETDATE()) AT TIME ZONE @tz
----------------------------------
2015-12-02 14:51:28.6266667 +03:00 |
В чем польза от такого нововведения? Например, можно вывести сколько времени в других часовых поясах исходя из текущего времени у нас:
SELECT name, CONVERT(DATETIME,
SWITCHOFFSET(SYSUTCDATETIME() AT TIME ZONE name,
DATENAME(TzOffset, SYSDATETIMEOFFSET()))
)
FROM sys.time_zone_info |
---------------------------------- -----------------------
Dateline Standard Time 2015-12-03 02:56:41.940
UTC-11 2015-12-03 01:56:41.940
...
Pacific SA Standard Time 2015-12-02 17:56:41.940
UTC-02 2015-12-02 16:56:41.940
...
UTC 2015-12-02 14:56:41.940
GMT Standard Time 2015-12-02 14:56:41.940
Greenwich Standard Time 2015-12-02 14:56:41.940
Central European Standard Time 2015-12-02 13:56:41.940
... |
Поддержка JSON – это одна из основных фишек SQL Server 2016. Начиная с CTP2.0появилась возможность генерировать JSON по аналогии с XML. Поддерживаются две конструкции FOR JSON AUTO и FOR JSON PATH:
SELECT
TOP (2) name, database_id, source_database_id, create_date
FROM sys.databases
FOR JSON AUTO, ROOT('root') |
{"root":
[
{"name":"master","database_id":1,"create_date":"2003-04-08T09:13:36.390"},
{"name":"tempdb","database_id":2,"create_date":"2015-12-02T11:34:36.080"}
]
} |
SELECT
TOP (2)
name
, [db.id] = database_id
, [db.scr_id] = source_database_id
, [db.date] = create_date
FROM sys.databases
FOR JSON PATH, ROOT
{"root":
[
{
"name":"master",
"db":{"id":1,"date":"2003-04-08T09:13:36.390"}
},
{
"name":"tempdb",
"db":{"id":2,"date":"2015-12-02T11:34:36.080"}
}
]
} |
Чтобы NULL значения при генерации включались в JSON, нужно использовать опцию INCLUDE_NULL_VALUES:
SELECT
TOP (1) name, database_id, source_database_id
FROM sys.databases
FOR JSON AUTO, INCLUDE_NULL_VALUES
[
{
"name":"master",
"database_id":1,
"source_database_id":null
}
] |
Для хранения JSON необходимо использовать NVARCHAR, поскольку отдельного типа данных не предусмотрено:
DECLARE @json NVARCHAR(MAX) = (
SELECT key1 = 1, key2 = GETDATE()
FOR JSON PATH
)
SELECT @json
{"key1":1,"key2":"2015-12-02T15:45:05.530"} |
DECLARE @json NVARCHAR(MAX) = N'
{
"UserID" : 1,
"UserName": "JC Denton",
"IsActive": true,
"RegDate": "2015-12-02"
}';
SELECT * FROM OPENJSON(@json)
key value type
----------- ------------ ----
UserID 1 2
UserName JC Denton 1
IsActive true 3
RegDate 2015-12-02 1 |
Такое поведение можно использовать в качестве «еще одного варианта» сплита строки:
DECLARE @a NVARCHAR(100) = '1,2,3'
SELECT CAST(value AS INT)
FROM OPENJSON(N'[' + @a + N']') |
-----------
1
2
3 |
Если записей несколько:
DECLARE @json NVARCHAR(MAX) = N'
[
{
"UserID" : 1,
"UserName": "JC Denton",
"IsActive": true,
"RegDate": "2015-12-02"
},
{
"UserID" : 2,
"UserName": "Paul Denton",
"IsActive": false,
"RegDate": "2015-11-02"
}
]';
SELECT * FROM OPENJSON(@json) |
то результат будет следующим:

В нормальном виде данные можно будет получить так:
DECLARE @json NVARCHAR(MAX) = N'
[
{
"UserID" : 1,
"UserName": "JC Denton",
"IsActive": true,
"RegDate": "2015-12-02"
},
{
"UserID" : 2,
"UserName": "Paul Denton",
"IsActive": 0,
"RegDate": "2015-11-02"
}
]';
SELECT *
FROM OPENJSON(@json)
WITH
(
UserID INT,
UserName VARCHAR(50),
IsActive BIT,
[Date] DATE '$.RegDate'
) |
UserID UserName IsActive Date
----------- --------------- -------- ----------
1 JC Denton 1 2015-12-02
2 Paul Denton 0 2015-11-02 |
DECLARE @json NVARCHAR(4000) = N'
{
"UserID" : 1,
"Detail": [
{ "Year":2016 },
{ "Year":2015, "Options": [{ "Visible":true }]
]
}'
SELECT
JSON_VALUE(@json, '$.UserID')
, JSON_VALUE(@json, '$.Detail[0].Year')
, JSON_VALUE(@json, '$.Detail[1].Year')
, JSON_VALUE(@json, '$.Detail[1].Options[0].Visible') |
DECLARE @json NVARCHAR(MAX) = N'{"ID" : 1}';
SELECT ISJSON(@json), ISJSON('') |
DROP
TABLE
IF
EXISTS dbo.Users
CREATETABLE dbo.Users (
OrderID INT PRIMARYKEY,
JSON NVARCHAR(4000),
CONSTRAINT CK_IsJSON CHECK (ISJSON(JSON)=1),
Age AS (CONVERT(INT, JSON_VALUE(JSON, '$.Age')))
)
CREATEINDEX IX_Age ON dbo.Users(Age) |
ALTER
TABLE dbo.Users
ADD RegDate AS (CAST(JSON_VALUE(JSON, '$.Age') AS DATE))
GO
CREATEINDEX IX_RegDate ON dbo.Users(RegDate)
Msg 2729, Level 16, State 1, Line 15
Column 'RegDate' intable 'dbo.Users' cannot be used in an indexorstatisticsoras a partition key because it is non-deterministic. |
Этот баг должны исправить в следующей версии CTP.
DROP
TABLE
IF
EXISTS dbo.tbl
CREATETABLE dbo.tbl (x VARCHAR(255) NULL)
GO
ALTERTABLE dbo.tbl
ALTERCOLUMN x VARCHAR(255) NOTNULLWITH (ONLINE = ON)
GO
ALTERTABLE dbo.tbl
ALTERCOLUMN x NVARCHAR(255)
COLLATE Cyrillic_General_100_CI_AS NOTNULLWITH (ONLINE = ON) |
CREATE PARTITION FUNCTION PF (SMALLINT) AS RANGE RIGHTFORVALUES (1, 2, 3, 4, 5)
GO
CREATE PARTITION SCHEME PS AS PARTITION PF ALLTO ([PRIMARY])
GO
DROPTABLEIFEXISTS dbo.tbl
CREATETABLE dbo.tbl (a SMALLINT PRIMARYKEY) ON PS (a)
GO
INSERTINTO dbo.tbl (a)
VALUES (0), (1), (2), (3), (4), (5)
SELECT partition_number, [rows]
FROM sys.partitions
WHERE [object_id] = OBJECT_ID('dbo.tbl')
AND index_id < 2
---------------- ------
1 1
2 1
3 1
4 1
5 1
6 1
TRUNCATETABLE dbo.tbl WITH (PARTITIONS (1, 4 TO 5))
partition_number rows
---------------- ------
1 0
2 1
3 1
4 0
5 0
6 1 |
Из того, что я сейчас наблюдаю, релиз SQL Server 2016 обещает быть очень интересным. С каждым новым CTP добавляется большое количество фишек, которые сложно описать в рамках одной статьи. Чтобы сохранить читабельность, за бортом этого обзора я оставилTemporal Tables, Dynamic Data Masking и улучшения в In-Memory, которые планирую добавить в скором продолжении.
Оценка 45 Оценить ![]() ![]() ![]() ![]() ![]() ![]()
|