Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

[ MS SQL ] уникальность в столбце типа: varchar

18K
23 декабря 2011 года
imAlex
179 / / 29.07.2010
Добрый день. Можно ли в ms sql настроить столбец с типом varchar так, чтоб он принимал только уникальные значения? Если да, то как?
14
23 декабря 2011 года
Phodopus
3.3K / / 19.06.2008
Не проще ли самому проверить? UNIQUE.
18K
24 декабря 2011 года
imAlex
179 / / 29.07.2010
1) А как это вообще обычно делается? Допустим надо добавлять уникальное название компании.
2) Можно ли SQL запросом вернуть существующую запись при попытке добавить не уникальную?
3) Можно ли таким методом проверять уникальность по нескольким столбцам (в совокупности)?
Можно пример конструкции?

Я так понимаю конструкция будет следующего вида:
 
Код:
IF NOT EXISTS(SELECT comp FROM Company  WHERE comp='TestComp' OR adres='TestAdress')
 BEGIN
 INSERT INTO Company
 (перечисляем столбцы)
 VALUES (перечисляем значения)
 END
ELSE
 BEGIN
 SELECT comp FROM Company  WHERE comp='TestComp' OR adres='TestAdress'
 END

Еще я тут по прочитанному понял, что можно создать хранимую процедуру вида:
 
Код:
CREATE PROCEDURE InsertUniqueComp
@Comp nvarchar(50),
//тут еще поля
@CompID int OUTPUT
AS

и прям в нее передавать значения. Вот только как?
5
24 декабря 2011 года
hardcase
4.5K / / 09.08.2005
Цитата: imAlex

Можно пример конструкции?

MERGE

18K
24 декабря 2011 года
imAlex
179 / / 29.07.2010
Спасибо. Буду изучать.
А по поводу хранимой процедуры, можете просветить? Я правильно понял ее смысл и как ей пользоваться?
5
24 декабря 2011 года
hardcase
4.5K / / 09.08.2005
Цитата: imAlex
Спасибо. Буду изучать.
А по поводу хранимой процедуры, можете просветить? Я правильно понял ее смысл и как ей пользоваться?


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

385
25 декабря 2011 года
SomewherSomehow
477 / / 25.07.2004
Уникальность на уровне логической модели обеспечивает unique constraint, на уровне физической unique index. В mssql можете создать хоть одно, хоть другое, будет однофигственно, т.к. для обеспечения unique constraint всерно будет создан уникальный индекс. Раз индекс, то желательно чтобы размер колонки был не сильно большой.
Уникальность по комбинации нескольких столбцов обеспечивается включением их в один уникальный индекс. Уникальность по каждому отдельному столбцу - созданием отдельных уникальных индексов. В любом случае, смотрите на размер ключа индекса, чем он меньше тем лучше.

Если требуется вернуть, то да, примерно так, как вы сами написали, если требуется добавить новую или обновить существующую, то можно использовать, как уже посоветовали, MERGE, если версия сервера >=2008. Ну или по старинке insert+update.

Смысл хранимой процедуры - в том же, в чем и смысл процедуры в обычных языках программирования. Вы можете взять определенный набор инструкций, дать ему имя и сохранить на сервере, после чего, для его выполнения, просто обратиться по этому имени, указав список параметров если он был задан. Имейте кстати ввиду, что процедура!=транзакция. Т.е. если вы вызовете процедуру и думаете что она либо выполнится целиком, либо нет, то это не так, чтобы это было так - вызов процы нужно обернуть в транзакцию.

Касательно параметров output - вот, накидал простой пример, можете скопипастить и выполнить его в SSMS у себя на тестовом сервере
Код:
use tempdb;
go
-- создаем таблицу
create table dbo.Company(CompID int identity primary key, Name varchar(100));
go
create unique nonclustered index ix_Name on dbo.Company(Name);
go
------------------------
-- создаем процедуру
create proc dbo.Company_insert
    @CompID int = null out,
    @Name varchar(100)
as
set @CompID = null;

if exists (select * from dbo.Company where Name = @Name) begin
    raiserror ('Компания ''%s'' уже заведена в БД',10,1,@Name);
    return;
end;

insert into dbo.Company(Name)
values (@Name);

set @CompID = scope_identity();
go
------------------------
-- вызываем процедуру, смотрим что добавилось, смотрим какой ИД получила запись
declare @CompID_inserted int;

-- вызов раз
exec dbo.Company_insert @CompID = @CompID_inserted out, @Name = 'ООО Рога и Копыта';
select @CompID_inserted;

