Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > БД в Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 17.04.2013, 22:02   #1
Сергей089
Форумчанин
 
Регистрация: 22.09.2008
Сообщений: 214
По умолчанию ADOQuery с серверным курсором не отображает некоторые записи (с NULL значениями)

Всем привет! У меня проблема с серверным курсором. Он не отображает некоторые данные. Помогите, пожалуйста, разобраться. Есть 2 таблицы:

Код:
таблица school_adres
id,
school,
rayon,
gorod,
naspunkt
Код:
таблица kladr (здесь адреса нашей страны)
name,
socr,
code
В таблице school_adres в столбцах rayon, gorod, naspunkt хранятся коды. Для того, чтобы показать наименование района для школы, написал запрос

Код:
select
	*
from
	school_adres
	left join kladr kl_rayon on kl_rayon.code = rayon
В столбце rayon может быть код, которого нет в кладре. Для таких записей запрос в столбцах name, socr, code, ясное дело, дает NULL. Так вот ADOQuery с курсором clUseServer не показывает эти строки, а с clUseClient все нормально отображается. Почему так и как это исправить?

Я использую серверный курсор, т.к. с с ним запрос
Код:
select
	*
from
	school_adres
выполняется 3 секунды, а с clUseClient - 12 секунд. В это таблице 850422 записи

Последний раз редактировалось Сергей089; 17.04.2013 в 22:06.
Сергей089 вне форума Ответить с цитированием
Старый 17.04.2013, 22:35   #2
Аватар
Старожил
 
Аватар для Аватар
 
Регистрация: 17.11.2010
Сообщений: 18,922
По умолчанию

А почему решили, что такая большая разница по времени выполнения запроса? Просто при клиентском курсоре переданы все данные клиенту, а при серверном вы все данные приняли за эти 3 сек? Скорее всего именно поэтому не увидели данных с NULL по полям kladr. Кстати зачем их все передавать клиенту? Как насчет индекса по полю code таблицы kladr?
Если бы архитекторы строили здания так, как программисты пишут программы, то первый залетевший дятел разрушил бы цивилизацию
Аватар вне форума Ответить с цитированием
Старый 17.04.2013, 22:51   #3
Сергей089
Форумчанин
 
Регистрация: 22.09.2008
Сообщений: 214
По умолчанию

Время открытия ADOQuery замерял с помощью QueryPerformanceCounter и QueryPerformanceFrequency, затем переводил в секунды.
Код:
  QueryPerformanceCounter(i1);
  ADOQuery1.Active := true;
  QueryPerformanceCounter(i2);
  QueryPerformanceFrequency(i3);
  Label3.Caption := 'Время: ' + FloatToStr((i2 - i1) / i3) + ' [сек.]';
При курсоре clUseClient ADOQuery открывается 12 секунд и загружает все данные.
При курсоре clUseServer ADOQuery открывается 3 секунд и загружает только ЧАСТЬ данных. Затем при скролинге идет подгрузка данных. Это я наблюдал в диспетчере задач (график "получено байт" желтого цвета). Подгрузка данных идет довольно быстро, тормозов я не наблюдал.

Это я замерял только открытие таблицы school_adres БЕЗ кладра.

Цитата:
Как насчет индекса по полю code таблицы kladr?
В кладре уже есть индекс.

Последний раз редактировалось Сергей089; 17.04.2013 в 22:54.
Сергей089 вне форума Ответить с цитированием
Старый 17.04.2013, 22:58   #4
Аватар
Старожил
 
Аватар для Аватар
 
Регистрация: 17.11.2010
Сообщений: 18,922
По умолчанию

Цитата:
Затем при скролинге идет подгрузка данных
Все правильно, не делайте скроллинг, а выполните команду ADOQuery.Last и замерьте время на подгрузку всех оставшихся данных. В совокупе на те же 12 сек и выйдете, если не больше. И записей будет ровно столько, сколько и при клиентском курсоре
Если бы архитекторы строили здания так, как программисты пишут программы, то первый залетевший дятел разрушил бы цивилизацию
Аватар вне форума Ответить с цитированием
Старый 17.04.2013, 23:08   #5
Сергей089
Форумчанин
 
Регистрация: 22.09.2008
Сообщений: 214
По умолчанию Почему с серверным курсором ADOQuery вместо left join выполняет inner join?

Аватар, вы меня не поняли. Наверное я не так написал.

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

Данные начинают пропадать только после добавления в запрос
Код:
left join kladr kl_rayon on kl_rayon.code = rayon
И пропадают только те записи, для которых не нашлось соответствия в таблице kladr.

Короче, вместо
Код:
select
	*
from
	school_adres
	left join kladr kl_rayon on kl_rayon.code = rayon
как будто бы выполняется
Код:
select
	*
