SQL Server insert if not exists best practice
у меня есть Competitions
таблицы, которая содержит имена членов команды и их рейтинг с одной стороны.
С другой стороны мне нужно поддерживать таблица уникальных имен конкурентов:
CREATE TABLE Competitors (cName nvarchar(64) primary key)
Теперь у меня есть около 200 000 результатов в 1-й таблице и когда таблица конкурентов пуста Я могу выполнить это:
INSERT INTO Competitors SELECT DISTINCT Name FROM CompResults
и запрос занимает всего около 5 секунд, чтобы вставить около 11000 имена.
до сих пор это не критическое приложение, поэтому я могу рассмотреть усечь таблицу конкурентов раз в месяц, когда я получаю новые результаты конкурса с некоторыми 10 000 строк.
но какова лучшая практика, когда добавляются новые результаты, с новыми и существующими конкурентами? Я не хочу усекать существующие таблицы конкурентов
мне нужно выполнить инструкцию INSERT только для новых конкурентов и ничего не делать, если они существует.
7 ответов:
семантически вы просите "вставить конкурентов там, где их еще нет":
INSERT Competitors (cName) SELECT DISTINCT Name FROM CompResults cr WHERE NOT EXISTS (SELECT * FROM Competitors c WHERE cr.Name = c.cName)
другой вариант-слева присоединиться к таблице результатов с существующей таблицей конкурентов и найти новых конкурентов путем фильтрации различных записей, которые не соответствуют int join:
INSERT Competitors (cName) SELECT DISTINCT cr.Name FROM CompResults cr left join Competitors c on cr.Name = c.cName where c.cName is null
новый синтаксис слияние также предлагаем компактный, элегантный и эффективный способ сделать это:
MERGE INTO Competitors AS Target USING (SELECT DISTINCT Name FROM CompResults) AS Source ON Target.Name = Source.Name WHEN NOT MATCHED THEN INSERT (Name) VALUES (Source.Name);
не знаю, почему никто еще этого не сказал;
нормализуется.
у вас есть таблица, которая моделирует соревнования? Соревнования состоят из конкурентов? Вам нужен четкий список участников одного или нескольких соревнований......
вы должны иметь следующие таблицы.....
CREATE TABLE Competitor ( [CompetitorID] INT IDENTITY(1,1) PRIMARY KEY , [CompetitorName] NVARCHAR(255) ) CREATE TABLE Competition ( [CompetitionID] INT IDENTITY(1,1) PRIMARY KEY , [CompetitionName] NVARCHAR(255) ) CREATE TABLE CompetitionCompetitors ( [CompetitionID] INT , [CompetitorID] INT , [Score] INT , PRIMARY KEY ( [CompetitionID] , [CompetitorID] ) )
С ограничениями на конкурентов.CompetitionID и CompetitorID, указывающие на другие таблицы.
С этим видом таблицы структура-ваши ключи все простые INTS-кажется, нет хорошего естественного ключа, который соответствовал бы модели, поэтому я думаю, что суррогатный ключ хорошо подходит здесь.
поэтому, если у вас было это, чтобы получить отличный список конкурентов в конкретном соревновании, вы можете отправить запрос следующим образом:
DECLARE @CompetitionName VARCHAR(50) SET @CompetitionName = 'London Marathon' SELECT p.[CompetitorName] AS [CompetitorName] FROM Competitor AS p WHERE EXISTS ( SELECT 1 FROM CompetitionCompetitor AS cc JOIN Competition AS c ON c.[ID] = cc.[CompetitionID] WHERE cc.[CompetitorID] = p.[CompetitorID] AND cc.[CompetitionName] = @CompetitionNAme )
и если вы хотели счет для каждого конкурса конкурент находится в:
SELECT p.[CompetitorName] , c.[CompetitionName] , cc.[Score] FROM Competitor AS p JOIN CompetitionCompetitor AS cc ON cc.[CompetitorID] = p.[CompetitorID] JOIN Competition AS c ON c.[ID] = cc.[CompetitionID]
и когда у вас есть новая конкуренция с новыми конкурентами, то вы просто проверьте, какие из них уже существуют в таблице конкурентов. Если они уже существуют, то вы не вставляете в конкурента для этих конкурентов и вставляете для новых.
затем вы вставляете новый конкурс в конкурс и, наконец, вы просто делаете все ссылки в CompetitionCompetitors.
вам нужно будет объединить таблицы вместе и получить список уникальных конкурентов, которые еще не существуют в
Competitors
.Это позволит вставить уникальные записи.
INSERT Competitors (cName) SELECT DISTINCT Name FROM CompResults cr LEFT JOIN Competitors c ON cr.Name = c.cName WHERE c.Name IS NULL
может наступить время, когда эту вставку нужно сделать быстро, не дожидаясь выбора уникальных имен. В этом случае вы можете вставить уникальные имена во временную таблицу, а затем использовать эту временную таблицу для вставки в вашу реальную таблицу. Это хорошо работает, потому что все обработка происходит во время вставки во временную таблицу, поэтому она не влияет на вашу реальную таблицу. Затем, когда вы закончите всю обработку, вы сделаете быструю вставку в реальную таблицу. Я мог бы даже обернуть последнюю часть, где вы вставляете в реальную таблицу, внутри транзакции.
нормализует ваш операционные столы как предложил Transact Charlie, это хорошая идея, и сэкономит много головных болей и проблем с течением времени - но есть такие вещи, как интерфейс таблицы, которые поддерживают интеграцию с внешними системами, и reporting таблицы, которые поддерживают такие вещи, как аналитическая обработка; и эти типы таблиц должны Не обязательно нормализоваться - в самом деле, очень часто это гораздо, гораздо удобнее и эффективнее для них не быть.
в этом случае я думаю, что предложение Transact Charlie для ваших операционных таблиц является хорошим.
но я бы добавил индекс (не обязательно уникальный) к CompetitorName в таблице Competitors для поддержки эффективных соединений на CompetitorName в целях интеграции (загрузка данных из внешних источников), и я бы поместил таблицу интерфейса в микс: Результаты соревнований.
CompetitionResults должны содержать любые данные, которые ваши результаты соревнований имеют в нем. Смысл интерфейсной таблицы, подобной этой, заключается в том, чтобы сделать ее как можно более быстрой и простой для усечения и перезагрузки с листа Excel или файла CSV или любой другой формы, в которой у вас есть эти данные.
эта таблица интерфейса не должна рассматриваться как часть нормализованного набора операционных таблиц. тогда вы можете присоединиться к CompetitionResults как Ричард предложил вставлять записи в конкурентов, которые еще не существуют, и обновлять те, которые существуют (например, если у вас действительно есть больше информации о конкурентах, например, их номер телефона или адрес электронной почты).
одно хотел бы отметить-на самом деле, имя конкурента, мне кажется, это очень вряд ли, чтобы быть уникальным в ваших данных. В 200 000 конкурентов, вы можете очень хорошо иметь 2 или более Дэвида Смита, например. Поэтому я бы рекомендовал вам собрать больше информация от конкурентов, например, их номер телефона или адрес электронной почты, или что-то, что, скорее всего, будет уникальным.
ваша операционная таблица, конкуренты, должна иметь только один столбец для каждого элемента данных, который вносит вклад в составной естественный ключ; например, он должен иметь один столбец для основного адреса электронной почты. Но таблица интерфейса должна иметь слот для старый и new значения для основной адрес электронной почты, так что старое значение может быть используется для поиска записи в конкурентах и обновления этой части до нового значения.
таким образом, результаты конкурса должны иметь некоторые" старые "и" новые " поля - oldEmail, newEmail, oldPhone, newPhone и т. д. Таким образом, вы можете сформировать составной ключ, в конкурентах, из имени конкурента, электронной почты и телефона.
затем, когда у вас есть некоторые результаты соревнований, вы можете усечь и перезагрузить таблицу CompetitionResults с вашего листа excel или все, что у вас есть, и запустить один, эффективная вставка для вставки всех новых конкурентов в таблицу конкурентов и одно эффективное обновление для обновления всей информации о существующих конкурентах из результатов конкурса. И вы можете сделать одну вставку, чтобы вставить новые строки в таблицу CompetitionCompetitors. Эти действия можно выполнить в хранимой процедуре ProcessCompetitionResults, которая может быть выполнена после загрузки таблицы CompetitionResults.
Это своего рода рудиментарное описание того, что Я видел это снова и снова в реальном мире с приложениями Oracle, SAP, PeopleSoft и списком других корпоративных программных пакетов.
последний комментарий, который я бы сделал, - это тот, который я сделал раньше: если вы создадите внешний ключ, который гарантирует, что конкурент существует в таблице конкурентов, прежде чем вы сможете добавить строку с этим конкурентом в нее для CompetitionCompetitors,убедитесь, что внешний ключ настроен на каскадное обновление и удаление. Таким образом, если вам нужно удалите конкурента, вы можете сделать это, и все строки, связанные с этим конкурентом, будут автоматически удалены. В противном случае, по умолчанию, внешний ключ потребует от вас удалить все связанные строки из CompetitionCompetitors, прежде чем он позволит вам удалить конкурента.
(некоторые люди думают, что не каскадные внешние ключи являются хорошей мерой предосторожности, но мой опыт заключается в том, что они просто долбаная боль в заднице, которая чаще всего является результатом надзор и они создают кучу работы для DBA. работа с людьми, случайно удаляющими вещи, - это то, почему у вас есть такие вещи, как диалоги "вы уверены" и различные типы регулярных резервных копий и избыточных источников данных. Это гораздо, гораздо чаще на самом деле хотят удалить конкурента, чьи данные все перепутались, например, чем случайно удалить один, а затем пойти "О нет! Я не хотел этого делать! А теперь у меня нет результатов их соревнований! ААА!"Последнее, безусловно, достаточно распространенный, поэтому вам нужно быть готовым к нему, но первый гораздо более распространен, поэтому самый простой и лучший способ подготовиться к первому, imo, - это просто сделать внешние ключи каскадными обновлениями и удалениями.)
ответы, выше которых говорят о нормализации, велики! Но что, если вы окажетесь в таком положении, как я, где вам не разрешено касаться схемы или структуры базы данных в ее нынешнем виде? Например, DBA - это "боги", и все предлагаемые изменения идут в /dev/null?
в этом отношении я чувствую себя так был дан ответ с этой публикацией переполнения стека тоже в отношении всех пользователей выше давать примеры кода.
Я перепечатываю код из ВСТАВИТЬ ЗНАЧЕНИЯ, ГДЕ НЕ СУЩЕСТВУЕТ что помогло мне больше всего, так как я не могу изменить какие-либо базовые таблицы базы данных:
INSERT INTO #table1 (Id, guidd, TimeAdded, ExtraData) SELECT Id, guidd, TimeAdded, ExtraData FROM #table2 WHERE NOT EXISTS (Select Id, guidd From #table1 WHERE #table1.id = #table2.id) ----------------------------------- MERGE #table1 as [Target] USING (select Id, guidd, TimeAdded, ExtraData from #table2) as [Source] (id, guidd, TimeAdded, ExtraData) on [Target].id =[Source].id WHEN NOT MATCHED THEN INSERT (id, guidd, TimeAdded, ExtraData) VALUES ([Source].id, [Source].guidd, [Source].TimeAdded, [Source].ExtraData); ------------------------------ INSERT INTO #table1 (id, guidd, TimeAdded, ExtraData) SELECT id, guidd, TimeAdded, ExtraData from #table2 EXCEPT SELECT id, guidd, TimeAdded, ExtraData from #table1 ------------------------------ INSERT INTO #table1 (id, guidd, TimeAdded, ExtraData) SELECT #table2.id, #table2.guidd, #table2.TimeAdded, #table2.ExtraData FROM #table2 LEFT JOIN #table1 on #table1.id = #table2.id WHERE #table1.id is null
приведенный выше код использует другие поля, чем то, что у вас есть, но вы получаете общую суть с различными методами.
обратите внимание, что в соответствии с оригинальный ответ на переполнение стека, этот код скопировать отсюда.
в любом случае моя точка зрения "лучшей практики" часто сводится к тому, что вы можете и не можете сделать как ну и теория.
- если вы можете нормализовать и генерировать индексы / ключи -- отлично!
- если нет, и у вас есть возможность использовать кодовые хаки, такие как я, надеюсь, что выше помогает.
удачи!
хорошо, это было задано 7 лет назад, но я думаю, что лучшее решение здесь-полностью отказаться от новой таблицы и просто сделать это как пользовательское представление. Таким образом, вы не дублируете данные, не беспокойтесь об уникальных данных, и это не касается фактической структуры базы данных. Что-то вроде этого:
CREATE VIEW vw_competitions AS SELECT Id int CompetitionName nvarchar(75) CompetitionType nvarchar(50) OtherField1 int OtherField2 nvarchar(64) --add the fields you want viewed from the Competition table FROM Competitions GO
другие элементы могут быть добавлены здесь, как соединение с другими таблицами, где положения и т. д. Это, скорее всего, самое элегантное решение этой проблемы, так как теперь вы можете просто запросить вид:
SELECT * FROM vw_competitions
...и добавьте любые предложения WHERE, IN или EXISTS в запрос представления.