-- вызов два-с
exec dbo.Company_insert @CompID = @CompID_inserted out, @Name = 'ИП Вася Пупкин';
select @CompID_inserted;

-- что в таблице
select * from dbo.Company;

-- пробуем добавить повторно
exec dbo.Company_insert @CompID = @CompID_inserted out, @Name = 'ИП Вася Пупкин';
select @CompID_inserted;
go
------------------------
-- удаляем демонстрационные данные
drop table dbo.Company;
drop proc dbo.Company_insert;
18K
26 декабря 2011 года
imAlex
179 / / 29.07.2010
Цитата: SomewherSomehow
..Раз индекс, то желательно чтобы размер колонки был не сильно большой.


Имеется ввиду количество позиций (строк)? Предполагается примерно пару сотен. Возможно до тысячи, ну не больше))

Цитата: SomewherSomehow
Уникальность по комбинации нескольких столбцов обеспечивается включением их в один уникальный индекс. Уникальность по каждому отдельному столбцу - созданием отдельных уникальных индексов. В любом случае, смотрите на размер ключа индекса, чем он меньше тем лучше.


Я так понял, это то что описано на MSDN - Индекс с включенными столбцами.
Отсюда вопрос: работает ли это на 2005-ом сервере? (Ах да, я вроде не упоминал про него. Сейчас учусь работать с ним)

Про хранимки пока понятно. Буду тестировать.

5
26 декабря 2011 года
hardcase
4.5K / / 09.08.2005
Цитата: imAlex
Имеется ввиду количество позиций (строк)?

Имеется в виду длина строк которые будут храниться в этом столбце.

385
26 декабря 2011 года
SomewherSomehow
477 / / 25.07.2004
Цитата: imAlex
Имеется ввиду количество позиций (строк)? Предполагается примерно пару сотен. Возможно до тысячи, ну не больше))


Нет, имеется ввиду размер типа данных поля, то что сказал hardcase. Т.е. если у вас поле например имеет тип varchar(8000), то индекс по нему, плохая идея. Собственно желательно чтобы суммарный размер типов данных колонок, участвующих в ключе, не превышал 900 байт, да и вообще, хоть 900 байт и позволяется как крайнее значение - не стоит этим злоупотреблять.

Цитата: imAlex
Я так понял, это то что описано на MSDN - Индекс с включенными столбцами.
Отсюда вопрос: работает ли это на 2005-ом сервере? (Ах да, я вроде не упоминал про него. Сейчас учусь работать с ним)


Нет, включенные столбцы это другое. Это если, например, осуществляется операция поиск по индексу, найден нужный ключ индекса, то после этого требуется найти саму строку данных на которую указывает этот ключ, а это дополнительные операции. Но если в индекс включены, помимо самого ключа, еще и все данные что требуется запросу, то дополнительной операции поиска можно избежать, сразу взяв требуемые данные из самого индекса.
А я имел ввиду, что если например есть таблица с полями Фамилия, Имя. То если вы сделаете два уникальных индекса, один по фамилии, другой по имени, то нельзя будет ввести двух Ивановых (вне зависимости от их имени) или двух Василиев, вне зависимости от фамилии. Если же вы сделаете один индекс сразу по двум полям Имя+Фамилия, то будет проверяться уникальность сочетания этих значений. Например Можно будет ввести Иванов Иван и Иванов Василий. Но нельзя будет ввести Иванов Иван, Иванов Иван.
Если, кстати, у вас реально до тысячи и не предполагается больше, и таблица не широкая, то можете, в принципе, пока особо не задумываться над производительностью, это не большой объем данных. Но ради обеспечения целостности данных, если какое-то поле предполагается быть уникальным, то по нему в любом случае лучше создать уникальный констрейнт или индекс (с учетом пожеланий по размеру приведенных выше), даже если предполагается что с ним будут работать только через хранимые процедуры.

18K
26 декабря 2011 года
imAlex
179 / / 29.07.2010
Цитата: SomewherSomehow
чтобы суммарный размер типов данных колонок, участвующих в ключе, не превышал 900 байт


Эти 900 байт как высчитываются? Это Name varchar(900) (F_Name varchar(n) + S_Name varchar(m) < 900)? Или при Name varchar (n) и кол. предполагаемых строк "m": n*m<900 (то есть длинна строки помноженная на длинну) ?
На данный момент я практикую varchar (<100), но интересно естественно и дальнейшее развитие.

