Каскадне оновлення пов’язаних списків

В опублікованій раніше статті “Довідники” крім опису процесу і короткої теорії створення довідкових форм і таблиць також наводився приклад використання функції AppendLookupTable, Що дозволяє додавати відсутнє значення в список. Додавання відбувалося автоматично після прямого введення потрібного значення у список. Однак ця функція дозволяла додавати дані тільки в одиночний список. На практиці ж досить часто буває необхідність подібного автозаповнення, але вже пов’язаних (залежних) списків. Про варіанті рішення – доопрацюванні функції AppendLookupTable і піде мова в цій статті.

Для розгляду прикладу створимо три довідкових таблиці:





















































Ім’я таблиці

Ім’я поля

Тип поля

Зв’язана таблиця

Поле в пов’язаної таблиці

Країни

id_Страни

Лічильник



Країна

Текст



Виробники

id_Компаніі

Лічильник



id_страни

Числове (довге ціле)

Країни

id_Страни

Компанія

Текст



Товари

Id_товара

Лічильник



id_компаніі

Числове (довге ціле)

Виробники

id_Компаніі

Товар

Текст



Індексуємо поля таблиць для отримання можливості автоматичного відстеження унікальності значень. В іншому випадку може статися, що одне і те ж значення в довіднику повторюється. Для цього відкриваємо таблицю в режимі конструктора, тиснемо на значок в меню “Індекси” (значок блискавки). У діалоговому вікні в полі “Індекс” пишемо назву індексу (будь-яке), наприклад “Країна”. Потім у полі “Ім’я поля” вибираємо зі списку “Країна” і в нижній частині форми вибираємо зі списку “Унікальний індекс” – Так. Тепер при спробі ввести вже існуюче значення з’явиться відповідне повідомлення і введення буде заблокований.

У таблицях “Виробники” і “Товари” індекс повинен бути складовою, оскільки потрібно відстежувати унікальність пари Країна – Виробник. Адже різні країни можуть випускати однакові товари. Приклад створення складеного індексу:






























Ім’я таблиці


Ім’я індексу


Ім’я поля


Індекс


Країни


Країна


Країна


Унікальний індекс – Так


Виробники


Компанія


id_страни


Унікальний індекс – Так


Компанія


Товари


Товар


id_компаніі


Унікальний індекс – Так


Товар

Пов’язуємо ставленням один до багатьох довідкові таблиці: Країни – Виробники – Товари. Таким чином отримали систему з трьох пов’язаних довідників, в якій крім даних встановлюються так само залежно між таблицями. Створимо так само для демонстрації прикладу автозаповнення дві робочі таблиці: Замовлення і Список товарів.










































Ім’я таблиці

Ім’я поля

Тип поля

Зв’язана таблиця

Поле в пов’язаної таблиці

Замовлення

id_Заказ

Лічильник



id_страни

Числове (довге ціле)



id_Компаніі

Числове (довге ціле)



Список товарів

id_Спісок

Лічильник



id_Заказ

Числове (довге ціле)

Замовлення

id_Заказ

Id_товар

(Довге ціле)

Товари

Id_товара

Пов’язуємо таблиці “Замовлення” і “Список товарів” співвідношенням один до багатьох по полях “id_Заказ” (на одне замовлення може бути багато товарів).
Тепер розглянемо варіант створення форми – довідника. Можна двома варіантами:



  1. Зробити довідник “Країни”. Потім два довідника: “Країни – виробники” та “Виробники – товари”. Заповнюватися вони повинні по черзі: Спочатку “Країни”, потім “Країни – виробники”, потім “Виробники – Товари “.
  2. Зробити один дворівневий довідник: “Країни – виробники – товари”.

У другому випадку потрібно передбачити процедуру, що перешкоджає введенню даних в форму “Виробники”, якщо не заведені дані у формі “Країни”. Інакше отримаємо не пов’язану запис. Для цього служить процедура події форми Enter (Вхід).


Private Sub subСправочникПроизводители_Enter ()
      If IsNull (Me.id_Страни) Then
            MsgBox “Спочатку вкажіть країну виробника”, vbCritical, “admin”
            Страна.SetFocus
      End If
End Sub

Як бачимо, при спробі ввести дані в форму, коли не введений ключ від головної форми


If IsNull (Me.id_Страни) Then

з’являється повідомлення про помилку і фокус введення переноситься на полі головної форми. Аналогічна процедура блокування зроблена і для підлеглої табличної форми на формі “Замовлення”.

На формі “Замовлення” присутні два списки: Країни та Виробники. Причому другий пов’язаний з першим через посилання на нього в запиті (джерело своїх даних). Відкрийте форму “Замовлення” в конструкторі і подивіться на джерело рядків списку Виробник (id_Компаніі). На поле id_Страни встановлено умову відбору


Eval (“Forms! Замовлення! Id_Страни”)

Аналогічно і на поле зі списком табличній підлеглої форми “subЗакази”


Eval (“Forms! Замовлення! Id_Компаніі”)

Незвичайне в цих посиланнях – це їх обробка через функцію Eval (). Але про це трохи пізніше.
Довідкову форму можна запускати подвійним кліком по списку чи по меню зліва. Про реалізацію подібного інтерфейсу докладно розповідалося в статті “Довідники”.

Тепер розглянемо реалізацію каскадного заповнення списків. Як вже говорилося, завдання полягає в тому, щоб при автозаповнення пов’язаного списку окрім внесення нового значення в таблицю – джерело, потрібно так само внести і ключове значення від головного списку. Інакше отримаємо не пов’язану запис. З цієї причини запроваджується нова змінна ctl1.


Функція NotInList допрацьовувалася з урахуванням, щоб через неї можна було додавати відсутні значення верб звичайні списки, а не тільки пов’язані. У цьому випадку при виклику функції в якості другого аргументу (Головного списку) потрібно повторити посилання на список. Це буде зазначенням, що додавати дані потрібно тільки в один список. Виклик функції автозаповнення списків потрібно робити з процедури NotInList (відсутність в списку).
Private Sub id_Страни_NotInList (NewData As String, Response As Integer)
      Set ctl = Me! Id_Страни “встановлюємо посилання на список” Країни “
      “Запускаємо функцію каскадного додавання, і в якості другого аргументу
      “Вказуємо перший список. Тобто функція працює як звичайна процедура
      “Додавання в один список
      Response = AppendLookupTable(ctl, ctl, ctl.Text, 0)
      Me.id_Страни.RowSource = Me.id_Страни.RowSource “оновлюємо список країн
      Set ctl = Nothing “очищаємо змінну
End Sub

У цьому випадку після перевірки умови (каскадне або звичайне додавання) відсутнє значення додається так само як в старій функції


If cbo.Name = cbo1.Name Then
      Set rst = CurrentDb.OpenRecordset(cbo.RowSource)
      With rst
            .AddNew
            rst(1) = NewData
            .Update
            .Close
      End With

Якщо потрібно додати дані в зв’язаний список, тоді при виклику функції AppendLookupTable вказуємо в якості аргументів два списки.



Private Sub id_Компаніі_NotInList (NewData As String, Response As Integer)
      Set ctl = Me.id_Компаніі “встановлюємо посилання на список” Компанії “
      Set ctl1 = Me.id_Страни “встановлюємо посилання на список” Країни “
      “Запускаємо функцію каскадного додавання в два списки
      Response = AppendLookupTable (ctl, ctl1, ctl.Text, Nz (Me.id_Страни, 0))
      Set ctl = Nothing “очищаємо змінну
      Set ctl1 = Nothing “очищаємо змінну
      Me.id_Компаніі.RowSource = Me.id_Компаніі.RowSource “оновлюємо список компаній
      “У разі відмови від заповнення
      If Response = 0 Then
            Me.id_Компаніі = Null “обнуляем список компаній
            id_Страни.SetFocus “встановлюємо фокус на список країни
      End If
End Sub

А тепер з приводу Eval(). Справа в тому, що процедура додавання відсутнього значення в таблицю – джерело списку відбувається за допомогою об’єктної моделі DAO. Якщо в рядку запиту зустрінеться вираз типу Forms! Замовлення! Id_Страни – буде повідомлення про помилку типу: “Потрібно параметр”. Тому, що DAO, Знати нічого не знає, про відкриті формах, саме з цього і лається. Йому дали рядок SQL, Він намагається відкрити відповідний рекордсет. Намагається знайти поле таблиці з іменм Forms! і зрозуміло не знаходить.

