none
Автоматическое создание ключевого поля RRS feed

  • Вопрос

  • Здравствуйте!

    Есть таблица, в ней есть ключевое поле, которое должно автоматически генерироваться при создании записи программы из c# в следующем виде:

    хххх_хххххх, где хххх - год, который вводится в программе на c#, а хххххх - номер записи начинается с 000001. Т.е. в таблице будут такие значения у записей ключевого поля: 2010_000001, 2010_000002 ..., 2009_000001...

    Ключевое поле сформировать удается, а вот проверку или что-то другое, чтобы при добавлении, например записи связанной с 2010 и имеющей последний номер например 000098, следующей добавлялось 2010_000099?


    Спасибо!
    • Перемещено Siddharth Chavan 2 октября 2010 г. 0:29 MSDN Forums Consolidation (От:SQL Server для разработчиков)

Ответы

  • Ну тогда, либо как я предложил сохранять в базе ещё и дату (не обязательно в datetime, можете хранить просто год в виде строки) + IDENTITY поле + Calculated поле. Либо пишите ваш инсерт в хранимке, обрачиваете код, выбирающий поледнее занчение, формирующий новое для вставки (хххх_хххххх строка, так что придётся парсить, чтобы нарастить на единицу)  и вставляющий данные в одну транзакцию и ставите блокировку чтобы не получилось неприятной ситуации при одновременном редактировании данных. Блокировка здесь нужна эксклюзивная (если один пользователь работает с данными, другой в это время ждёт снятия блокировки, чтобы прочитать последнее значение), устанавлиается либо при помощи SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, либо при помощи хинта XLOCK. Я больше склоняюсь к первому варианту, но выбирать вам. К тому, я думаю можно ещё придумать какие-нибудь варианты решения проблемы.

    PS: если запрос выполняется с хинтом NOLOCK, то блокировка не поможет. 

    • Помечено в качестве ответа I.Vorontsov 6 мая 2010 г. 5:47

