T-SQL — Вставить строку в таблицу, только если она еще не существует

#sql-server #tsql

#sql-сервер #tsql

Вопрос:

У меня есть T-SQL, показанный ниже. @Results — это табличная переменная, а ‘CTE’ — обычное табличное выражение. Я хочу вставлять строки в таблицу @Results только в том случае, если идентификатор SubID, который я собираюсь вставить, еще не был вставлен в таблицу. Приведенный ниже код не выполняет эту работу, и я не уверен, почему. Кто-нибудь видит проблему?

 Insert Into @Results (
    Cell, 
    CellSettings, 
    RecipeInstanceId, 
    BiasStartTime, 
    SubId 
    RuleInstanceId)
Select 
    Cell, 
    CellSettings, 
    RecipeInstanceId, 
    BiasStartTime, 
    SubId, 
    RuleInstanceId
From CTE
Where CTE.SubId NOT IN (Select SubId From @Results)
  

Комментарии:

1. в чем проблема? Сообщение об ошибке?

2. возможно, вам также придется опубликовать cte — или даже лучше: сведите его к демонстрации меньшего размера. Возможно, ваша проблема даже решится сама собой, если вы сделаете это … 😉

3. Получаете ли вы сообщение об ошибке при добавлении дубликата?

4. Приведенный вами код выполняет работу точно так, как указано. Я могу только предположить, что ваш вопрос связан с ошибками PK или последующим поиском дубликатов. ПРИМЕЧАНИЕ: Дубликаты вызваны дубликатами в исходном CTE , а не ошибкой в предложении WHERE. Возможно, вы ожидали, что это устранит дубликаты, проверив соответствие более поздних строк строкам, добавленным ранее во вставке…. Это не так работает SQL. Вся вставка вычисляется как одна операция. Значения, которые уже есть в Results начале , не будут вставлены; но в противном случае все 5 строк с одинаковыми значениями будут вставлены. ………. SubId

Ответ №1:

Сначала вам нужно проверить наличие:

 IF NOT EXISTS(SELECT * FROM @Results WHERE SubId = .......)
   INSERT INTO @Results (Cell, CellSettings, RecipeInstanceId, 
                          BiasStartTime, SubId, RuleInstanceId)
     SELECT 
         Cell, CellSettings, RecipeInstanceId, 
         BiasStartTime, SubId, RuleInstanceId
     FROM CTE
  

Возможно, вы могли бы включить это требование (возвращать только те строки, которые еще не существуют) в свой CTE, чтобы вам не пришлось снова фильтровать выходные данные из CTE…

Комментарии:

1. Не лучше ли использовать соединение?

2. @Hogan: нет — почему? Как вы думаете, что вы можете получить от использования JOIN вместо этого??

3. потому что эта проверка должна выполняться для каждой строки (нового выбора), где соединение оптимизировано в SQL для быстрой работы с наборами даты. Это разница между O (n) оптимизированным и O (1) O (1) … O (1) n раз. (Смотрите мой ответ для примера использования объединения.)

4. @Hogan: Я бы подумал, что лучшим подходом было бы отфильтровать те строки, которые уже существуют в целевой таблице в CTE, и не возвращать их вообще.

Ответ №2:

Я бы сделал это следующим образом (предположение — у вас нет дублирующегося SubID в вашем CTE, то есть вы вставляете SubID из X, а затем позже в этом же запросе вставляете тот же самый.)

 WITH CTE AS
( 
  blah
), CTENEW AS
(
   SELECT CTE.* 
   FROM CTE
   LEFT JOIN @Results R ON CTE.SubID = R.SubID
   WHERE R.SubID IS NULL
)
Insert Into @Results (
    Cell, 
    CellSettings, 
    RecipeInstanceId, 
    BiasStartTime, 
    SubId 
    RuleInstanceId)
Select 
    Cell, 
    CellSettings, 
    RecipeInstanceId, 
    BiasStartTime, 
    SubId, 
    RuleInstanceId
From CTENEW
  

Или вы могли бы перенести соединение, которое я сделал, в свой CTE.

Ответ №3:

Попробуйте использовать предложение `except:

 insert MyTable(c1, c2, c3)

select ot.c1, ot.c2, ot.c3
from OtherTable ot

except

select mt.c1, mt.c2, mt.c3
from MyTable
  

Ответ №4:

Проверьте, существует запись или нет, используя «Exists»

 If Not Exists(Select SubId From @Results)
    Insert Into @Results (
        Cell, 
        CellSettings, 
        RecipeInstanceId, 
        BiasStartTime, 
        SubId 
        RuleInstanceId)
    Select 
        Cell, 
        CellSettings, 
        RecipeInstanceId, 
        BiasStartTime, 
        SubId, 
        RuleInstanceId
    From CTE
    Where CTE.SubId NOT IN (Select SubId From @Results)