Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Базы данных и репортинг > Переход по номеру записи


Автор: budg 19.5.2006, 14:51
ADO, MSSQL2000

Прошу подсказать вариант быстрого перехода по номеру записи в базе(таблице). Сейчас использую "MoveBy(n)", однако, при n=200000 (или даже 100000) переход занимает очень много времени - десятки минут. Причем заметил - от мощности  сервера зависит слабо.
Есть ли другие, не-MoveBy варианты?

Заранее спасибо.

 smile  

Автор: Vit 19.5.2006, 15:38
Мдя, тяжёлый случай.... Общие рекомендации такие:

1. Забыть даже о возможности существования компонента TADOTable, а помнить что есть только TADOQuery
2. Забыть о существовании каких-то номеров записи, их нет и быть не может, таблица это не массив чтоб к записи обращаться по номеру. У вас должно быть поле в таблице которое каждую запись идентифицирует, например автоинкремент и по нему ключ. Если нет - добавить.
3. Использовать SQL запрос для получения данных из конкретной записи
4. Забыть о возможности чтения более десятка записей одним запросом, чтение миллиона записей... это нечто... Вы будете приятно удивлены что ваши "минуты" при переходе плавно превратятся в дни при запуске нескольких экземпляров программы на одной базе данных...
5. Прочитать вот эту статью: http://vingrad.ru/ART-DELPHI-002171 

Автор: budg 19.5.2006, 16:06
Согласен - случай тяжелый. 

ADOTable нет и в помине. Пользуюсь ADOQuery, собсно вот:

Код

procedure TForm1.FormCreate(Sender: TObject);
begin

 With ADOQuery1 do
    begin
    Close;
    SQL.Clear;
    SQL.Add('SELECT ID, OfferID FROM OfferEntry');
    Open;
    First;
//    MoveBy(11000);
    NumZ.Caption:=IntToStr(RecNo);
    NumRec:=RecNo;
    CurID:=StrToInt(ADOQuery1.FieldByName('ID').Value);
    CurOfID:=StrToInt(ADOQuery1.FieldByName('OfferID').Value);
    stop:=false;
    end;
//  Edit1.Text:='';

end;


и далее:

Код

procedure TForm1.Button1Click(Sender: TObject);
    var
        found: string;
        cur: string;
        cur1: integer;
        colR: integer;
    label bg;
    label st;
    begin
    NumRec:=0;
    cl:=1;
    com:=0;
    colR:=0;
    LDeleted.Caption:='УДАЛЕНО';
    ADOConnection1.Connected:=true;
    if Edit1.Text='' then ADOQuery1.MoveBy(0)
                     else
                     ADOQuery1.MoveBy(StrToInt(Edit1.Text));

......


Это всё не от хорошей жизни. Вынужден перебирать каждую строку записи в огромной таблице, ибо не знаю заранее её логического значения.
Может использовать поле с нумерованными строками? Но ведь перебор все равно не исчезнет. Да ладно с ним, с перебором, можно подождать. Мне надо выйти быстро на нужный номер записи, а вот это самое проблематичное... smile
Экземпляр проги только один - у меня. Ни у кого более его не будет.
За ссылку спасибо, почитаю.. smile

 

Автор: skyboy 19.5.2006, 16:40
budg, пойми, когда у тебя в файле(чем и является база данных) размер блока(строка в отношениий структуры базы данных) нефиксирован, то при переходе от одной строки к другой надо постоячнно искать конец текущей блока, потом - конец следующего и т.д., т.к. бессмыслено работать как с массивом: <offset>=<counter>*<record_size> - ведь резмер записи не одинаков. А когда ты объявишь индекс, то доступ к строке с нужным тебе индексом(использование автоинкремента и даст тебе номер строки) буде облегчен - ядро будет "в курсе", что строка с индексом 5(5 строка!) находится в файле по смещению 200 байт, а строка с индексом 50 - по смещению в 1012354 байта. При этом не надо будет перебирать данные в поисках признака конца строки - смещения будут заданы явно. Конечно, если у тебя была удалена запись с индексом 42, а последняя строка имеет автоинкрементый индекс 2201, то никто не "заполнит" пустую позицию с номером-индексом 42, но ведь ты при проверке можешь удостовериться, что требуемая запись существует, правда ведь? ;) Так что с индексом намного проще и быстрее.. 

Автор: Vit 19.5.2006, 18:21
Цитата(budg @  19.5.2006,  07:06 Найти цитируемый пост)
Вынужден перебирать каждую строку записи в огромной таблице, ибо не знаю заранее её логического значения.
Может использовать поле с нумерованными строками? Но ведь перебор все равно не исчезнет. Да ладно с ним, с перебором, можно подождать. Мне надо выйти быстро на нужный номер записи, а вот это самое проблематичное... 
Экземпляр проги только один - у меня. Ни у кого более его не будет


