2015-06-22 5 views
0

Я пытаюсь написать хранимую процедуру, которая вставляет данные, но с некоторыми довольно простыми проверками, которые выглядят как хорошая практика.Проверка дубликатов при вставке хранимой процедуры

В таблице в настоящее время имеет 300 столбцов, из которых Eсть последовательный primary_key_id, столбец, что мы хотим, чтобы проверить, прежде чем вставить, скажем address, child_of столбец используется, когда есть новые данные (то, что мы вставляем), а затем Остальные 297 столбцов.

Так скажем, таблица в настоящее время выглядит следующим образом:

---------------------------------------------------------------------- 
|PK |Address    |child_of |other_attr_1|other_attr2|... 
---------------------------------------------------------------------- 
|1  | 123 Main St  |NULL  |...   |...  |... 
|2  | 234 South Rd  |NULL  |...   |...  |... 
|3  | 345 West Rd  |NULL  |...   |...  |... 
---------------------------------------------------------------------- 

и мы хотим, чтобы добавить эту строку, где адрес имеет новый атрибут new в other_attr_1 колонки. Мы использовали бы child_of для ссылки на primary_key_id предыдущей записи строки. Это позволит создать базовую историю (надеюсь).

|4  | 123 Main St  |1   |new   |...  |... 

Как проверить дублирование хранимой процедуры? Я перебираю каждый входной параметр с тем, что уже находится в БД, если оно есть?

Вот код, у меня до сих пор:

USE [databaseINeed] 
-- SET some_stuff ON --or off :) 
-- .... 
-- GO 
CREATE Procedure [dbo].[insertNonDuplicatedData] 
    @address text, @other_attr_1 numeric = NULL, @other_attr_2 numeric = NULL, @other_attr_3 numeric = NULL,....; 
AS 
BEGIN TRY 
    -- If the address already exists, lets check for updated data 
    IF EXISTS (SELECT 1 FROM tableName WHERE address = @address) 
    BEGIN 
     -- Look at the incoming data vs the data already in the record 

     --HERE IS WHERE I THINK THE CODE SHOULD GO, WITH SOMETHING LIKE the following pseudocode: 
     if any attribute parameter values is different than what is already stored 
     then Insert into tableName (address, child_of, attrs) Values (@address, THE_PRIMARY_KEY_OF_THE_RECORD_THAT_SHARES_THE_ADDRESS, @other_attrs...)  

     RETURN 
    END  
    -- We don't have any data like this, so lets create a new record altogther 
    ELSE 
    BEGIN 
     -- Every time a SQL statement is executed it returns the number of rows that were affected. By using "SET NOCOUNT ON" within your stored procedure you can shut off these messages and reduce some of the traffic. 
     SET NOCOUNT ON 
     INSERT INTO tableName (address, other_attr_1, other_attr_2, other_attr_3, ...) 
     VALUES(@address,@other_attr_1,@other_attr_2,@other_attr_3,...) 
    END 
END TRY 
BEGIN CATCH 
    ... 
END CATCH 

Я попытался добавить CONSTRAINT на самой таблице для всех 297 атрибутов, которые должны быть уникальными при проверке против address колонн через:

ALTER TABLE tableName ADD CONSTRAINT 
    uniqueAddressAttributes UNIQUE -- tried also with NONCLUSTERED 
    (other_attr_1,other_attr_2,...) 

, но я получаю сообщение об ошибке

ERROR: cannot use more than 32 columns in an index SQL state: 54011

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

+1

Один из осложнения состоят в том, что таблица имеет 297 столбцов. Трудно понять, почему можно построить таблицу с таким количеством столбцов, а не нормализовать ее не только для производительности, но и для упрощения работы с данными. Я бы настоятельно рекомендовал вам создать резервную копию в вашем дизайне. Конечно, я не подвергаюсь вашим требованиям. – CLaFarge

+1

Я этого не делал! Я унаследовал это. Но я пытаюсь работать с ним медленно, но верно :) –

ответ

2

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

-- I assume you get the last id to set the 
-- THE_PRIMARY_KEY_OF_THE_RECORD_THAT_SHARES_THE_ADDRESS 
DECLARE @PK int = (SELECT MAX(PK) FROM tableName WHERE address = @address) 

