SQL Server - Как вставить запись и убедиться, что она уникальна

StackOverflow https://stackoverflow.com/questions/267804

  •  06-07-2019
  •  | 
  •  

Вопрос

Я пытаюсь найти наилучший способ вставить запись в одну таблицу, но только в том случае, если элемент еще не существует.Ключом в данном случае является поле NVARCHAR(400).Для этого примера давайте представим, что это имя слово в Оксфордском словаре английского языка / вставьте сюда свой любимый словарь.Кроме того, я предполагаю, что мне нужно будет сделать поле Word первичным ключом.(таблица также будет иметь уникальный идентификатор PK).

Итак ..я мог бы получить эти слова, которые мне нужно добавить в таблицу...

например.

  • Кошка
  • Собака
  • Фу
  • Бар
  • Оловянный Пот
  • и т.д...

Поэтому традиционно я бы попробовал следующее (псевдокод)

SELECT WordID FROM Words WHERE Word = @Word
IF WordID IS NULL OR WordID <= 0
    INSERT INTO Words VALUES (@Word)

т. е. Если слово не существует, то вставьте его.

Сейчас же ..проблема , о которой я беспокоюсь , заключается в том , что мы получаем МНОГО обращений ..итак , возможно ли , что слово могло быть вставлено из другого процесса между SELECT и INSERT ..который затем выдал бы ошибку ограничения?(то есть.a Условия гонки).

Затем я подумал , что , возможно , смог бы сделать следующее ...

INSERT INTO Words (Word)
SELECT @Word
WHERE NOT EXISTS (SELECT WordID FROM Words WHERE Word = @Word)

в основном, вставьте слово, когда оно не существует.

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

Итак, что вы, Sql-гуру, думаете / делаете?

Я надеялся иметь простую вставку и "перехватывать" ее при любых возникших ошибках.

Это было полезно?

Решение

Ваше решение:

INSERT INTO Words (Word)
    SELECT @Word
WHERE NOT EXISTS (SELECT WordID FROM Words WHERE Word = @Word)

... примерно настолько хорош, насколько это возможно.Вы могли бы упростить это до следующего:

INSERT INTO Words (Word)
    SELECT @Word
WHERE NOT EXISTS (SELECT * FROM Words WHERE Word = @Word)

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

Однако, как вы упомянули, это не особенно эффективно, потому что это заблокирует всю таблицу во время вставки.За исключением этого, если вы добавите уникальный индекс (он не обязательно должен быть первичным ключом) в Word, то ему нужно будет заблокировать только соответствующие страницы.

Ваш лучший вариант - смоделировать ожидаемую нагрузку и посмотреть на производительность с помощью SQL Server Profiler.Как и в любой другой области, преждевременная оптимизация - это плохо.Определите приемлемые показатели производительности, а затем измерьте, прежде чем делать что-либо еще.

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

Другие советы

Я думаю, что нашел лучший (или, по крайней мере, более быстрый) ответ на этот вопрос.Создайте индекс, подобный:

CREATE UNIQUE NONCLUSTERED INDEX [IndexTableUniqueRows] ON [dbo].[table] 
(
    [Col1] ASC,
    [Col2] ASC,

)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = ON, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

Включите все столбцы, которые определяют уникальность.Важной частью является IGNORE_DUP_KEY = ВКЛ.Это превращает неуникальные вставки в предупреждения.Служба SSIS игнорирует эти предупреждения, и вы по-прежнему можете использовать fastload.

Если вы используете MS SQL Server, вы можете создать уникальный индекс для столбцов вашей таблицы, которые должны быть уникальными (документированными здесь):

CREATE UNIQUE [ CLUSTERED | NONCLUSTERED ] INDEX <index_name>
    ON Words ( word [ ASC | DESC ])

Указать Clustered или NonClustered, в зависимости от вашего случая.Кроме того, если вы хотите, чтобы он был отсортирован (чтобы включить более быстрый поиск), укажите ASC или DESC для порядка сортировки.

Видишь здесь, если вы хотите узнать больше об архитектуре индексов.

В противном случае вы могли бы использовать UNIQUE CONSTRAINTS как задокументировано здесь:

ALTER TABLE Words
ADD CONSTRAINT UniqueWord
UNIQUE (Word); 

У меня была похожая проблема, и вот как я ее решил

insert into Words
( selectWord , Fixword)
SELECT word,'theFixword'
FROM   OldWordsTable
WHERE 
(
    (word LIKE 'junk%') OR
     (word LIKE 'orSomthing') 

)
and word not in 
    (
        SELECT selectWord FROM words WHERE selectWord = word
    ) 

хотя ограничение уникальности, безусловно, является одним из способов решения проблемы, вы также можете использовать это для своей логики вставки:http://www.sqlteam.com/article/application-locks-or-mutexes-in-sql-server-2005

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

это мьютекс в коде sql.

Я не могу говорить о деталях MS SQL, но одним из аспектов первичного ключа в SQL является обеспечение уникальности.Таким образом, по определению в общих терминах SQL, первичный ключ - это одно или несколько полей, уникальных для таблицы.Хотя существуют различные способы принудительного применения этого поведения (замените старую запись на новую по сравнениюотклонить новую) Я был бы удивлен, если бы в MS SQL не было механизма для обеспечения соблюдения этого поведения и что это не было сделано для отклонения новой записи.Просто убедитесь, что вы установили первичный ключ в поле Word, и это следует работать.

Еще раз, однако, я отрицаю, что все это основано на моих знаниях по программированию MySQL и моем классе databases, поэтому прошу прощения, если я не разбираюсь в тонкостях MS SQL.

declare @Error int

begin transaction
  INSERT INTO Words (Word) values(@word)
  set @Error = @@ERROR
  if @Error <> 0 --if error is raised
  begin
      goto LogError
  end
commit transaction
goto ProcEnd

LogError:
rollback transaction
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top