Re[42]: Опять валидация данных
От: stalcer Россия  
Дата: 06.04.05 05:39
Оценка:
Здравствуйте, Sinclair, Вы писали:

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

1:
select x from ClassA x where x is ClassB


2:
select (x as ClassB).F1 from x where (x as ClassB).F2 = 7


Как реализовать операторы is и as. Оператор as семантически думаю как в C# сделать, чтобы возвращал NULL если приведение не удалось, потому как делать эксепшены при выполнении OQL запроса (читай при выполнении соответствующего SQL запроса) — никак.
Как их реализовать, чтобы было эффективно, в идеале чтобы индексы БД работали? Как для этого построить структуру таблиц? Может какие-нибудь хитрые дополнительные поля включать?

Все это интересует для случая, когда все наследники хранятся в той же таблице, что и предки. Т.е. одна таблица содержит все необходимые поля.
http://www.lmdinnovative.com (LMD Design Pack)
Re[43]: Опять валидация данных
От: Sinclair Россия https://github.com/evilguest/
Дата: 06.04.05 06:29
Оценка:
Здравствуйте, stalcer, Вы писали:

S>1:

S>
S>select x from ClassA x where x is ClassB
S>

А, пардон, зачем? Ведь можно сразу сделать
select * from ClassB

S>2:
S>
S>select (x as ClassB).F1 from x where (x as ClassB).F2 = 7
S>

Ну вот, уже пошли даункасты. Зачем???
Надо так:
select x.F1 from ClassB x where x.F2 = 7


S>Как реализовать операторы is и as.

Хм. Я не очень понимаю контекст... В моем понимании is и as могут быть полезны только в таком контексте:
select * from Order where Order.CounterAgent is Person

или вот так:
select * from Order where (Order.CounterAgent is Person) and (Order.CounterAgent as Person).LastName like 'Peter%'

Во втором случае мы видим источник потенциальной проблемы с оператором as. С одной стороны, мы защитили результат даункаста при помощи is, с другой стороны, порядок вычисления термов оператора AND не определен. Что означает, что мы нарвемся либо на Invalid Cast либо на Null Reference (в зависимости от семнтики as).
Я бы запретил as совсем. Вышеприведенный запрос можно переписать как
select * from Order where Order.CounterAgent in (select * from Person where Person.LastName like 'Peter%')

Как видно, исчезла потребность не только в as, но и в is.
Тем не менее, применение такой же техники для самого is не оправдано с точки зрения производительности:
select * from Order where Order.CounterAgent in (select * from Person)


Поэтому многие собаководы включают идентификатор класса прямо в ссылку. Тогда запрос
select * from Order where Order.CounterAgent is Person

превращается в
select * from Order where Order.[CounterAgent.TypeID] in ({PersonDescendantsList})


S>Оператор as семантически думаю как в C# сделать, чтобы возвращал NULL если приведение не удалось, потому как делать эксепшены при выполнении OQL запроса (читай при выполнении соответствующего SQL запроса) — никак.

Как я уже упомянул, само по себе приведение к null ничем не поможет.
... << RSDN@Home 1.1.4 beta 4 rev. 347>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[44]: Опять валидация данных
От: stalcer Россия  
Дата: 06.04.05 07:07
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>А, пардон, зачем? Ведь можно сразу сделать


Это же просто для примера. Я же не спрашиваю как переписать запрос, чтобы не использовать as. Я спрашиваю, как реализовать as в общем случае.

S>Ну вот, уже пошли даункасты. Зачем???


Для полноты поддержки концепции наследования в языке запросов. Это тоже был просто пример.

S>Поэтому многие собаководы включают идентификатор класса прямо в ссылку. Тогда запрос

S>
S>select * from Order where Order.CounterAgent is Person
S>

S>превращается в
S>
S>select * from Order where Order.[CounterAgent.TypeID] in ({PersonDescendantsList})
S>


[CounterAgent.TypeID] это что ?
Значит, in используется. Это, наверное, тоже тормозить будет. Но, это уже идея. А может быть некоторые другие сабаководы как-нибудь по другому поступают ?

S>>Оператор as семантически думаю как в C# сделать, чтобы возвращал NULL если приведение не удалось, потому как делать эксепшены при выполнении OQL запроса (читай при выполнении соответствующего SQL запроса) — никак.

S>Как я уже упомянул, само по себе приведение к null ничем не поможет.

Ну, почему. Это же NULL в понимании SQL. То есть — неизвестное значение. Что получается:

(x as ClassB).F2

