SQL Server: как вы вставляете строки, где некоторые строки могут иметь дубликаты ключей? [Дубликат]

Симон Моурир дал этот пример :

object o = null;
DateTime d = (DateTime)o;  // NullReferenceException

, где unboxing преобразование (литье) из object (или из одного из классов System.ValueType или System.Enum или из типа интерфейса) - тип значения (кроме Nullable<>) сам по себе дает NullReferenceException.

В другом направлении конверсия бокса из a Nullable<>, которая имеет HasValue, равную false , на ссылочный тип, может дать ссылку null, которая затем может привести к NullReferenceException. Классический пример:

DateTime? d = null;
var s = d.ToString();  // OK, no exception (no boxing), returns ""
var t = d.GetType();   // Bang! d is boxed, NullReferenceException

Иногда бокс происходит по-другому. Например, с помощью этого не общего метода расширения:

public static void MyExtension(this object x)
{
  x.ToString();
}

следующий код будет проблематичным:

DateTime? d = null;
d.MyExtension();  // Leads to boxing, NullReferenceException occurs inside the body of the called method, not here.

Эти случаи возникают из-за специальных правил, используемых во время выполнения при боксе Nullable<> экземпляров.

132
задан DaNet 21 March 2012 в 22:23
поделиться

7 ответов

Семантически вы спрашиваете «вставить конкурентов, где их еще нет»:

INSERT Competitors (cName)
SELECT DISTINCT Name
FROM CompResults cr
WHERE
   NOT EXISTS (SELECT * FROM Competitors c
              WHERE cr.Name = c.cName)
182
ответ дан gbn 3 September 2018 в 14:43
поделиться

Вам нужно будет объединить таблицы и получить список уникальных конкурентов, которые еще не существуют в Competitors.

Это вставляет уникальные записи.

INSERT Competitors (cName) 
SELECT DISTINCT Name
FROM CompResults cr LEFT JOIN Competitors c ON cr.Name = c.cName
WHERE c.Name IS NULL

Возможно, наступит время, когда эту вставку нужно выполнить быстро, не дожидаясь выбора уникальных имен. В этом случае вы можете вставить уникальные имена во временную таблицу, а затем использовать эту временную таблицу для вставки в свою реальную таблицу. Это хорошо работает, потому что вся обработка происходит во время вставки во временную таблицу, поэтому она не влияет на вашу реальную таблицу. Затем, когда вы закончите обработку, вы быстро вставляете ее в реальную таблицу. Я мог бы даже обернуть последнюю часть, где вы вставляете в реальную таблицу внутри транзакции.

10
ответ дан Andriy M 3 September 2018 в 14:43
поделиться

Хорошо, это было спрошено 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

Здесь могут быть добавлены другие элементы, такие как объединения в другие таблицы, предложения WHERE и т. Д. Это наиболее вероятно самое изящное решение этой проблемы, так как теперь вы можете просто запросить view:

SELECT *
FROM vw_competitions

... и добавить в запрос вида любые предложения WHERE, IN или EXISTS.

0
ответ дан Beervenger 3 September 2018 в 14:43
поделиться

Еще один вариант - оставить в левой части таблицы результатов с существующими конкурентами. Таблицу и найти новых конкурентов путем фильтрации отдельных записей, которые не соответствуют 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 также предлагает компактный, элегантный и эффективный способ сделать это:

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);
48
ответ дан Chris Schiffhauer 3 September 2018 в 14:43
поделиться

Нормализация ваших операционных таблиц , как это предлагает Transact Charlie, является хорошей идеей и с течением времени сэкономит много головных болей и проблем, но есть такие вещи, как таблицы interface , которые поддерживают интеграцию с внешними системами и отчетности таблиц, которые поддерживают такие вещи, как аналитическая обработка; и эти типы таблиц должны не обязательно быть нормализованы - на самом деле очень часто намного, гораздо удобнее и результативнее для них не быть .

В этом случае я думаю, что предложение Transact Charlie для ваших рабочих таблиц является хорошим.

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

Результаты конкурса должны содержать любые данные, полученные в результате ваших результатов. Точка таблицы интерфейса, подобная этой, заключается в том, чтобы сделать ее максимально быстрой и легкой, чтобы усечь ее и перезагрузить ее с листа Excel или файла CSV или любой другой формы, в которой вы располагаете.

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

Одна вещь, которую я хотел бы отметить - на самом деле, мне кажется, имя конкурента очень маловероятно, чтобы быть уникальным в ваших данных . Например, у 200 000 конкурентов у вас может быть 2 или более Дэвида Смита. Поэтому я бы рекомендовал вам собрать больше информации от конкурентов, таких как их номер телефона или адрес электронной почты, или нечто более вероятное, чтобы быть уникальным.