По поводу процедур, я попробовал, вроде разобрался как работает. Вы упоминали о том, что ХП не является транзакцией и при необходимости, необходимо ХП обернуть в транзакцию.
Для этого необходимо задать начало транзакции (BEGIN TRANSACTION <name>) и ее завершение (COMMIT TRANSACTION <name>). Тогда все, происходящее внутри либо завершится с успехом, либо "откатится" до точки начала транзакции?

5
26 декабря 2011 года
hardcase
4.5K / / 09.08.2005
Кстати, не рекомендуется использовать varchar() - это строка с однобайтовой кодировкой, учитывая что у вас это имя компании, лучше использовать тип данных nvarchar() - юникодную строку.
Для работы с большим количеством строк (например в объемах онлайн-словарей) гораздо производительнее использовать хэши (CHECKSUM) и проверять сперва существование в таблице одноименной записи по хэшу, а лишь потом сравнивая сами строки.
385
26 декабря 2011 года
SomewherSomehow
477 / / 25.07.2004
По первому варианту, просто сложение размеров данных. Кол-во строк не учитывается.
Насчет транзакций да, нужно обернуть. Можно открыть транзакцию в начале тела самой ХП, но имхо, удобнее для этого использовать код клиентских компонентю Например в .NET что-то типа такого.
Код:
sqlCon.Open();
var sqlTran = sqlCon.BeginTransaction();
try
{
sqlCommand.Transaction = sqlTran;
sqlCommand.ExecuteNonQuery();
sqlTran.Commit();
}
catch
{
sqlTran.Rollback();
}
finally
{
sqlCon.Close()
}

Где sqlCon, sqlTran, sqlCommand - соотв. клиентские компоненты для работы с соединением, транзакцией и процедурой.
писал по памяти, студию влом запускать, так что мог налажать в названиях методов, но принцип думаю понятен. Наверняка можно что-то подобное изобразить и у вас. Тогда не нужно будет заботится об открытии и закрытии транзакций в самих процедурах.

hardcase,
Если не планируется вводить названия компаний более чем на двух языках (один из них англ), лучше оставить varchar() - функционал тот же, места в два раза меньше.
18K
28 декабря 2011 года
imAlex
179 / / 29.07.2010
По поводу транзакции, вы мне писали в другой теме пример. Спасибо.
Я так понял, транзакция самостоятельно откатывает действия?
Можно ли как-нибудь проверить работу отката? Интересно как это выглядит )))

PS: огромное спасибо за терпение и "разжевывание". Жаль, что в университете так не объясняли :D. Получается предмет пройден, а из знаний только некоторые понятия, и то половина по собственной инициативе.
385
28 декабря 2011 года
SomewherSomehow
477 / / 25.07.2004
Проверить хм... проверьте состояние данных до и после =) Но фишка современных субд в том что, они как правило гарантируют принципы ACID. Так что можете не волноваться, даже если у вас отключится электроэнергия, то все транзакции должны быть в согласованном состоянии, для этого сиквел, например, использует упреждающую запись. Т.е. данные не попадают на страницы данных до того как информация об изменениях не будет записана в журнал транзакций. (Хотя у нас один раз умерла батарейка на контроллере и почему-то админ БД сказал, что БД можно восстановить только с бэкапа, но есть мнение, что слажал наш админ, а не сервер) .
Если хотите проверить примитивно то вот например, запустите скрипт:
 
Код:
create table t (a int);
insert into t select 1;
begin tran;
insert into t select 2;
select * from t;
rollback tran;
select * from t;
go
drop table t;
go

Как вы увидите, сперва поcле добавления в транзакции в таблице будет две записи. Потом, без удаления, а просто откатом транзакции останется одна, т.е. последний инсерт откатится. Это очень упрощенно.
Интереснее исследовать механизмы блокировки и грязного чтения. Например, если вы откроете два окна в SSMS и
1) в первом выполните
Код:
create table t (a int);
go
insert into t select 1;

begin tran;
    insert into t select 2;
    select * from t;
----------
/*
rollback tran;
    select * from t;
go
drop table t;
go
*/

2) во втором
 
Код:
select * from t with(nolock);

3) далее вернитесь в первое и выделите мышкой только то, что между /*...*/ и, т.е.
 
Код:
rollback tran;
    select * from t;
go
drop table t;
go
, а потом нажмите выполнить или F5.