Все ответы

  • Делать можно по разному. Но сперва следует ответить на некоторые вопросы:

    Важна ли непрерывность? Или достаточно монотонности? Возможны ли удаления уже вставленных записей? Если да, то надо ли пытаться заполнять возникающие "дырки" в нумерации при последующих вставках? Или при удалении надо восстанавливать непрерывность "сдвигом" существующих записей "вниз"?

    Нужно ли знать получившееся значение в программе на C# до фактической вставки? Или достаточно получить его последующим запросом?

  • Важна ли непрерывность? Или достаточно монотонности? Возможны ли удаления уже вставленных записей? Если да, то надо ли пытаться заполнять возникающие "дырки" в нумерации при последующих вставках? Или при удалении надо восстанавливать непрерывность "сдвигом" существующих записей "вниз"?

    Нужно ли знать получившееся значение в программе на C# до фактической вставки? Или достаточно получить его последующим запросом?


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

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


    Спасибо!
  • Ещё нужно подумать о варианте, когда несколько пользователей меняют данные, если будете делать проверку, то с этим могут быть проблемы. Может быть вот такой вариант вам подойдёт: делаете ключевое поле IDENTITY, а ваше 2010_000001 реализовываете как Calculated. Потом можете его использовать в своей программе, и таким образом избавляетесь от проверок.

  • Заведите новую таблицу с актуальными значениями нумератора для каждого года. При вставке проверяйте наличие записи для указанного года и инкрементом текущего значения нумератора получайте искомый номер. Не забудьте занести в нумератор новое текущее значение. При отсутствии записей для нужного года, выполните вставку единицы.

    Делать это всё можно в программе на c#, after или instead of триггере на вставку в таблицу или в хранимой процедуре, вызываемой из программы на C# для вставки новой записи.

    • Предложено в качестве ответа Naomi N 5 мая 2010 г. 19:07
  • Ещё нужно подумать о варианте, когда несколько пользователей меняют данные, если будете делать проверку, то с этим могут быть проблемы. Может быть вот такой вариант вам подойдёт: делаете ключевое поле IDENTITY, а ваше 2010_000001 реализовываете как Calculated. Потом можете его использовать в своей программе, и таким образом избавляетесь от проверок.


    если не тяжело, не могли бы вы привести небольшой пример
    Спасибо!
  • Примерно так: Два пользователя одновременно добавляют данные. Допустим последнее значение в базе у вас 2010_000001. Оба делают проверку, и понимают, что им нужно вставлять 2010_000002. Потом, кто-то всё равно первым вставляет запись 2010_000002, а второй, попытавшись вставить такую же, получает ошибку дубликата первичного ключа.

  • Всё это разруливается. Хоть чтением с updlock, к примеру. Деваться то некуда - нумератор является общим ресурсом.

    Если же обсуждаемое поле вообще ни для чего не нужно, кроме визуализации, то его вообще можно считать во view при помощи row_number. Но нам не хотят рассказывать, зачем нужно это поле. :) Будут ли, к примеру, по нему искать и/или сортировать?

    • Предложено в качестве ответа Naomi N 5 мая 2010 г. 19:08
  • это поле является идентификатором человека, ктороый состоит из года принятия его и номера в этом году. Пока никак действий сним не предусмотрено


    Спасибо!
  • я немного не пойму как сдлеать поле одновременно INDENTITY и CALCULATED. Приращение у меня будет 1, а вот дальше. Не могу построить sql запрос
    Спасибо!
  • Нет, вы меня неправильно поняли. Одно поле IDENTITY - это ваш первичный ключ (назовём его Id). Второе Calculated. Выглядеть оно будет примерно так (HireDate - дата принятия на работу, которую вы тоже вставляете в таблицу):

    AltKey AS DATENAME(year, HireDate) + '_' + CONVERT(varchar, Id)
    
    • Предложено в качестве ответа Naomi N 5 мая 2010 г. 19:08
  • вы немного не правильно меня поняли или я упустил это. Дата не хранится в таблице! Она вводится пользователем в программе в текстовое поле TextBox и потом ее значение просто берется для составления ID. Как в таком слечае быть?


    Спасибо!
  • Хм... А почему вы не хотите её хранить в базе? Ведь даже в вашем примере в Id подставляется только год. А сама дата приёма на работу? Это ведь полезная информация, как мне кажется.

  • просто по заданию так, как писал в самом начале. Одно поле с форматом хххх_хххххх


    Спасибо!
  • Ну тогда, либо как я предложил сохранять в базе ещё и дату (не обязательно в datetime, можете хранить просто год в виде строки) + IDENTITY поле + Calculated поле. Либо пишите ваш инсерт в хранимке, обрачиваете код, выбирающий поледнее занчение, формирующий новое для вставки (хххх_хххххх строка, так что придётся парсить, чтобы нарастить на единицу)  и вставляющий данные в одну транзакцию и ставите блокировку чтобы не получилось неприятной ситуации при одновременном редактировании данных. Блокировка здесь нужна эксклюзивная (если один пользователь работает с данными, другой в это время ждёт снятия блокировки, чтобы прочитать последнее значение), устанавлиается либо при помощи SET TRANSACTION ISOLATION LEVEL SERIALIZABLE, либо при помощи хинта XLOCK. Я больше склоняюсь к первому варианту, но выбирать вам. К тому, я думаю можно ещё придумать какие-нибудь варианты решения проблемы.

    PS: если запрос выполняется с хинтом NOLOCK, то блокировка не поможет. 

    • Помечено в качестве ответа I.Vorontsov 6 мая 2010 г. 5:47
  • V temu?

    http://forums.asp.net/p/1550632/3801444.aspx#3801444


    Premature optimization is the root of all evil in programming. (c) by Donald Knuth

    Naomi Nosonovsky, Sr. Programmer-Analyst

    My blog