-- No need for an EXISTS(), just check the @PK 
IF @PK IS NOT NULL 
BEGIN 

    IF EXISTS(
     -- List of attributes from table 
     -- Possibly very poor performance to get the row by ntext 
     SELECT other_attr_1, other_attr_2 ... FROM tableName WHERE PK = @PK 
     INTERSECT 
     -- List of attributes from variables 
     SELECT @other_attr_1, @other_attr_2 ... 
    ) 
    BEGIN 
     Insert into tableName (address, child_of, attrs) Values 
     (@address, @PK, @other_attr_1, @other_attr_2 ...) 
    END 

END 
+0

Хм, ты поможешь мне немного больше. Я не передаю '@ PK' в качестве параметра или сравнивая его, поэтому два сравнения вариантов для оператора TRANSFORM не будут работать. Первый выбор может быть «SELECT 1 FROM @table_name WHERE address = @ address», потому что я передаю @address. Но мне нужно получить 'pk' из этой строки. Должен ли я добавить «AND PK = @ PK»? Второй выбор использует '@ pk', это первый выбор? Не могли бы вы предоставить еще несколько встроенных примечаний относительно того, что это на самом деле делает и подход? Я становлюсь ближе каждый раз, когда читаю, просто нужна дополнительная помощь. –

+0

@chrisFrisina См. Редактирование. Вы должны отредактировать способ получения PK для вставки. – Eric

0

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

Возможно, вам понадобилось бы сделать некоторое преобразование данных, чтобы сделать ваши 300ish столбцы всеми nvarchar, чтобы они могли быть объединены для ввода в функцию HASHBYTES. Кроме того, если какой-либо из столбцов может быть NULL, вам придется подумать о том, как их обрабатывать. Например, если существующая запись имеет поле 216, установленное в NULL, и строка, пытающаяся быть добавленной, точно такая же, за исключением поля 216 является пустой строкой, является ли это совпадением?

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

Что все сказано, ваша архитектура действительно требует этой 300-мм структуры столбцов? Если бы вы могли уйти от этого, мне не было бы здесь так креативно.

0

У меня недостаточно комментариев для комментариев, поэтому вместо этого я отправляю ответы.

SQL Эрика должна быть изменена с IF EXISTS на IF NOT EXISTS

Я считаю, что желаемая логика должна быть:

  1. Если существующий адрес записи, проверьте, какие атрибуты различны.
  2. Если какие-либо атрибуты различны, вставить новый адрес записи, хранения первичного ключа последней существующей записи адреса в child_of колонке

SQL рефакторинга Криса & Эрика:

USE [databaseINeed] 
-- SET some_stuff ON --or off :) 
-- .... 
-- GO 
CREATE Procedure [dbo].[insertNonDuplicatedData] 
    @address text, @other_attr_1 numeric = NULL, @other_attr_2 numeric = NULL, @other_attr_3 numeric = NULL,....; 
AS 
BEGIN TRY 
    -- If the address already exists, lets check for updated data 
    IF EXISTS (SELECT 1 FROM tableName WHERE address = @address) 
    BEGIN 
     -- Look at the incoming data vs the data already in the record 

     --HERE IS WHERE I THINK THE CODE SHOULD GO, WITH SOMETHING LIKE the following pseudocode: 

     DECLARE @PK int = (SELECT MAX(PK) FROM tableName WHERE address = @address) 
     IF NOT EXISTS(
      -- List of attributes from table 
      -- Possibly very poor performance to get the row by ntext 
      SELECT other_attr_1, other_attr_2 ... FROM tableName WHERE PK = @PK 
      INTERSECT 
      -- List of attributes from variables 
      SELECT @other_attr_1, @other_attr_2 ... 
     ) 
     BEGIN 
      -- @simplyink: existing address record has different combination of (297 column) attribute values 
      --   at least one attribute column is different (no intersection) 
      Insert into tableName (address, child_of, attrs) Values 
      (@address, @PK, @other_attr_1, @other_attr_2 ...) 
     END 


     RETURN 
    END  
    -- We don't have any data like this, so lets create a new record altogther 
    ELSE 
    BEGIN 
     -- Every time a SQL statement is executed it returns the number of rows that were affected. By using "SET NOCOUNT ON" within your stored procedure you can shut off these messages and reduce some of the traffic. 
     SET NOCOUNT ON 
     INSERT INTO tableName (address, other_attr_1, other_attr_2, other_attr_3, ...) 
     VALUES(@address,@other_attr_1,@other_attr_2,@other_attr_3,...) 
    END 
END TRY 
BEGIN CATCH 
    ... 
END CATCH 
Смежные вопросы