Так... ставим с головы на ноги... 

1. В чём логика проверки каждой строки? Опиши пожалуйста, возможно что это можно сделать одним запросом, если нет, то это надо писать на T-SQL курсорами, это даст выигрышь в производительности примерно 2-3 порядка в самом плохом случае, в хорошем - на 5-6 порядков.

2. Нумеровать строки надо обязательно!

3. Тот запрос что вы привели делать не в коем случае нельзя если таблица больше 1000 записей, убьете не только сервак но и сетку.

4. MoveTo использовать не желательно. Используйте запрос строки по её номеру который проиндексирован...


А вообще желательно описать здесь конкретно задачу, мы помозгуем вместе. Я уже года 2 как занимаюсь тем что переписываю такие вот ужастики в нормальный вид, и то что работало неделю почему-то начинает работать по 10 минут.... Скорее всего вы просто не с правильного конца решаете задачу, это очень частая проблема, особенно для тех кто только что перешел с локальных баз данных или вообще только начал работать с базами данных. Сервера могут работать по логике которую вы пытаетесь создать, но как правило это даёт примерно такой-же эффект как использование микроскопа для забивания гвоздей, гвозди забиваются но очень криво и замена окуляра помогает незначительно...
 

Автор: budg 22.5.2006, 09:25
Задача, собственно, в следующем...
Есть в базе две таблицы (вполне большого размера). Назовем их условно А и В. Таблица В подчинена таблице А по ринципу "один ко многим", т.е. одной строке таблицы А соответствует множество строк таблицы В ( от одной до нескольких сотен). Программист который создавал базу,  построил связь таблиц через код проги клиента по некому полю ID . При удалении строки в табл. А, должны были удаляться строки в талице В. Все просто. Однако, он то ли забыл, то ли была еще какая задумка, но удаление строк в таблице В не реализовано. Удаляется только строка в табл. А. Строки в табл. В с данным значением индексного поля "подвисают". Хуже всего, что при создании новых записей, возможно пересечение значений индексных полей с "подвисшими" записями, что приводит к появлению ложной информации. Периодически это проявлялось. Впрочем, это можно рассматривать как "фичу", поскольку  удается восстановить случайно удаленные данные(пользователи все равно удаляют, несмотря на предупреждения).  Хотя это немного трудоемко. 
Для очистки таблицы от таких "подвисших" записей написал небольшую прогу.  Все получилось неплохо, кроме одного - сканирования таблицы и перехода к нужной записи. Проверять приходиться каждую строку  в табл. В на предмет наличия заголовка в табл. А. Отсюда все проблемы.  
Если подскажете, буду премного благодарен... smile   

Автор: TheCetus 22.5.2006, 10:24
Судя по описанию все 'очистки' можно сделать одним SQL запросом...
опиши подробнее критерий удаления подвисших записей 

Автор: budg 22.5.2006, 11:32
Цитата(TheCetus @ 22.5.2006,  10:24)
Судя по описанию все 'очистки' можно сделать одним SQL запросом...
опиши подробнее критерий удаления подвисших записей


можно одним запросом - это точно...

А критерий очень простой - 

- текущая строка
- если значения поля ID из табл. В нет в столбце ID табл. А - зачит удаляем (или заносим в список для удаления).
- след строка

Впрочем, пытаюсь создать некий программный инструмент не только для удаления, но и восстановления данных 

Автор: Vit 22.5.2006, 15:31
Ну обычный outer join даёт тебе список строк которые не прилинкованы. Что с ними делать? 

1) Или удалить:
Код

Delete from B
Where id not in (Select id From A)



2) Или перенести в новую таблицу и там с ними разбираться:

Код

Select * 
Into MyNewTable
From B
Left Outer Join A on A.id=B.id
Where A.id is null

Delete from B
Where id not in (Select id From A)



3) Или создать для них корреспондентные записи в мастер таблице:

Код

Insert into A (id, чего-то там по умолчанию)
Select B.id 
Into MyNewTable
From B
Left Outer Join A on A.id=B.id
Where A.id is null


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

Автор: budg 22.5.2006, 16:54
Спасибо.
Буду разбираться. 

Автор: Vit 22.5.2006, 19:09
Ну спрашивай если чего... пока твоя задача из раздела когда никаких пошаговых проходов вроде не нужно, как и клиентских програм... Я таких случаев много поисправлял, Query Analyser практически единственное что нужно, да немного анализа что творится, а исправляется всё это несколькими запросами. Но даже если и нужен проход по записям, то и он организуется без всякой программы, прямо в Query Analyser при щпомощи курсоров, будет на 2-3 порядка медленнее чем запросом, но на много порядков быстрее чем с помощью клиентского приложения. 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)