Тоже самое и у меня было. 4 года назад в новую КВ купил разных светодиодных ламп. Гаусы все подсохли первыми в первый же год (лампы разной мощности), хотя стоили значительно дороже конкурентов. Самые стойкие оказались какие-то самые дешёвые Feron, до сих пор большинство ламп этого бренда работают. У них просто выгорает люминофор и светят со временем слабее.
Данные сразу будут записаны в LOB хранилище. И это правильно, т.к. в большинстве случаев если мы читаем var...(max) поле, то мы читаем его полностью, значит нет смысла часть его хранить в таблице а часть в LOB. И второе, достаточно часто в запросах читаются все поля кроме var...(max), т.е. больше поля читаются как правило точеными запросами, типа получения объекта по его идентификатору. А значит большинство запросов выигрывают в случае включения этой опции, т.к. читая страницы таблицы мы не читаем багаж в виже части LOB полей.
1 = varchar(max), nvarchar(max), varbinary(max), xml and large user-defined type (UDT) columns in the table are stored out of row, with a 16-byte pointer to the root.
0 = varchar(max), nvarchar(max), varbinary(max), xml and large UDT values are stored directly in the data row, up to a limit of 8000 bytes and as long as the value can fit in the record. If the value does not fit in the record, a pointer is stored in-row and the rest is stored out of row in the LOB storage space. 0 is the default value.
К примеру в таблице 15 столбцов, один из столбцов (DataStatus) содержит 50% одинаковых значений (значение Completed), и что вы предлагаете, как нормализовать такую таблцу?
Перенести Completed записи в другую таблицу? А потом мучиться с отчетами, и с процессами по переносу данных и одной таблиы в другую. Может кто-то захочит перевести статус в Active? А если нужно обновить данные другого столбца то придется обновлять две таблицы, делать два запроса, писать "умный" DataAccess. В общем это проблема из ничего.
Нормализация хороша в теории, на практике выше 3-й нормальной формы заморачиватся не стоит.
Все нормально работает. Просто нужно указывать точный фильтр в запросе, такой же как и в фильтрованном индексе. Будет работать и для сложных условий по нескольким столбцам тоже.
create index IDX_VAL on Unlucky (VAL) where VAL <> 'n/a'
go
create or alter procedure SmartGetVal
@val varchar(32)
as
if @val = 'n/a'
select *
from dbo.Unlucky
where VAL = 'n/a' -- not @val!!!
else
select *
from dbo.Unlucky with ( index = IDX_VAL )
where VAL = @val
and VAL <> 'n/a'
go
exec dbo.SmartGetVal @val = '1val'
go
print @@version
Microsoft SQL Server 2017 (RTM-GDR) (KB4583456) - 14.0.2037.2 (X64)
Nov 2 2020 19:19:59
Copyright (C) 2017 Microsoft Corporation
Developer Edition (64-bit) on Windows 10 Pro 10.0 <X64> (Build 19044: )
При использовании типа varchar(n) / nvarchar(n), sql server предполагает что средний размет строки будет n/2, и исходя из этого выделяет память для выполнения запросов. Поэтому при выборе значения n идеальным будет удвоенное значение средней длины строк хранящихся в этом столбце. Для varcha(max) / nvarchar(max) он предполагает что средняя длина равна 4000 символам, т.е. если вы скажем будете хранить строки размером в 100 символов в таком столбце, то памяти будет выделяться в 40 раз больше чем нужно для запроса, что в свою очередь плохо скажется на производительности.
Скажем у вас в таблице есть столбцы с данными, и один или несколько столбцов varcha(max) / nvarchar(max). По умолчанию данные varcha(max) / nvarchar(max) хранятся IN_ROW если объем их меньше 8000 байт. Иногда имеет смысл поменять это поведение, и сразу хранить все эти данные как LOB_DATA. Это улучшит производительность запросов которые не читают LOB столбцы из таблицы. Такое поведение можно включить через опцию large value types out of row
exec sys.sp_tableoption
@TableNamePattern = 'dbo.TableName'
, @OptionName = 'large value types out of row'
, @OptionValue = '1'
Не работает так, как вы описываете. Без кластерного индекса такое же поведение.
Все также висит X (exclusive) блокировка на некластерном индексе во время апдейта, и она не совместима с S (shared) блокировкой при чтении, вторая сессия ждет завершения первой.
Может быть описываемое вами поведение это просто баг/фича в какой-нибудь старой версии sql server?
Тестировалось на:
Microsoft SQL Server 2019 (RTM-GDR) (KB4583458) - 15.0.2080.9 (X64) Nov 6 2020 16:50:01
Copyright (C) 2019 Microsoft Corporation Developer Edition (64-bit)
on Windows 10 Pro 10.0 <X64> (Build 19044: )
Я правильно понимаю что мы говорим о двух сессиях, первая обновляет строку, вторая читает ее в обычном read committed?
-- table definition
create table dbo.test1 (
id int primary key clustered
, data1 varchar(100)
, data2 char(100)
, data3 nchar(100)
)
go
create index idx_test1_allincluded
on dbo.test1 ( id ) include ( data1, data2, data3 )
go
insert into dbo.test1 ( id, data1, data2, data3 )
values (1, 'data1 1', 'data2 1', 'data3 1')
, (2, 'data1 2', 'data2 2', 'data3 2')
, (3, 'data1 3', 'data2 3', 'data3 3')
go
-- session 1
update dbo.test1 set data1 = 'new data 1' where id = 2
go
-- session 2
select * from dbo.test1 where id = 2
go
Если да, то в данном случае не важно есть ли дополнительный индекс или нет, т.к. эксклюзивная блокировка ключа (id=2) накладывается и в кластерном индексе и в дополнительном некластерном. Вторая сессия будет ждать завершения первой в любом случае, и с дополнительным индеком и без него.
Тоже самое и у меня было. 4 года назад в новую КВ купил разных светодиодных ламп. Гаусы все подсохли первыми в первый же год (лампы разной мощности), хотя стоили значительно дороже конкурентов. Самые стойкие оказались какие-то самые дешёвые Feron, до сих пор большинство ламп этого бренда работают. У них просто выгорает люминофор и светят со временем слабее.
Мы используем ноутбуки и git, у нас есть итерации бранят и continuous delivery. Такой вариант всех устраивает.
Databricks + Azure DevOps.
Данные сразу будут записаны в LOB хранилище. И это правильно, т.к. в большинстве случаев если мы читаем var...(max) поле, то мы читаем его полностью, значит нет смысла часть его хранить в таблице а часть в LOB. И второе, достаточно часто в запросах читаются все поля кроме var...(max), т.е. больше поля читаются как правило точеными запросами, типа получения объекта по его идентификатору. А значит большинство запросов выигрывают в случае включения этой опции, т.к. читая страницы таблицы мы не читаем багаж в виже части LOB полей.
Из доков:
https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-tableoption-transact-sql?view=sql-server-ver15#arguments
large value types out of row
1 = varchar(max), nvarchar(max), varbinary(max), xml and large user-defined type (UDT) columns in the table are stored out of row, with a 16-byte pointer to the root.
0 = varchar(max), nvarchar(max), varbinary(max), xml and large UDT values are stored directly in the data row, up to a limit of 8000 bytes and as long as the value can fit in the record. If the value does not fit in the record, a pointer is stored in-row and the rest is stored out of row in the LOB storage space. 0 is the default value.
Можно ставить "метку" на запрос.
В данном примере для n/a значения будет другой текст запроса, а значит и другой план (даже коммент в текте запроса меняет его хеш).
Как видите, тут два разных плана. Причем их будет только 2, а не миллион как с инлайном где также присутствует проблема sql injection.
К примеру в таблице 15 столбцов, один из столбцов (DataStatus) содержит 50% одинаковых значений (значение Completed), и что вы предлагаете, как нормализовать такую таблцу?
Перенести Completed записи в другую таблицу? А потом мучиться с отчетами, и с процессами по переносу данных и одной таблиы в другую. Может кто-то захочит перевести статус в Active? А если нужно обновить данные другого столбца то придется обновлять две таблицы, делать два запроса, писать "умный" DataAccess. В общем это проблема из ничего.
Нормализация хороша в теории, на практике выше 3-й нормальной формы заморачиватся не стоит.
Все нормально работает. Просто нужно указывать точный фильтр в запросе, такой же как и в фильтрованном индексе. Будет работать и для сложных условий по нескольким столбцам тоже.
Еще пара моментов которые надо учитывать.
При использовании типа varchar(n) / nvarchar(n), sql server предполагает что средний размет строки будет n/2, и исходя из этого выделяет память для выполнения запросов. Поэтому при выборе значения n идеальным будет удвоенное значение средней длины строк хранящихся в этом столбце.
Для varcha(max) / nvarchar(max) он предполагает что средняя длина равна 4000 символам, т.е. если вы скажем будете хранить строки размером в 100 символов в таком столбце, то памяти будет выделяться в 40 раз больше чем нужно для запроса, что в свою очередь плохо скажется на производительности.
Скажем у вас в таблице есть столбцы с данными, и один или несколько столбцов varcha(max) / nvarchar(max). По умолчанию данные varcha(max) / nvarchar(max) хранятся IN_ROW если объем их меньше 8000 байт.
Иногда имеет смысл поменять это поведение, и сразу хранить все эти данные как LOB_DATA. Это улучшит производительность запросов которые не читают LOB столбцы из таблицы.
Такое поведение можно включить через опцию
large value types out of rowНе работает так, как вы описываете. Без кластерного индекса такое же поведение.
Все также висит X (exclusive) блокировка на некластерном индексе во время апдейта, и она не совместима с S (shared) блокировкой при чтении, вторая сессия ждет завершения первой.
Может быть описываемое вами поведение это просто баг/фича в какой-нибудь старой версии sql server?
Я правильно понимаю что мы говорим о двух сессиях, первая обновляет строку, вторая читает ее в обычном read committed?
Если да, то в данном случае не важно есть ли дополнительный индекс или нет, т.к. эксклюзивная блокировка ключа (id=2) накладывается и в кластерном индексе и в дополнительном некластерном. Вторая сессия будет ждать завершения первой в любом случае, и с дополнительным индеком и без него.
Не за компом. Вот это разве не сработает?
ещё индексы на вьюхах в онлайне не построить.
Напишите пожалуйста подробнее. Что значит отключение версионирования? И какой лок накладывается? Впервые слышу про эту проблему...