Есть вопрос, который очень хочется обсудить с коллективным разумом.
Сначала преамбула.
Есть такой linq запрос:
var q =
from ch in Childs
group ch by ch.ParentID;
Вполне такой ничего себе запрос. В результате его выполнения будет выполнен SQL запрос, поднимающий сгруппированные записи, а затем будет выполнено по одному запросу для каждой группы, загружащему подгруппу. Примерно так:
SELECT [t0].[ParentID] AS [Key]
FROM [Child] AS [t0]
GROUP BY [t0].[ParentID]
GO
-- Region ParametersDECLARE @x1 Int = 1
-- EndRegionSELECT [t0].[ParentID], [t0].[ChildID]
FROM [Child] AS [t0]
WHERE ((@x1 IS NULL) AND ([t0].[ParentID] IS NULL)) OR ((@x1 IS NOT NULL) AND ([t0].[ParentID] IS NOT NULL) AND (@x1 = [t0].[ParentID]))
GO
-- Region ParametersDECLARE @x1 Int = 2
-- EndRegionSELECT [t0].[ParentID], [t0].[ChildID]
FROM [Child] AS [t0]
WHERE ((@x1 IS NULL) AND ([t0].[ParentID] IS NULL)) OR ((@x1 IS NOT NULL) AND ([t0].[ParentID] IS NOT NULL) AND (@x1 = [t0].[ParentID]))
GO
-- Region ParametersDECLARE @x1 Int = 3
-- EndRegionSELECT [t0].[ParentID], [t0].[ChildID]
FROM [Child] AS [t0]
WHERE ((@x1 IS NULL) AND ([t0].[ParentID] IS NULL)) OR ((@x1 IS NOT NULL) AND ([t0].[ParentID] IS NOT NULL) AND (@x1 = [t0].[ParentID]))
GO
-- Region ParametersDECLARE @x1 Int = 4
-- EndRegionSELECT [t0].[ParentID], [t0].[ChildID]
FROM [Child] AS [t0]
WHERE ((@x1 IS NULL) AND ([t0].[ParentID] IS NULL)) OR ((@x1 IS NOT NULL) AND ([t0].[ParentID] IS NOT NULL) AND (@x1 = [t0].[ParentID]))
Всё бы хорошо, но на каждую группу мы получим по одному запросу, что при приличном размере первого запроса может легко убить базу.
В принципе, сделать всё как есть, так же как и сделать всё по человечески двумя запросами, на данный момент не представляет особого труда. Но в последнем случае будет потеряна ленивость. Т.е. придётся результат первого да и скорее всего второго запроса закачивать в память полностью и уже потом отдаваться энумераторам. Что опять же при приличном размере первого запроса выльется в приличный объём памяти.
Собственно вопрос — как лучше поступить? Повторить реализацию MS или сделать более оптимальный для сервера БД, но затратный по памяти для клиента вариант?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>В принципе, сделать всё как есть, так же как и сделать всё по человечески двумя запросами, на данный момент не представляет особого труда. Но в последнем случае будет потеряна ленивость. Т.е. придётся результат первого да и скорее всего второго запроса закачивать в память полностью и уже потом отдаваться энумераторам. Что опять же при приличном размере первого запроса выльется в приличный объём памяти.
IT>Собственно вопрос — как лучше поступить? Повторить реализацию MS или сделать более оптимальный для сервера БД, но затратный по памяти для клиента вариант?
вот про память и ленивость, а есть гарантия, что первый способ не жрет ту же самую память что и второй — в смысле, когда мы получаем очередную порцию "ленивых" данных с сервера, то на предыдущую уже ссылок нет, и GC ее собрал?
кстати, про ленивость и БД можно отстраненный вопрос: а на каждую порцию ленивых данных создается своё соединение (ДбМанагер) или пользуется одно?
Здравствуйте, ili, Вы писали:
ili>вот про память и ленивость, а есть гарантия, что первый способ не жрет ту же самую память что и второй — в смысле, когда мы получаем очередную порцию "ленивых" данных с сервера, то на предыдущую уже ссылок нет, и GC ее собрал?
Гарантии нет. Это в большей степени зависит от способа использования.
ili>кстати, про ленивость и БД можно отстраненный вопрос: а на каждую порцию ленивых данных создается своё соединение (ДбМанагер) или пользуется одно?
Я бы создавал новое соединение, а дальше это не моё дело, а коннекшин пула.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Есть вопрос, который очень хочется обсудить с коллективным разумом.
IT>Собственно вопрос — как лучше поступить? Повторить реализацию MS или сделать более оптимальный для сервера БД, но затратный по памяти для клиента вариант?
Мне кажется имеет смысл сделать это дело конфигурируемым. Ибо иногда гораздо быстрее вытянуть лишние данные и затем уже переварить их на клиенте, чем за каждым чихом ходить на сервак. Классический пример такого случая — высоколатентные сети а-ля Cisco VPN и подобные ему самопалы (к сожалению, слишком часто мне приходится с ними иметь дело...). В таком случае производительность гораздо сильнее зависит от количества запросов, чем от объёма возвращаемых данных (в разумных пределах конечно). Довелось мне тут поработать через такое соединение, так вот — вытянуть сразу одним скопом 200к строк из БД занимает 40 сек, а выдёргивание 5к строк по одной из той же самой таблицы занимает >10 минут! Это реальные данные, и одна из причин, почему я использую BLToolkit в своих проектах (и с моей подачи все проекты в компании используют его же), в том, что его легко подпилить и заточить под конкретный случай. Так что гибкость, гибкость и ещё раз гибкость!
Вот и отлично! День прожит не зря, и можно со спокойной душой идти спать
P.S. Знал бы ты, каких боёв мне стоило добиться сноса всех самопальных велосипедов и внедрения BLToolkit'а как единого стандартного средства доступа в БД во всей нашей компании... Зато теперь мы с вами всерьёз и надолго
Здравствуйте, IT, Вы писали:
K>>Так что гибкость, гибкость и ещё раз гибкость!
IT>Убедил
кстати, а на сколько Linq провайдер получается "кастомизируемый"? т.е. можно ли будет такое ту же имплементацию GroupBy изменить в каких-нить наследниках, не трогая код библиотеки?
Здравствуйте, ili, Вы писали:
ili>кстати, а на сколько Linq провайдер получается "кастомизируемый"? т.е. можно ли будет такое ту же имплементацию GroupBy изменить в каких-нить наследниках, не трогая код библиотеки?
Изменить можно всё На данный момент поддерживается 11 sql провайдеров для разных серверов. Все они в каком-то смысле уникальны. Конкретно для GroupBy создаёшь свой провайдер, перекрваешь в нём BuildGroupByClause и поехали.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, koandrew, Вы писали:
IT>>Я бы создавал новое соединение, а дальше это не моё дело, а коннекшин пула.
K>А я бы нет
В ленивом сценарии подгрузка данных может осуществлена в момент, весьма отдалённый от использования первого соединения. Держать одно соединение открытым на всякий случай расточительно и в конце концов приведёт к проблемам.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Изменить можно всё На данный момент поддерживается 11 sql провайдеров для разных серверов. Все они в каком-то смысле уникальны. Конкретно для GroupBy создаёшь свой провайдер, перекрваешь в нём BuildGroupByClause и поехали.
тогда, ИМХО вообще особых проблем не видно... всё, по местной классике — базовое поведение "такое-то" хочешь по другому "вот тебе напильник и болгарка", глваное в вики написать, какой угол стачивать
Здравствуйте, IT, Вы писали:
IT>В ленивом сценарии подгрузка данных может осуществлена в момент, весьма отдалённый от использования первого соединения. Держать одно соединение открытым на всякий случай расточительно и в конце концов приведёт к проблемам.
Это да. С другой стороны, гибкость (С) Т.е. мне кажется, что тут стоит сделать такую же систему GetDbManager/CreateDbManager, как в акцессорах, с возможностью переопределить дефолтное поведение... У этой пары имеется занимательный побочный эффект, которым я пользуюсь для прозрачной поддержки транзакций — если создать акцессор, передав ему null в качестве DbManager'а и выставить DisposeDbManager в True, то на каждый запрос будет запрашиваться метод CreateDbManager, который я переопределил (см. код моего экстеншена для Unity — там увидишь, как это всё работает)...
Здравствуйте, ili, Вы писали:
ili>тогда, ИМХО вообще особых проблем не видно... всё, по местной классике — базовое поведение "такое-то" хочешь по другому "вот тебе напильник и болгарка", глваное в вики написать, какой угол стачивать
Проблемы есть. Изменить можно всё, что касается генерации SQL по уже распарсенной и подготовленной структуре. Здесь же речь идёт об изменении алгоритма парсера. Его менять нельзя и даже пока не планируется этого делать. С одной стороны, потому что это сложно, с другой, я пока не вижу в этом никакого смысла.
Если нам не помогут, то мы тоже никого не пощадим.