если здесь (x as ClassB) вернет NULL, то это означает, что мы хотим узнать значение поля F2 у неизвестного объекта. Результат этой операции (оператора ".") будет — неизвестное значение, т.е. NULL. И никакого эксепшена.
http://www.lmdinnovative.com (LMD Design Pack)
Re[44]: Опять валидация данных
От: stalcer Россия  
Дата: 06.04.05 07:12
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>>
S>>select x from ClassA x where x is ClassB
S>>

S>А, пардон, зачем? Ведь можно сразу сделать
S>
S>select * from ClassB
S>


Хорошо. Но, если вершиной иерархии наследования является ClassA, то твой OQL запрос, после преобразования в SQL будет неявно содержать where условие. Так что, явно я его указываю или оно само неявно в SQL запрос вписывается — неважно. Вопрос в том, как это эффективно реализовать?
http://www.lmdinnovative.com (LMD Design Pack)
Re[45]: Опять валидация данных
От: Sinclair Россия https://github.com/evilguest/
Дата: 06.04.05 07:31
Оценка:
Здравствуйте, stalcer, Вы писали:
S>[CounterAgent.TypeID] это что ?
Имеется в виду то, что для атрибута CounterAgent в таблице RDBMS хранится два поля: [CounterAgent.TypeID] и [CounterAgent.OID]. Тогда
S>Значит, in используется. Это, наверное, тоже тормозить будет.
Очень вряд ли. Там же фиксированный список будет. Сколько у тебя может быть потомков у класса в системе? 1000? Ерунда — полетит со страшной скоростью.
S>Но, это уже идея. А может быть некоторые другие сабаководы как-нибудь по другому поступают ?
Второй вариант честнее, но медленнее: переписывать is именно как
... where ([Order.CounterAgent.OID] in select OID from Person)

где Person — это view, содержащее всех потомков класса Person.

S>Ну, почему. Это же NULL в понимании SQL. То есть — неизвестное значение. Что получается:

S>если здесь (x as ClassB) вернет NULL, то это означает, что мы хотим узнать значение поля F2 у неизвестного объекта. Результат этой операции (оператора ".") будет — неизвестное значение, т.е. NULL. И никакого эксепшена.

Ага. Это, конечно, правильно. Но мы даем шикарную возможность вынудить сервер заниматься бессмысленной работой. Все должно быть оправдано. Если ты не можешь придумать реальный пример, который бы требовал именно as, то незачем его и делать.
... << RSDN@Home 1.1.4 beta 4 rev. 347>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[45]: Опять валидация данных
От: Sinclair Россия https://github.com/evilguest/
Дата: 06.04.05 07:52
Оценка: 5 (1)
Здравствуйте, stalcer, Вы писали:

S>Хорошо. Но, если вершиной иерархии наследования является ClassA, то твой OQL запрос, после преобразования в SQL будет неявно содержать where условие. Так что, явно я его указываю или оно само неявно в SQL запрос вписывается — неважно. Вопрос в том, как это эффективно реализовать?

Хороший вопрос. С точки зрения теории кристалла, мы можем работать с глобальным экстентом, ограничивая его предикатами. Например, предикатом реализации интерфейса или наследования от класса:
public bool Predicate1(object o)
{
    return o is ClassA;
}
public bool Predicate2(object o)
{
    return o is Interface1 && o is Interface2;
}

В реальности, запросы типа Predicate1 встречаются чаще любых других, поэтому от нижележащего слоя требуется способность эффективно выполнять сканирование экстентов.
На практике при использовании RDBMS в качестве нижнего слоя проще всего строить работу на основе View. Тогда для каждого случая можно выбирать способ раскладывания объектов по таблицам:
— хранить всех потомков некоторого класса в одной большой табличке, зануляя "лишние" атрибуты (потребуется хранить еще и идентификатор класса, чтобы построить view для каждого потомка)
— хранить каждый класс в отдельной табличке (потребуется сделать union для всех потомков каждого класса)
— хранить для каждого класса только "новые" поля, при сборке объектов выполнять join
Это связано с тем, что эффективность каждой реализации можно оценивать только с учетом статистики реального использования — количества объектов каждого класса и частоты соответствующих запросов.
Хранение либо нехранение ссылки на тип в ссылке на объект не зависит от выбора схемы хранения объектов; как правило оно все же применяется для оптимизации навигационных запросов и запросов типа where myObject.SomeReference is SomeClass.
... << RSDN@Home 1.1.4 beta 4 rev. 347>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[46]: Опять валидация данных
От: GlebZ Россия  
Дата: 07.04.05 12:31
Оценка: 36 (1)
Здравствуйте, Sinclair, Вы писали:

S>Очень вряд ли. Там же фиксированный список будет. Сколько у тебя может быть потомков у класса в системе? 1000? Ерунда — полетит со страшной скоростью.


S>Ага. Это, конечно, правильно. Но мы даем шикарную возможность вынудить сервер заниматься бессмысленной работой. Все должно быть оправдано. Если ты не можешь придумать реальный пример, который бы требовал именно as, то незачем его и делать.

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

Я вообще пока пошел по другому пути. Ввел понятие typeing. Например такой запрос:
select FucObjects

возвращает все объекты наследованные от FucObject — то есть, практически все объекты находящиеся в БД. А вот так не надо строить много as.
select FucObjects typeing typeas(FucObjects, FucDocument, FucMessage)

вернуть array<FucObjects> у которых реальный тип объектов FucDocument и FucMessage.
И еще одну феньку сделал. Можно в одном запросе возвращаеть сразу несколько наборов.

select a, b where a=10 and b=20

будет определено что это два графа выполнения, и будет возвращено два результирующих набора.

С уважением, Gleb.
... << RSDN@Home 1.1.4 beta 4 rev. 358>>
Re[47]: Опять валидация данных
От: GlebZ Россия  
Дата: 07.04.05 13:02
Оценка:
Здравствуйте, GlebZ, Вы писали:


GZ>возвращает все объекты наследованные от FucObject — то есть, практически все объекты находящиеся в БД. А вот так не надо строить много as.

Извиняюсь. Описка. Вместо as ессно is.

С уважением, Gleb.
... << RSDN@Home 1.1.4 beta 4 rev. 358>>
Re[46]: Опять валидация данных
От: stalcer Россия  
Дата: 08.04.05 05:59
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>>[CounterAgent.TypeID] это что ?

S>Имеется в виду то, что для атрибута CounterAgent в таблице RDBMS хранится два поля: [CounterAgent.TypeID] и [CounterAgent.OID]. Тогда

Ясно. Думал какой-нибудь наворот MS SQL.

S>>Значит, in используется. Это, наверное, тоже тормозить будет.

S>Очень вряд ли. Там же фиксированный список будет. Сколько у тебя может быть потомков у класса в системе? 1000? Ерунда — полетит со страшной скоростью.

Да, верно. Я и сам доодумался после того как запостил сообщение. Только тогда SQL может длинный получиться.

S>>Но, это уже идея. А может быть некоторые другие сабаководы как-нибудь по другому поступают ?

S>Второй вариант честнее, но медленнее: переписывать is именно как
S>
S>... where ([Order.CounterAgent.OID] in select OID from Person)
S>

S>где Person — это view, содержащее всех потомков класса Person.

Это понятно. Решение "в лоб".

S>>Ну, почему. Это же NULL в понимании SQL. То есть — неизвестное значение. Что получается:

S>>если здесь (x as ClassB) вернет NULL, то это означает, что мы хотим узнать значение поля F2 у неизвестного объекта. Результат этой операции (оператора ".") будет — неизвестное значение, т.е. NULL. И никакого эксепшена.

S>Ага. Это, конечно, правильно. Но мы даем шикарную возможность вынудить сервер заниматься бессмысленной работой. Все должно быть оправдано. Если ты не можешь придумать реальный пример, который бы требовал именно as, то незачем его и делать.


Скажем так: Хочу и все. А чем меньше реальных примеров, тем меньше as использовать будут на практике, тем меньше и сервер будет фигней страдать.
http://www.lmdinnovative.com (LMD Design Pack)
Re[46]: Опять валидация данных
От: stalcer Россия  
Дата: 08.04.05 05:59
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Хороший вопрос. С точки зрения теории кристалла, мы можем работать с глобальным экстентом, ограничивая его предикатами.


Ниииичего не понял .

S>- хранить всех потомков некоторого класса в одной большой табличке, зануляя "лишние" атрибуты (потребуется хранить еще и идентификатор класса, чтобы построить view для каждого потомка)


Я говорю только про этот способ, о чем я упоминал выше в ветке.

S>На практике при использовании RDBMS в качестве нижнего слоя проще всего строить работу на основе View.


Процитирую из сообщения выше:

select x from ClassA x where x is ClassB

А, пардон, зачем? Ведь можно сразу сделать:

select * from ClassB


В данном контексте мой вопрос про оператор as можно перевразировать так: Как эффективно построить view для ClassB, при "хранении всех потомков некоторого класса в одной большой табличке".
Вводить что-ли дополнительное поле в таблицу, например, IsClassBOrItsDescandant (1/0), строить по нему индекс и во view по этому полю фильтровать.
Дык сколько же таких поле нужно .
http://www.lmdinnovative.com (LMD Design Pack)
Re[47]: Опять валидация данных
От: stalcer Россия  
Дата: 08.04.05 06:13
Оценка:
Здравствуйте, GlebZ, Вы писали:

GZ>Я вообще пока пошел по другому пути. Ввел понятие typeing. Например такой запрос:

GZ>
GZ>select FucObjects
GZ>

GZ>возвращает все объекты наследованные от FucObject — то есть, практически все объекты находящиеся в БД. А вот так не надо строить много as.
GZ>
GZ>select FucObjects typeing typeas(FucObjects, FucDocument, FucMessage)
GZ>

GZ>вернуть array<FucObjects> у которых реальный тип объектов FucDocument и FucMessage.

Фишка интересная, но только как дополнительная возможность. Все таки is и as должны быть.

GZ>И еще одну феньку сделал. Можно в одном запросе возвращаеть сразу несколько наборов.

GZ>

GZ>select a, b where a=10 and b=20

GZ>будет определено что это два графа выполнения, и будет возвращено два результирующих набора.

ИМХО, здесь ты перемудрил. А может я просто не понял, т.к. ты from пропустил.

Дык все-таки интересно, можно ли сделать хоть как-то is и as, чтобы индексы работали.
http://www.lmdinnovative.com (LMD Design Pack)
Re[47]: Опять валидация данных
От: Sinclair Россия https://github.com/evilguest/
Дата: 08.04.05 06:21
Оценка:
Здравствуйте, stalcer, Вы писали:
S>Ниииичего не понял .
Это означает, что мы в качестве from всегда используем что-то вроде "everywhere". Никаких "встроенных" коллекций для нашего удобства нет, и даже просто "все объекты класса а" нужно выбирать.
S>>- хранить всех потомков некоторого класса в одной большой табличке, зануляя "лишние" атрибуты (потребуется хранить еще и идентификатор класса, чтобы построить view для каждого потомка)
S>В данном контексте мой вопрос про оператор as можно перевразировать так: Как эффективно построить view для ClassB, при "хранении всех потомков некоторого класса в одной большой табличке".
Ну так это же элементарно! Классика жанра — операции над древовидными данными
S>Вводить что-ли дополнительное поле в таблицу, например, IsClassBOrItsDescandant (1/0), строить по нему индекс и во view по этому полю фильтровать.
S>Дык сколько же таких поле нужно .
Вовсе так не надо. С учетом того, что изменения в структуре классов как минимум на три порядка реже, чем чтение данных, можно воспользоваться способом Joe Celko — он дает максимальную производительность чтения при минимальном оверхеде по объему. Способ описан например здесь
Автор(ы): Михаил Голованов
Дата: 28.01.2002
(структура с хранением границ ветви).
Главное — аккуратно перенумеровывать классы (и, соответственно, объекты) при изменении наследования.
... << RSDN@Home 1.1.4 beta 4 rev. 347>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[48]: Опять валидация данных
От: stalcer Россия  
Дата: 08.04.05 07:39
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Это означает, что мы в качестве from всегда используем что-то вроде "everywhere". Никаких "встроенных" коллекций для нашего удобства нет, и даже просто "все объекты класса а" нужно выбирать.


И что это дает?

S>>В данном контексте мой вопрос про оператор as можно перевразировать так: Как эффективно построить view для ClassB, при "хранении всех потомков некоторого класса в одной большой табличке".

S>Ну так это же элементарно! Классика жанра — операции над древовидными данными

А где здесь дерево то ты увидел.

S>Главное — аккуратно перенумеровывать классы (и, соответственно, объекты) при изменении наследования.


Аааа . Об этом я уже думал. Менять TypeId (или что там еще) у объектов тоже плохо. Так-то оно понятно, если специальным образом раздавать TypeId, то вместо:
  ...where x.TypeId in [<список TypeId наследников>]

Можно делать:
  ...where x.TypeId <= MIN_TYPE_ID and x.TypeId >= MAX_TYPE_ID

Для этого естественно необходимо, чтобы все TypeId всех наследников от, например, ClassB были в диапазоне [MIN_TYPE_ID..MAX_TYPE_ID], ну и соответственно, TypeId остальных типов — не были в этом диапазоне.
Что сказать, это тоже идея. Может даже неплохая.
http://www.lmdinnovative.com (LMD Design Pack)
Re[49]: Опять валидация данных
От: Sinclair Россия https://github.com/evilguest/
Дата: 08.04.05 08:05
Оценка:
Здравствуйте, stalcer, Вы писали:
S>А где здесь дерево то ты увидел.
Дерево наследования классов.
S>Можно делать:
S>
S>  ...where x.TypeId <= MIN_TYPE_ID and x.TypeId >= MAX_TYPE_ID
S>