Вы получите одну запись, тогда как запрос во втором окне, вернул две.
Т.е. он вернул запись которая реально уже не существует в таблице, т.к. транзакцию добавившую запись откатили а второй запрос попал так "неудачно" что прочел незафиксированные данные. Это эффекты грязного чтения с подсказкой nolock, которыми некоторые пользуются для ускорения работы сервера, т.к. не требуется ждать освобождения блокировок, но при этом не учитывают такие эффекты. так же возможны эффекты повторения записей, отсутствия записей и прочие интересные штуки.

Имхо, в универститете дают базу. Я вот с удовольствием учился первые три курса когда были матан, физика, дискретка, дифуры и т.д. И хоть сейчас я уже все почти забыл и в жизни это не пригодилось непосредственно - но зато это научило мыслить и воспринимать информацию абстрактно. Читал такую фразу и полностью с ней согласен : "Образование, это то, что у вас останется, когда вы забудете все, чему вас учили."

upd
А касательно собственой инициативы - это правильно! так что я целиком вас поддерживаю. Тоже учился всему что знаю самостоятельно и с помощью старших наставников на работе. Универ в этом плане у нас лажает. Мы тардиционно сильны в фундаментальных науках, все те предметы и многие другие что я перечислил выше, очень хорошие препы с советских времен. А на новые предметы увы - нет кадров. Зарплата не соответствует. Есть энтузиасты, в основном аспиранты или молодые препы работающие по совместительству, которым огромный поклон. Они пытаются что-то делать, но не у всех есть время и не всем работа позволяет. Общался недавно с одним таким челом, он сам MVP, более того приглашен на работу в MS, хотя пока преподает...но ведь тоже скоро уедет. Грустно короче. Энтузиасты - решение частных случаев, в очень не многих местах, чтобы это было повсеместно - нужна система. Ее увы пока нет. Хотя власти говорят о модернизации. Х.з. можно верить или это лозунги под очередные распилы бюджета. В любом случае надеяться, имхо, надо только на себя, так что самообразование пока рулит!
5
28 декабря 2011 года
hardcase
4.5K / / 09.08.2005
Цитата: SomewherSomehow
фишка современных субд в том что, они как правило гарантируют принципы ACID.

Не нужно так считать, например в MSSQL изолированность (буква I в аббревиатуре) управляется уровнем изоляции транзакции. Я предпочитаю открывать транзации не в клиентском коде, а в теле самой хранимой процедуре. В клиентском коде TransactionScope нужен для выполнения ряда запросов либо исполнения хранимых процедур, для случае вызова одной хранимки он выглядит странно.

5
28 декабря 2011 года
hardcase
4.5K / / 09.08.2005
Цитата: SomewherSomehow

hardcase,
Если не планируется вводить названия компаний более чем на двух языках (один из них англ), лучше оставить varchar() - функционал тот же, места в два раза меньше.

Это экономия на спичках либо преждевременная оптимизация.

385
28 декабря 2011 года
SomewherSomehow
477 / / 25.07.2004
Цитата: hardcase
Не нужно так считать, например в MSSQL изолированность (буква I в аббревиатуре) управляется уровнем изоляции транзакции. Я предпочитаю открывать транзации не в клиентском коде, а в теле самой хранимой процедуре. В клиентском коде TransactionScope нужен для выполнения ряда запросов либо исполнения хранимых процедур, для случае вызова одной хранимки он выглядит странно.



Не нужно считать, что MSSQL обеспечивает ACID? В таком случае, приведите пример, когда с помощью транзакций нельзя обеспечить ACID. Сам функционал isolation level подразумевает изоляцию транзакций, разве не это декларирует ACID? Или вы под изоляцией понимаете вообще всегда полную изоляцию? Это тоже возможно, но в таком случае это просто ограничение, никак не влияющее на сам принцип. И лично я, рад, что разработчикам предоставлена возможность самим влиять на уровень изоляции. И т.е. если возможно грязное чтение (как я в предыдущем посте показал) это не значит что не обеспечивается изоляция, это значит, что изоляция контролируема. Вы так не считаете?
В клиентском ли коде или в процедурах - дело вкуса. Лично мне, удобнее, когда я открываю процу для редактирования не видеть логики обработки транзакции, т.к. я стараюсь оформлять в процедуры такой код, который либо выполняется целиком, либо нет. И транзакции были бы загромождением кода. Но повторяю, дело вкуса, в предыдущем посте я сказал, что возможно просто открывать транзакцию в начале тела процедуры (или вообще в любом месте, на самом деле).
Насчет экономии на спичках...Хм...=)
Прежде чем ответить вам, спрошу, в чем конкретно экономия и спички? =)