В вашей рабочей таблице, конкурентам, должна быть только одна колонка для каждого элемента данных, который вносит вклад в составной естественный ключ; например, он должен иметь один столбец для основного адреса электронной почты. Но таблица интерфейса должна иметь слот для старых и новых значений для основного адреса электронной почты, так что старое значение может использоваться для поиска записи в Конкурентах и ​​обновления эта часть его к новому значению.

Итак, у соревнований должны быть некоторые «старые» и «новые» поля - oldEmail, newEmail, oldPhone, newPhone и т. д. Таким образом, вы можете сформировать составной ключ в Конкуренты, от имени конкурента, электронной почты и телефона.

Затем, когда у вас есть некоторые результаты конкурса, вы можете усекать и перезагружать таблицу CompetitionResults из своего листа excel или любого другого, что у вас есть, и запускать единую эффективную вставку вставьте всех новых конкурентов в таблицу конкурентов и однократное эффективное обновление, чтобы обновить всю информацию о существующих конкурентах от результатов конкурса. И вы можете сделать одну вставку для вставки новых строк в таблицу CompetitionCompetitors. Эти вещи могут быть выполнены в хранимой процедуре ProcessCompetitionResults, которая может быть выполнена после загрузки таблицы CompetitionResults.

Это своего рода рудиментарное описание того, что я видел много раз в реальном мире с Oracle Приложения, SAP, PeopleSoft и список стилей других пакетов корпоративного программного обеспечения.

Один последний комментарий, который я сделал бы, - это тот, который я сделал ранее в SO: Если вы создаете внешний ключ, который гарантирует, что Конкурент существует в таблице «Участники», прежде чем вы сможете добавить к нему конкуренцию в CompetCitors, , чтобы убедиться, что для внешнего ключа установлено каскадное обновление и удаляется . Таким образом, если вам нужно удалить участника, вы можете это сделать, и все строки, связанные с этим конкурентом, автоматически удаляются. В противном случае, по умолчанию, внешний ключ потребует, чтобы вы удалили все связанные строки из CompetCompetitors, прежде чем он позволит вам удалить конкурента.

(Некоторые люди считают, что некаскадные внешние ключи являются хорошей мерой предосторожности, но мой опыт заключается в том, что они всего лишь огромная боль в прикладе, которые чаще всего являются результатом недосмотра и они создают кучу работы для DBA. Работа с людьми, случайно удаляющими вещи, - это то, почему у вас есть такие вещи, как «вы уверены», диалоги и различные типы регулярных резервных копий и избыточных источников данных. Далеко, гораздо чаще встречается желание удалить конкурент, чьи данные все испорчены, например, чтобы случайно удалить его, а затем пойти «О нет! Я не хотел этого делать! И теперь у меня нет результатов конкурса! Аааах!» последнее, конечно, достаточно распространено, поэтому вам нужно быть готовым к этому, но первый гораздо более распространен, поэтому самый простой и лучший способ подготовки к первому, imo - просто сделать каскадные обновления и удалять внешние ключи. )

3
ответ дан Shavais 3 September 2018 в 14:43
поделиться

Ответы выше, которые говорят о нормализации, велики! Но что, если вы окажетесь в такой позиции, как я, где вам не разрешено касаться схемы или структуры базы данных в ее нынешнем виде? Например, DBA - это «боги», и все предлагаемые изменения идут в / dev / null?

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

Я пересказываю код из INSERT VALUES WHERE NOT EXISTS , который помог мне больше всего, так как я не могу изменить какие-либо базовые таблицы базы данных:

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

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

Обратите внимание, что в соответствии с исходным ответом на Stack Overflow этот код был скопирован из здесь .

В любом случае моя точка зрения - это «лучшая практика», которая часто сводится к тому, что вы можете и не можете делать, а также теории.

  • Если вы можете нормализовать и генерировать индексы / ключи - отлично!
  • Если нет, и у вас есть возможность использовать такие хаки, как я, надеюсь, что это помогает.

Удачи!

0
ответ дан Thrawn Wannabe 3 September 2018 в 14:43
поделиться

Не знаю, почему кто-то еще этого не сказал,

НОРМАЛИЗИРОВАТЬ.

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

У вас должны быть следующие таблицы .....

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 - похоже, нет хорошего NATURAL KEY, который бы соответствовал модели, поэтому Я думаю, что SURROGATE KEY хорошо подходит здесь.

Итак, если бы у вас было это, чтобы получить отдельный список конкурентов в конкретном соревновании, вы можете сделать такой запрос:

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.

30
ответ дан Transact Charlie 3 September 2018 в 14:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: