На самом деле, мы занимаемся тем, чем занимается любой ORM (Object-Relation Mapper...)
Неплохо некоторые идеи описаны у Усова (
http://alexus.ru/russian/articles/dbms/oop_rm/index.htm).
Контекст проекта: небольшой заказ на сбор данных с трёх медицинских анализаторов.
Делают мои студенты, архитектуру-каркас, естественно, пришлось с ними вместе выстроить.
Проектировалась не реляционная БД, проектировалась объектная модель - и ровно нужные функции хранения и выборки объектов. А потом уже это проецировалось на реляционку (а можно без изменения приложения реализовать на чём угодно). Я бы охотнее взял NoSQL - Mongo ту же самую (у нас она применяется, но не из ББ). Но ввиду отсутствия драйвера....
Если интересно, выглядит примерно так модель и хранение.
Это объектная модель:
Код:
DEFINITION HemoData;
   IMPORT Dates;
   CONST
      maxParamCount = 64;
   TYPE
      PersonalCode = ARRAY 64 OF SHORTCHAR;
      DateTime = RECORD 
         date: Dates.Date;
         time: Dates.Time
      END;
      Result = POINTER TO LIMITED RECORD (* Result почти неизменен, создаётся и "накачивается" данными однократно - при получении с анализатора, потом отправляется в базу. После считывания из базы возможно только одно изменение - FixPersonalCode. *)
         readOnly-: BOOLEAN;
         dbCode-: INTEGER;
         personalCode-: PersonalCode;
         analyzerType-: INTEGER;
         analyzeTime-, registerTime-: DateTime;
         values-: Value;
         (r: Result) AddInt (param, x: INTEGER), NEW;
         (r: Result) AddReal (param: INTEGER; x: REAL), NEW;
         (r: Result) AddString (param: INTEGER; IN s: ARRAY OF CHAR), NEW;
         (r: Result) Lock, NEW; (* Переводит объект в режим readOnly, после добавления всех параметров *)
         (r: Result) FixPersonalCode (IN newCode: ARRAY OF SHORTCHAR), NEW; (* Единственная операция, изменяющая уже существующий в базе результат *)
         (r: Result) Param (param: INTEGER): Value, NEW
      END;
      Value = POINTER TO ABSTRACT RECORD 
         next-: Value;
         param-: INTEGER
      END;
      IntValue = POINTER TO LIMITED RECORD (Value)
         x-: INTEGER
      END;
      RealValue = POINTER TO LIMITED RECORD (Value)
         x-: REAL
      END;
      StringValue = POINTER TO LIMITED RECORD (Value)
         x-: POINTER TO ARRAY OF CHAR
      END;
   VAR
      nullTime-: DateTime;
   PROCEDURE NewResult (dbCode: INTEGER; IN personalCode: ARRAY OF SHORTCHAR; analyzerType: INTEGER; IN analyzeTime, registerTime: DateTime): Result;
END HemoData.
А это хранилище:
Код:
DEFINITION HemoDB;
   IMPORT HemoData, Dates, HemoCollections;
   CONST
      compareAnalyzeDate = 2; compareAnalyzerType = 1; comparePersonalCode = 0; compareRegisterDate = 3;
   TYPE
      Criteria = RECORD 
         compare: SET;
         personalCode: HemoData.PersonalCode;
         analyzerType: INTEGER;
         analyzeDates, registerDates: RECORD 
            from, to: Dates.Date
         END
      END;
   VAR
      connected-: BOOLEAN;
   PROCEDURE Connect;
   PROCEDURE Close;
   PROCEDURE RegisterResults (results: HemoCollections.Collection);
   PROCEDURE UpdatePersonalCodes (results: HemoCollections.Collection);
   PROCEDURE Select (IN criteria: Criteria): HemoCollections.Collection;
END HemoDB.
База имеет следующий вид:
Код:
CREATE TABLE results (
   id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL,
   personalCode VARCHAR(255) NOT NULL,
   analyzerType INT UNSIGNED NOT NULL,
   analyzeDate DATE NOT NULL,
   analyzeTime TIME NOT NULL,
   registerDate DATE NOT NULL,
   registerTime TIME NOT NULL
);
CREATE TABLE real_values (
   result_id INT UNSIGNED NOT NULL,
   param INT UNSIGNED NOT NULL,
   value REAL NOT NULL,
   FOREIGN KEY (result_id) REFERENCES results(id),
   UNIQUE(result_id, param)
);
CREATE TABLE string_values (
   result_id INT UNSIGNED NOT NULL,
   param INT UNSIGNED NOT NULL,
   value VARCHAR(255) NOT NULL,
   FOREIGN KEY (result_id) REFERENCES results(id),
   UNIQUE(result_id, param)
);
CREATE TABLE int_values (
   result_id INT UNSIGNED NOT NULL,
   param INT UNSIGNED NOT NULL ,
   value INT UNSIGNED NOT NULL,
   FOREIGN KEY (result_id) REFERENCES results(id),
   UNIQUE(result_id, param)
);
Плюс, конечно, индексы.
Таким образом, при реализации на базе SQL RegisterResults продуцирует последовательность INSERT INTO results ...; INSERT INTO xx_values VALUES (LAST_INSERT_ID()).........
Select продуцирует UNION SELECT следующего вида (где условие WHERE приходится трижды помещать в каждый SELECT):
Код:
SELECT id, personalCode, analyzerType, analyzeDate, analyzeTime, registerDate, registerTime, param, 1 AS param_type, value AS int_value, NULL AS real_value, NULL AS string_value FROM results, int_values WHERE result_id = id " + where +
"UNION SELECT id, personalCode, analyzerType, analyzeDate, analyzeTime, registerDate, registerTime, param, 2 AS param_type, NULL AS int_value, NULL AS real_value, value AS string_value FROM results, string_values WHERE result_id = id " + where +
"UNION SELECT id, personalCode, analyzerType, registerDate, registerTime, registerDate, registerTime, param, 3 AS param_type, NULL AS int_value, value AS real_value, NULL AS string_value FROM results, real_values WHERE result_id = id ORDER BY id";