Именно об этом я и говорю. Это и называется структурой с хранением границ ветвей.
S>Для этого естественно необходимо, чтобы все TypeId всех наследников от, например, ClassB были в диапазоне [MIN_TYPE_ID..MAX_TYPE_ID], ну и соответственно, TypeId остальных типов — не были в этом диапазоне.
Можно ввести дополнительную косвенность — хранить соответствие TypeID/TypeSearchID в таблице типов. Тогда не придется апдейтить табличку с объектами при вставке класса.
... << RSDN@Home 1.1.4 beta 4 rev. 347>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[50]: Опять валидация данных
От: stalcer Россия  
Дата: 08.04.05 08:29
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Можно ввести дополнительную косвенность — хранить соответствие TypeID/TypeSearchID в таблице типов. Тогда не придется апдейтить табличку с объектами при вставке класса.


Если уж вводить дополнительную косвенность, то никакие LOW/HIGH не нужны, так как в таблице типов запросто можно сделать поля типа IsClassBOrItsDescandant (1/0). Для каждого типа по одному полю.
http://www.lmdinnovative.com (LMD Design Pack)
Re[51]: Опять валидация данных
От: Sinclair Россия https://github.com/evilguest/
Дата: 08.04.05 08:51
Оценка:
Здравствуйте, stalcer, Вы писали:

S>Если уж вводить дополнительную косвенность, то никакие LOW/HIGH не нужны, так как в таблице типов запросто можно сделать поля типа IsClassBOrItsDescandant (1/0). Для каждого типа по одному полю.

Не, ну если тебя не смущает отведение под это N*N ячеек, то вперед. Ограничим максимальное количество классов в базе 254.
... << RSDN@Home 1.1.4 beta 4 rev. 347>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[52]: Опять валидация данных
От: stalcer Россия  
Дата: 08.04.05 09:07
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Не, ну если тебя не смущает отведение под это N*N ячеек, то вперед. Ограничим максимальное количество классов в базе 254.


Не смущает. Тем более что в большинстве случаев наследников не так и много бывает.
Этот способ, как и способ с HIGH/LOW имеет преимущество в том, что можно заставить работать индексы БД.
Но, основной его недостаток в том, что необходимо делать join'ы.

Зато, если использовать HIGH/LOW без дополнительной таблицы, то и индексы работают, и join делать не нужно. Но, нужно менять TypeId у объектов.

Блин ! Ненавижу такие кубики-Рубика. В одном месте собираешь — в другом разбирается.

Все таки, наверное придется менять TypeId у объектов . Несмотря на некоторые заморочки с администрированием, производительность в runtime — важнее.
http://www.lmdinnovative.com (LMD Design Pack)
Re[48]: Опять валидация данных
От: GlebZ Россия  
Дата: 08.04.05 12:29
Оценка:
Здравствуйте, stalcer, Вы писали:

S>Фишка интересная, но только как дополнительная возможность. Все таки is и as должны быть.

Да. Но это просто возможность сразу определить какие типы объектов используются в запросе. Это лучше и легче чем писать большую строку as.

GZ>>И еще одну феньку сделал. Можно в одном запросе возвращаеть сразу несколько наборов.

GZ>>

GZ>>select a, b where a=10 and b=20

GZ>>будет определено что это два графа выполнения, и будет возвращено два результирующих набора.

S>ИМХО, здесь ты перемудрил. А может я просто не понял, т.к. ты from пропустил.

Это допущение языка. Если в select описан тип объекта, то from можно пропустить. Поскольку в большинстве случаев выборка идет по объектам а не по полям, то очень удобно.

S>Дык все-таки интересно, можно ли сделать хоть как-то is и as, чтобы индексы работали.

Зачем? У меня по крайней мере так. Если предполагается то, что в одной таблице находится только один тип, то проверяется на метаданных. То есть, проверяется до генерации запросов. Если это тип находится в разных таблицах, то проверяется по нахождению в таблице(обычно обрубается inner join'ом). Ну а если есть несколько типов в одной таблице, то тут уже есть обязательный typeID (если тип находится в нескольких таблицах — то он может находится в любой из них). Что приколько, натягивал эти метаданные на несколько баз — никаких доработок. Сейчас есть только одно условие которое необходимо для работы — наличие primary или unique индекса в таблице. Больше проблем с существующими реляционками нет (по крайней мере не нашел). А тебе легче — ты реляционку делаешь сам.

С уважением, Gleb.
... << RSDN@Home 1.1.4 beta 4 rev. 358>>
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.