from
	school_adres
	INNER JOIN kladr kl_rayon on kl_rayon.code = rayon
Наверное, правильно будет спросить "Почему с серверным курсором ADOQuery вместо left join выполняет inner join?".

Кстати, СУБД - MS SQL 2005.

Последний раз редактировалось Сергей089; 17.04.2013 в 23:28. Причина: Переименование заголовка
Сергей089 вне форума Ответить с цитированием
Старый 17.04.2013, 23:22   #6
Аватар
Старожил
 
Аватар для Аватар
 
Регистрация: 17.11.2010
Сообщений: 18,922
По умолчанию

А как можно визуально убедиться на таком количестве записей, что что-то пропало? В варианте запроса с LEFT JOIN запросто данные могут быть переданы не в той последовательности, чем без него, особенно в той части, где в kladr не найдено соответствия. Поставьте один и тот же ORDER BY для обеих случаев и не сомневаюсь, что пропажа найдется. Или после ADOQuery.Last посмотрите что вернет ADOQuery.RecordCount
Если бы архитекторы строили здания так, как программисты пишут программы, то первый залетевший дятел разрушил бы цивилизацию
Аватар вне форума Ответить с цитированием
Старый 17.04.2013, 23:58   #7
Сергей089
Форумчанин
 
Регистрация: 22.09.2008
Сообщений: 214
По умолчанию

Я сейчас на тестовой базе в таблице school_adres оставил всего две записи

Первая запись - школа в столице, поэтому района нет
Код:
rayon = '0000000000000'
gorod = '0800000100000'
Вторая запись - школа в простом городе
Код:
rayon = '0800600000000'
gorod = '0800600100000'
Района с кодом '0000000000000' в кладре не существует.

После выполнения запроса
Код:
select
	*
from
	school_adres
	left join kladr kl_rayon on kl_rayon.code = rayon
Осталась только вторая запись, а первая пропала.
Сергей089 вне форума Ответить с цитированием
Старый 18.04.2013, 09:21   #8
Сергей089
Форумчанин
 
Регистрация: 22.09.2008
Сообщений: 214
По умолчанию

Выполнил какие-то шаманские действия, и ADOQuery стал нормально работать

Просто так захотел посмотреть на результат запроса
Код:
select
	*
from
	school_adres
	left join
	(
		select
			'0000000000000' code,
			'' socr,
			'' name
		union
		select
			code,
			socr,
			name
		from
			kladr
	) kl_rayon on kl_rayon.code = rayon
ADOQuery при старте стал ругаться
Цитата:
Operation not allowed on a unidirectional dataset
Тогда оформил этот запрос как представление. Ошибка исчезла.
Вставил назад первый запрос
Код:
select
	*
from
	school_adres
	left join kladr kl_rayon on kl_rayon.code = rayon
В результате две строки как и положено!

Если создать новый проект, кинуть на форму ADOConnection, ADOQuery, DataSource и DBGrid, выставить в ADOQuery серверный курсор, выполнить запрос
Код:
select
	*
from
	school_adres
	left join kladr kl_rayon on kl_rayon.code = rayon
то получается такая же проблема. А после выполнения вышеописанных действий, новая программка тоже начинает правильно работать.

В чем может быть причина? Что-то мне такое решение не нравится.

Последний раз редактировалось Сергей089; 18.04.2013 в 09:25.
Сергей089 вне форума Ответить с цитированием
Старый 18.04.2013, 09:41   #9
Сергей089
Форумчанин
 
Регистрация: 22.09.2008
Сообщений: 214
По умолчанию

Сравнив свойства ADOQuery до и после моего шаманства, увидел

до
Код:
CursorLocation = clUseServer
CursorType = ctKeyset
LockType = ltOptimistic
после
Код:
CursorLocation = clUseServer
CursorType = ctStatic
LockType = ltReadOnly
Опять сделал новый проект, сразу выставил эти 3 свойства, и ADOQuery заработал правильно!

Пойду почитаю про них...
Сергей089 вне форума Ответить с цитированием
Старый 18.04.2013, 10:55   #10
Сергей089
Форумчанин
 
Регистрация: 22.09.2008
Сообщений: 214
По умолчанию

Почитал про эти свойства. Пока меня это устраивает.
Проблема решена
Всем спасибо!
Сергей089 вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
добавление записи через adoquery kolyan2288 БД в Delphi 2 07.01.2013 22:18
Фильтр через CheckListBox не конвертирует, не отображает null reihtmonbern БД в Delphi 1 24.05.2012 11:24
Добавление записи в ADOQuery. lovetolaugh БД в Delphi 10 11.06.2011 19:07
Добавление записи в БД средствами ADOQuery. lovetolaugh Помощь студентам 0 16.05.2011 19:02
ADOQuery ничего не отображает kopoba БД в Delphi 3 05.06.2009 10:40