T-SQL запускает ошибку “Имя столбца или количество предоставленных значений не соответствует определению таблицы”

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

  •  01-07-2019
  •  | 
  •  

Вопрос

Вот кое-что, что я не смог исправить, и я посмотрел повсюду.Возможно, кто-нибудь здесь знает!

У меня есть таблица под названием dandb_raw, состоящая, в частности, из трех столбцов:dunsId (PK), имя и имя для поиска.У меня также есть триггер, который действует на эту таблицу:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER TRIGGER [dandb_raw_searchNames]
    ON [dandb_raw]
    FOR INSERT, UPDATE
    AS

SET NOCOUNT ON

  select dunsId, name into #magic from inserted

        UPDATE dandb
            SET dandb.searchName = company_generateSearchName(dandb.name)
            FROM (select dunsId, name from #magic) i
            INNER JOIN dandb_raw dandb
                on i.dunsId = dandb.dunsId


        --Add new search matches
        SELECT c.companyId, dandb.dunsId
            INTO #newMatches
            FROM dandb_raw dandb
            INNER JOIN (select dunsId, name from #magic) a
                on a.dunsId = dandb.dunsId
            INNER JOIN companies c
                ON dandb.searchName = c.searchBrand
                --avoid url matches that are potentially wrong
                AND (lower(dandb.url) = lower(c.url)
                    OR dandb.url = ''
                    OR c.url = ''
                    OR c.url is null)


        INSERT INTO #newMatches (companyId, dunsId)
        SELECT c.companyId, max(dandb.dunsId) dunsId
            FROM dandb_raw dandb
            INNER JOIN
                (
                    select
                    case when charindex('/',url) <> 0 then left(url, charindex('/',url)-1)
                    else url
                    end urlMatch, * from companies
                ) c
                ON dandb.url = c.urlMatch
            where subsidiaryOf = 1 and isReported = 1 and dandb.url <> ''
                and c.companyId not in (select companyId from #newMatches)
            group by companyId
            having count(dandb.dunsId) = 1

        UPDATE cd
            SET cd.dunsId = nm.dunsId
            FROM companies_dandb cd
            INNER JOIN #newMatches nm
                ON cd.companyId = nm.companyId
GO

Триггер приводит к сбою вставки:

insert into  [dandb_raw](dunsId, name)
    select 3442355, 'harper'
    union all
    select 34425355, 'har 466per'
update [dandb_raw] set name ='grap6767e'

С этой ошибкой:

Msg 213, Level 16, State 1, Procedure companies_contactInfo_updateTerritories, Line 20
Insert Error: Column name or number of supplied values does not match table definition.

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

Итак, что же приводит к сбою триггера?Как это можно остановить?

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

Решение

Я думаю, что Дэвид и Черво вместе взятые справились с этой проблемой.

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

В результате он попытается найти таблицу, которая уже существовала с другой схемой, умрет и выдаст приведенное выше сообщение.Одно доказательство, которое говорило бы в пользу:Использует ли inserted область действия в стиле стека (вложенные триггеры имеют свои собственные inserteds?)

Тем не менее, я все еще строю предположения - по крайней мере, сейчас, кажется, все работает!

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

Что такое companies_contactInfo_updateTerritories?В фактической ссылке упоминается процедура "companies_contactInfo_updateTerrorities", но я не вижу ее в приведенном коде.Также я не вижу, где это вызывается.Если только это не из вашего приложения, которое вызывает SQL и, следовательно, не имеет значения....

Если вы все протестировали, и это сработало, но теперь это не работает, значит, что-то должно быть по-другому.Одна вещь, которую следует учитывать, - это безопасность.Я заметил, что вы просто вызываете таблицу [dandb_raw], а не [dbo].[dandb_raw].Таким образом, если бы у пользователя была таблица с тем же именем [user].[dandb_raw], эта таблица использовалась бы для проверки определений вместо вашей таблицы.Кроме того, триггер создает временные таблицы.Но если некоторые из временных таблиц уже существовали по какой-либо причине, но с другими определениями, это также может быть проблемой.

Я не вижу никакой очевидной проблемы в коде.

- ВЫБИРАЙ ..INTO" - это слабое кунг-фу.Попробуйте явно создать определение временной таблицы:

CREATE TABLE #newMatches
(
  CompanyID int PRIMARY KEY,
  DunsID int
)

Когда вы закончите с #newMatches, вам следует избавиться от него, чтобы позже вы могли создать его снова (временные таблицы ограничены областью подключения !!)

DROP TABLE #newMatches

Код триггера (поскольку он должен выполняться каждый раз при обновлении данных) должен быть эффективным и учитывать несколько вставок записей.Вы преуспели во втором, но не в первом.Вы сделали это чрезмерно сложным и использовали такие вещи, как Not в операторах, которые обычно менее эффективны, чем использование left join.Временные таблицы здесь не нужны (я бы никогда не стал рассматривать возможность их использования в триггере), поскольку они усиливают неэффективность триггера.Нет причин не писать From вставлено i вместо FROM (выберите dunsId, имя из #magic) i

Первый, скорее всего, будет быстрее и проще в чтении и обслуживании.

Здесь:ПРИСОЕДИНИТЬСЯ (выберите случай, когда charindex('/',URL) <> 0 затем осталось(url, charindex('/',url)-1) еще URL end urlMatch, * от компаний ) c НА dandb.url = c.urlMatch

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

Также в целом я бы избегал использования select *, но особенно в триггере.Предположим, вы выполняете вставку в другую таблицу и использовали select * из какой-либо таблицы, присоединенной к inserted или deleted.Добавление столбца в эту таблицу приведет к сбою триггера и остановит все изменения данных до тех пор, пока это не будет исправлено.

Вы также использовали функцию в триггере.Это может быть мучительно медленным, если у вас большая вставка.Я предлагаю вам протестировать это, обновив большую группу записей, и посмотреть, что произойдет.Все изменения данных происходят не только из пользовательского интерфейса, по одной записи за раз.Будут случаи, когда одно поле обновляется из специального запроса в management studio (когда все цены необходимо скорректировать на 10%, как самый простой пример, который приходит на ум). Ваш триггер должен быть способен обрабатывать эти типы обновлений if, а также те, которые вы ожидаете.Я бы запустил тестовый пример обновления 100000 строк и посмотрел, насколько этот триггер замедляет работу.

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

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