Тому, коли використовується робота із запитом за допомогою DAO, То посилання на елементи форм потрібно оформляти через Eval(). Наприклад так:


Eval (“Forms! Замовлення! Id_Страни”)

Або замість імені приводити сам текст запиту, і в ньому вказувати посилання:


Set rst = CurrentDb.OpenRecordset (“SELECT Проізводітелі.id_Компаніі,” & _
“Проізводітелі.Компанія, Проізводітелі.id_страни” & _
“FROM Виробники” & _
“WHERE Проізводітелі.id_Страни =” & Forms! Замовлення! Id_Страни & _
“ORDER BY Проізводітелі.Компанія”)

Ці рекомендації справедливі і при роботі з об’єктною моделлю ADO. Часто перше, на чому спотикаються ті, хто вперше вирішив перевести свій проект з mdb на adp – це помилки при виконанні запитів, де є посилання на елементи форми. Адже тепер обробка даних відбувається на сервері, на якому немає ніяких форм. Втім, це вже зовсім інша тема, до даної статті не має відношення – перехід від mdb до adp.

На закінчення зупинюся ще на одному питанні, яке так само часто задають початківці розробники: як відключити стандартні повідомлення Access? Наприклад, при використанні процедури на видалення запису


DoCmd.DoMenuItem acFormBar, acEditMenu, 8, , acMenuVer70
DoCmd.DoMenuItem acFormBar, acEditMenu, 6, , acMenuVer70

якщо користувач натисне “Скасувати” буде повідомлення: “Перервано виконання макрокоманди DoMenuItem”.
Позбутися його можна по різному. Наприклад, “захистити” цю процедуру виклику командами відключення / включення стандартних повідомлень Access.


DoCmd.SetWarnings False
DoCmd.DoMenuItem acFormBar, acEditMenu, 8, , acMenuVer70
DoCmd.DoMenuItem acFormBar, acEditMenu, 6, , acMenuVer70
DoCmd.SetWarnings True

Але в цьому випадку відключаються всі повідомлення, а не тільки “Перервано виконання макрокоманди DoMenuItem”. І якщо при виконанні “огородженій” процедури виникне будь яка інша помилка – ніхто про це “не дізнається “. Адже помилки бувають і фатальними, з” вильотом “з програми. Ще гірше, якщо захопившись включенням / відключенням стандартних повідомлень розробник забуде потім в коді програми включити їх.

Тому більш розумний варіант – створити свою процедуру обробки помилок, у якій залежно від коду помилки можна виводити свої повідомлення (або не виводити нічого). Приклад такої процедури (на видалення записів)


Function sDeleteRecord() As Boolean
On Error GoTo Err_
      DoCmd.DoMenuItem acFormBar, acEditMenu, 8, , acMenuVer70
      DoCmd.DoMenuItem acFormBar, acEditMenu, 6, , acMenuVer70
      sDeleteRecord = True
Exit_:
      Exit Function
Err_:
      ErrNum = Err.Number
      sDeleteRecord = False
      Err.Clear
      Resume Exit_
End Function

Для її роботи в глобальному модулі Constants введена змінна Public ErrNum As Long. Через неї передається номер помилки. Сама ж процедура видалення запису виглядає так (для форми “ДОВІДНИК країни виробники товари “):


Private Sub butDelete_Click()
      sDeleteRecord
      Select Case ErrNum
            Case 2501
                  Err.Clear
            Case 3396
                  MsgBox “Дані можна видаляти, інакше в таблиці [Список товарів] з’являться не пов’язані записи!”, VbCritical, “admin”
      End Select
End Sub

Тут можливі дві помилки:


Першу помилку можна просто ігнорувати – Err.Clear, а ось у разі виникнення другої – не завадить вивести відповідне повідомлення, щоб було зрозуміло, чому не можна видаляти.      Таким чином, можна створювати свої процедури обробки помилок виконання. Щоб дізнатися номери помилок, можна скористатися Err.Number. Наприклад, при налагодженні нової процедури включити в неї обробник помилок:



On Error GoTo Err_


      …..
      …..
      …..


Exit_:
      Exit Sub
Err_:
      MsgBox “Помилка” & Err.Number & “:” & Err.Description, vbInformation
      Err.Clear “скидання помилки
Resume Exit_

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*