5
28 декабря 2011 года
hardcase
4.5K / / 09.08.2005
Цитата: SomewherSomehow
Сам функционал isolation level подразумевает изоляцию транзакций, разве не это декларирует ACID? Или вы под изоляцией понимаете вообще всегда полную изоляцию?

Под I подразумевается полная иозляция (уровень SERIALIZABLE), а при приличной нагрузке это выльется в очень сильное падение прозводительности. Поэтому были придуманы менее сильные режимы изоляции - они позволяют параллельно работать большему числу клиентов. Я веду к тому, что стандартный уровень изоляции READ COMMITTED совсем не является тем I про который написано в учебниках, он существнно слабее.

Цитата: SomewherSomehow

Насчет экономии на спичках...Хм...=)
Прежде чем ответить вам, спрошу, в чем конкретно экономия и спички? =)

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

385
29 декабря 2011 года
SomewherSomehow
477 / / 25.07.2004
Цитата: hardcase
Под I подразумевается полная иозляция (уровень SERIALIZABLE), а при приличной нагрузке это выльется в очень сильное падение прозводительности. Поэтому были придуманы менее сильные режимы изоляции - они позволяют параллельно работать большему числу клиентов. Я веду к тому, что стандартный уровень изоляции READ COMMITTED совсем не является тем I про который написано в учебниках, он существнно слабее.

Повторяю, вопрос не в том, каков уровень по-умолчанию, вопрос в том, что если нужно - можно обеспечить полную изоляцию. А в новых версиях сервера, благодаря snapshot isolation можно даже не сильно потерять в производительности. Хотя тут мы говорим не о производительности, а о самом факте наличия такой возможности.

Цитата: hardcase
Смысл в том, что у ТС имен компаний вряд ли наберется столько, что двукратное использование дисковой памяти под их хранение будет хоть сколько нибудь заметно. Попробуй ответить на вопрос - сколько имен компаний можно уместить в мегабайт.


Значит дисковая память под хранение. Отлично, именно по этому я уточнил что вы имеете ввиду говоря о спичках. =)

Но штука в том, что если бы сиквел сервер всегда осуществлял все операции чтения с диска - он был бы жутким тормозом. Для того чтобы этого избежать, в нем есть такая вещь как буферный кэш. И все операции чтения в сиквеле идут только через него!
Возможности этого кэша гораздо скромнее в плане объемов возможностей диска. По этому, чем он больше и чем меньше страниц данных в таблице, тем лучше для скорости.
Что мы получим, если в два раза увеличим объем строковых полей.
1. Кол-во страниц данных возрастет в два раза, потребуется в два раза больше физических операций чтения с диска, при отсутствии страницы в кэше.
2. Кол-во места занимаемого в кэше таблицей увеличится в два раза, а значит в два раза возрастет вероятность того, что данные будут вытеснены из кэша другими данными и придется делать повторное обращение к диску.
3. Если планируется осуществлять поиск по имени компании (а это в 99% почти всегда нужно), придется построить индекс по этому полю. Объем ключа индекса возрастет в два раза. А значит:
3.1. В два раза возрастут затраты на обслуживание индекса
3.2. В два раза уменьшится время необходимое для заполнения верхнего уровня индекса и создания еще одного. А значит в два раза быстрее увеличится число чтений при операции index seek (что так же накладывается на в два раза большую вероятность отсутствия страницы в кэше и дает эффект синергии падения производительности)
4. Если мы распространим такую "экономию на спичках" на проектирование всей БД - все эффекты возрастут многократно.

Хорошая плата за преждевременную псевдоуниверсальность?
Дисковое место тут играет последнюю роль, оно сейчас дешево, так что вы правы, когда говорите что его экономия нецелесообразна, но это очень поверхностно, а следует взглянуть в корень.
Моя полемика тут продиктована не желанием поспорить, а практическим опытом. Так уж получилось что я как раз занимаюсь системой CRM, в международной группе компаний, и пока за 7 лет существования системы, ни разу не возникла необходимость в юникоде. Т.к. в основном все интернациональные компании имеют международное английское название, а менеджеры работающие с системой, предпочитают видеть не китайские иероглифы на родном языке компании, а ее англоязычное название.
Так же, порекомендую вам статью Экономим место в БД за счет использования правильных типов данных. Там не обсуждается nvarchar vs varchar, но экономия всего в несколько байт позволяет уменьшить таблицу и снизить нагрузку на сервер, а тут мы говрим об экономии в два раза!

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог