Перевести SQL на OCL?
У меня есть кусок SQL, который я хочу перевести на OCL. Я плохо разбираюсь в SQL, поэтому хочу повысить удобство обслуживания. Мы используем Interbase 2009, Delphi 2007 с разработкой Bold и modeldriven. Теперь я надеюсь, что кто-то здесь говорит на хорошем SQL и OCL:-) Оригинальный SQL:
Select Bold_Id, MessageId, ScaniaId, MessageType, MessageTime, Cancellation, ChassieNumber, UserFriendlyFormat, ReceivingOwner, Invalidated, InvalidationReason,
(Select Parcel.MCurrentStates From Parcel
Where ScaniaEdiSolMessage.ReceivingOwner = Parcel.Bold_Id) as ParcelState From ScaniaEdiSolMessage
Where MessageType = 'IFTMBP' and
not Exists (Select * From ScaniaEdiSolMessage EdiSolMsg
Where EdiSolMsg.ChassieNumber = ScaniaEdiSolMessage.ChassieNumber and EdiSolMsg.ShipFromFinland = ScaniaEdiSolMessage.ShipFromFinland and EdiSolMsg.MessageType = 'IFTMBF') and
invalidated = 0 Order By MessageTime desc
После небольшого упрощения:
Select Bold_Id, (Select Parcel.MCurrentStates From Parcel
where ScaniaEdiSolMessage.ReceivingOwner = Parcel.Bold_Id) From ScaniaEdiSolMessage
Where MessageType = 'IFTMBP' and not Exists (Select * From ScaniaEdiSolMessage
EdiSolMsg Where EdiSolMsg.ChassieNumber = ScaniaEdiSolMessage.ChassieNumber and
EdiSolMsg.ShipFromFinland = ScaniaEdiSolMessage.ShipFromFinland and
EdiSolMsg.MessageType = 'IFTMBF') and invalidated = 0
ПРИМЕЧАНИЕ. Существует два случая для типа сообщения: "IFTMBP" и "IFTMBF".
Таким образом, таблица для перечисления - ScaniaEdiSolMessage. Он имеет такие атрибуты, как:
- MessageType: String
- ChassiNumber: String
- ShipFromFinland: Boolean
- Недействительно: логическое значение
Он также имеет ссылку на таблицу Parcel с именем ReceivingOwner с ключом BoldId.
Таким образом, кажется, что он перечисляет все строки ScaniaEdiSolMessage, а затем имеет подзапрос, который также перечисляет все строки ScaniaEdiSolMessage и называет его EdiSolMsg. Тогда это исключает почти все строки. На самом деле, приведенный выше запрос дает одно попадание из 28000 записей.
В OCL легко перечислить все экземпляры:
ScaniaEdiSolMessage.allinstances
Также легко фильтровать строки, выбрав, например:
ScaniaEdiSolMessage.allinstances->select(shipFromFinland and not invalidated)
Но я не понимаю, как я должен сделать OCL, чтобы соответствовать SQL выше.
3 ответа
Послушайте Габриэля и Стефани, узнайте больше SQL.
Вы заявляете, что хотите сделать код более понятным, но число разработчиков, понимающих SQL, намного больше, чем число разработчиков, понимающих OCL.
Если вы выйдете из проекта завтра после преобразования его в OCL, шансы на то, что вы сможете найти кого-то, кто мог бы поддерживать OCL, очень малы. Однако шансы на то, что вы сможете найти кого-то для поддержки SQL, очень высоки.
Не пытайтесь втиснуть квадратный колышек в круглое отверстие только потому, что у вас хорошо с круглыми молотками:)
Есть проект, Dresden OCL, который может вам помочь.
Dresden OCL предоставляет набор инструментов для анализа и оценки ограничений OCL для различных моделей, таких как UML, EMF и Java. Кроме того, Dresden OCL предоставляет инструменты для генерации кода Java/AspectJ и SQL. Инструменты Dresden OCL могут использоваться как библиотека для другого проекта или как проект подключаемого модуля, который расширяет Eclipse с поддержкой OCL.
Я не использовал его, но есть демонстрация, показывающая, как инструмент генерирует SQL из модели и ограничений OCL. Я понимаю, что вы просите об обратном, но, возможно, используя это, вы можете понять это. Существует также статья, которая описывает преобразования OCL->SQL теми же людьми.
С MDriven (преемником Bold для Delphi) я бы сделал это так:
При работе с OCL to SQL все становится проще, если вы думаете о другом наборе информации, который нужно проверить, а затем используете операторы ocl как -> пересечение, чтобы найти нужный вам набор.
Так что в вашем случае у вас может быть такой набор:
ScaniaEdiSolMessage.allinstances->select(shipFromFinland and not invalidated)
но у вас также есть такой набор:
ScaniaEdiSolMessage.allinstances->select(m|m.ReceivingOwner.MessageType = 'IFTMBP')
И у вас еще больше есть этот критерий:
Parcel.allinstances->select(p|p.Messages->exists(m|m.MessageType = 'IFTMBF')).Messages
Если все эти наборы имеют одинаковый тип результата (коллекция ScaniaEdiSolMessage), вы можете просто пересечь их, чтобы получить желаемый результат
ScaniaEdiSolMessage.allinstances->select(shipFromFinland and not invalidated)
->intersection(ScaniaEdiSolMessage.allinstances->select(m|m.ReceivingOwner.MessageType = 'IFTMBP'))
->intersection(Parcel.allinstances->select(p|p.Messages->exists(m|m.MessageType = 'IFTMBF')).Messages
)
И, глядя на это, мы можем немного уменьшить его до:
ScaniaEdiSolMessage.allinstances
->select(m|m.shipFromFinland and (not m.invalidated) and
(m.ReceivingOwner.MessageType = 'IFTMBP'))
->intersection(Parcel.allinstances->select(p|
p.Messages->exists(m|m.MessageType = 'IFTMBF')).Messages
)