Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2008.11.30;
Скачать: CL | DM;

Вниз

Оптимизация запроса   Найти похожие ветки 

 
kadr   (2008-05-12 10:30) [0]

Здравствуйте.  
Имеем таблицу
CREATE TABLE DATA (
   ID        DOM_ID NOT NULL /* DOM_ID = INTEGER NOT NULL */,
   DT_NAME   DOM_STR20 /* DOM_STR20 = VARCHAR(20) */,
   DT_VALUE  DOM_BIGINT /* DOM_BIGINT = BIGINT */,
   DATE_     DOM_DATE_ /* DOM_DATE_ = TIMESTAMP DEFAULT current_timestamp */
);
CREATE INDEX IDX_DATE_ ON DATA (DATE_);
CREATE INDEX IDX_NAME ON DATA (DT_NAME);

Необходимо выбрать значения dt_value на последнюю дату и dt_name

Использую запрос:
select d.dt_name, s.dt_value
from (select dt_name, max(date_) as date_ from data group by 1) d
   inner join (select dt_value, dt_name, date_  from data) s
       on s.dt_name = d.dt_name and s.date_ = d.date_

или хранимку
CREATE PROCEDURE LAST_DATA
returns (
   dt_value integer,
   dt_name varchar(20))
as
declare variable max_date timestamp;
declare c cursor for (select distinct(dt_name) from data
);
begin

      open C;
        while (1 = 1) do
        begin
          fetch C into :dt_name;
          if (row_count = 0) then
            leave;

          select first 1 dt_name, dt_value
          from data
          where dt_name = :dt_name
          order by date_ DESC
          into
           :dt_name, dt_value;

          suspend;
        end
        close C;

end^

вроде все ничего, но приблизительный объем записей таблицы 3 и более миллионов. На таких объемах что запрос, что хранимка отрабатывает около 30 сек.
Есть ли мысли по поводу оптимизации?


 
ANB   (2008-05-12 10:35) [1]

1. Сделать составной индекс Имя, дата


 
Sergey13 ©   (2008-05-12 10:40) [2]

> [0] kadr   (12.05.08 10:30)

> inner join (select dt_value, dt_name, date_  from data) s

А стОит ли джойнить с селектом? Почему не просто с таблицей? Ну и индекс составной, как [1] ANB   (12.05.08 10:35) предложил.


 
ЮЮ ©   (2008-05-12 10:42) [3]

Нормализовать таблицу, ибо, исходя из желаеемого,
 ID        DOM_ID NOT NULL /* DOM_ID = INTEGER NOT NULL */,
 DT_NAME   DOM_STR20 /* DOM_STR20 = VARCHAR(20) */,
— это одна сущность, связанная связью один ко многим с другой.

Связь по s.dt_name = d.dt_name всяко тяжелее, ИМХО, чем по целым полям.
да и 3 и более миллионов разобъется на сомножители.


 
ЮЮ ©   (2008-05-12 10:47) [4]

А сколько из этих 30 секунд выполняется подзапрос
 select dt_name, max(date_) as date_ from data group by 1
?


 
ANB   (2008-05-12 12:11) [5]


> ЮЮ ©   (12.05.08 10:47) [4]
> А сколько из этих 30 секунд выполняется подзапрос
>  select dt_name, max(date_) as date_ from data group by
> 1
> ?

Если количество вариантов по dt_name невелико (в пределах 1000), то должен при наличии составного индекса шустро работать.
А если велико (500 тысяч, например), то никакая оптимизация не поможет. 30 сек - это уже ОЧЕНЬ быстро.


 
kadr   (2008-05-12 13:56) [6]

количество dt - до 2500.
Сейчас провоже тесты - результаты через полчаса-час будут в ветке


 
kadr   (2008-05-12 14:53) [7]

Результаты испытаний на 1 млн записей:

/////////////////////////////////////////////////////////////////////////
ИСПОЛЬЗОВАНИЕ ИНДЕКСОВ  IDX1 dt_name, IDX2 date_
/////////////////////////////////////////////////////////////////////////

select dt_name, max(date_) as date_ from data group by 1

>План
>PLAN (DATA ORDER IDX_NAME)
------ Performance info ------
Prepare time = 0ms
Execute time = 57s 562ms

//*********************************************
select d.dt_name, s.dt_value
from (select dt_name, max(date_) as date_ from data group by 1) d
   inner join (select dt_value, dt_name, date_  from data) s
       on s.dt_name = d.dt_name and s.date_ = d.date_

>План
>PLAN JOIN (D DATA ORDER IDX_NAME, S DATA INDEX (IDX_DATE_))
------ Performance info ------
Prepare time = 15ms
Execute time = 57s 282ms

//*********************************************
select d.dt_name, s.dt_value
from (select dt_name, max(date_) as date_ from data group by 1) d
   inner join data s
       on s.dt_name = d.dt_name and s.date_ = d.date_

>План
>PLAN JOIN (D DATA ORDER IDX_NAME, S INDEX (IDX_DATE_))
------ Performance info ------
Prepare time = 0ms
Execute time = 57s 672ms

/////////////////////////////////////////////////////////////////////////
ИСПОЛЬЗОВАНИЕ СОСТАВНОГО ИНДЕКСА (DT_NAME, DATE_)
/////////////////////////////////////////////////////////////////////////

select dt_name, max(date_) as date_ from data group by 1

>План
>PLAN (DATA ORDER IDX_NAME)
------ Performance info ------
Prepare time = 0ms
Execute time = 20s 140ms

//*********************************************
select d.dt_name, s.dt_value
from (select dt_name, max(date_) as date_ from data group by 1) d
   inner join (select dt_value, dt_name, date_  from data) s
       on s.dt_name = d.dt_name and s.date_ = d.date_

>План
>PLAN JOIN (D DATA ORDER IDX_NAME, S DATA INDEX (IDX_NAME))
------ Performance info ------
Prepare time = 0ms
Execute time = 20s 109ms

//*********************************************
select d.dt_name, s.dt_value
from (select dt_name, max(date_) as date_ from data group by 1) d
   inner join data s
       on s.dt_name = d.dt_name and s.date_ = d.date_

>План
>PLAN JOIN (D DATA ORDER IDX_NAME, S INDEX (IDX_NAME))
------ Performance info ------
Prepare time = 0ms
Execute time = 20s 203ms


 
kadr   (2008-05-12 14:56) [8]

>> 3 Попытался немного нормализовать...
Создал таблицу


CREATE TABLE DATA_VALUE (
   ID        DOM_ID NOT NULL /* DOM_ID = INTEGER NOT NULL */,
   ID_DT     DOM_INT /* DOM_INT = INTEGER */,
   DT_VALUE  DOM_INT /* DOM_INT = INTEGER */,
   DATE_     DOM_DATE_ /* DOM_DATE_ = TIMESTAMP DEFAULT current_timestamp */
);
ALTER TABLE DATA_VALUE ADD CONSTRAINT FK_DATA_VALUE_1 FOREIGN KEY (ID_DT) REFERENCES DATA (ID);


Теперь не догоню, как выбрать все нужное запросом...
На ум приходит только такое:


CREATE PROCEDURE LAST_DATA1
returns (
   dt_name varchar(20),
   dt_value integer)
as
declare variable dt_date_ timestamp;
declare variable dt_id integer;
declare c cursor for (select id_dt, max(date_) from data_value group by 1);
begin
      open C;
        while (1 = 1) do
        begin
          fetch C into :dt_id, :dt_date_;
          if (row_count = 0) then
            leave;

          select first 1 dn.dt_name, dv.dt_value
          from data dn
           inner join data_value dv on (dv.id_dt = dn.id)
          where dv.id_dt = :dt_id and dv.date_ = :dt_date_
          into
            :dt_name, dt_value;

          suspend;
        end
        close C;
end


 
Sergey13 ©   (2008-05-12 15:41) [9]

> [8] kadr   (12.05.08 14:56)
> Попытался немного нормализовать...

А я бы попробовал немного ДЕнормализовать. Добавить признак актуальности в TABLE DATA из [0] и при вводе новой записи в нее ставить (в тригере) актуальность для нее в истину, при этом бывшей актуальной соответствующей записи менять на ложь. Этим самым избавляешься от поиска максимальной даты и значения для него.


 
kadr   (2008-05-12 16:13) [10]

>> 9 Уже думал об этом. Это будет САМЫЙ быстрый способ выборки. Хотелось обойтись тем, что есть, да видно не получиться :)


 
ANB   (2008-05-12 16:50) [11]


> kadr   (12.05.08 14:53) [7]

Судя по индексам у тебя к планам новый индекс не прикрутился.
Грохни одиночные индексы для чистоты эксперимента. Или прохинтуй запрос.
(я не умею хинтовать ФБ).

Некоторое ускорение - только кажущееся. Эт просто сервер таблицу закэшировал.


 
ANB   (2008-05-12 16:51) [12]


> Sergey13 ©   (12.05.08 15:41) [9]
> > [8] kadr   (12.05.08 14:56)
> > Попытался немного нормализовать...
>
> А я бы попробовал немного ДЕнормализовать. Добавить признак
> актуальности в TABLE DATA из [0] и при вводе новой записи
> в нее ставить (в тригере) актуальность для нее в истину,
>  при этом бывшей актуальной соответствующей записи менять
> на ложь. Этим самым избавляешься от поиска максимальной
> даты и значения для него.

Если отчет нужно гонять не чаще раза в день - то излишне. Взамен ускорения отчета получим тормоза при вставке записей.


 
kadr   (2008-05-12 17:34) [13]

Это не отчет.
Есть датчики и их значения должны писаться в БД с интервалом 10 сек. Датчиков максимум 400 (пока). Ну и затем ессесно отчеты по периодам


 
Игорь Шевченко ©   (2008-05-12 21:52) [14]


> (я не умею хинтовать ФБ).


Интересно, а кто-нибудь умеет ?


 
PEAKTOP ©   (2008-05-13 00:20) [15]

> Интересно, а кто-нибудь умеет ?

Дима Еманов, Влад Хорсун, Ded - точно умеют :)
----------------------------------------------
Топик-стартеру:
1) А что, классика не канает ?

.........................
FOR
 SELECT D.DT_NAME, MAX(D.DATE_) AS MAX_DATE
 FROM   DATA D
 GROUP BY D.DT_NAME
 INTO   :DT_NAME, :MAX_DATE
DO
 BEGIN
 SELECT FIRST 1 D1.DT_VALUE
 FROM   DATA D1
 WHERE (D1.DATE_ = :MAX_DATE)
     AND (D1.DT_NAME = :DT_NAME)
 INTO   :DT_VALUE;
 SUSPEND;
 END
..................

Интересен план такого запроса.

2)Если БД реалтайм и по этим данным буду строится отчеты, то имеет смысл ее денормализовать. Создаешь еще одну таблицу с полями DT_NAME, DT_VALUE, MAX_DATE и обновляешь ее записи на триггерах вставки+изменения таблицы DATA. Тогда вообще все будет шустро


 
Виталий Панасенко(дом)   (2008-05-13 00:44) [16]

а создать использовать индекс по убыванию не пробовал?


 
kadr   (2008-05-13 13:36) [17]

>> 15 А процедура в посте 8 не то, что подсказываете Вы? :)

>> 16
А вот слона то мы и не заметили :))
Действительно создал индекс по убыванию и получил:


CREATE PROCEDURE LAST_DATA_COPY
returns (
   dt_value integer,
   dt_name varchar(20))
as
declare variable max_date timestamp;
declare c cursor for (select distinct(dt_name) from data
);
begin

      open C;
        while (1 = 1) do
        begin
          fetch C into :dt_name;
          if (row_count = 0) then
            leave;

          select first 1 dt_name, dt_value
          from data
          where dt_name = :dt_name
          order by date_ DESC
          into
           :dt_name, dt_value;

          suspend;
        end
        close C;

end


Plan:
(DATA ORDER DATA_IDX1)
------ Performance info ------
Prepare time = 0ms
Execute time = 8s 781ms

При использовании рецепта [9] - [10] следующие результаты:

CREATE PROCEDURE LAST_DATA
returns (
   dt_value integer,
   dt_name varchar(20))
as
declare variable max_date timestamp;
declare c cursor for (select distinct(dt_name) from data
);
begin

/*$$IBEC$$        open C;
        while (1 = 1) do
        begin
          fetch C into :dt_name;
          if (row_count = 0) then
            leave;

          select first 1 dt_name, dt_value
          from data
          where dt_name = :dt_name
          order by date_ DESC
          into
           :dt_name, dt_value;

          suspend;
        end
        close C; $$IBEC$$*/

  for select dt_name, dt_value from data where is_new = 1 into :dt_name, :dt_value do
   suspend;

end


Plan:
(DATA NATURAL)
------ Performance info ------
Prepare time = 0ms
Execute time = 1s 469ms

Но возникает вопрос, что будет более нагружать сервер при записи в БД отработка триггера при вставке в DATA (использование рецепта [9])

CREATE trigger data_bi1 for data
active before insert position 1
AS
begin
 if (new.dt_value > 0) then
  begin
   update data set is_new = 0 where dt_name = new.dt_name and is_new = 1;
   new.is_new = 1;
  end
end

или изменения индекса по data_  на бОльших объемах данных



Страницы: 1 вся ветка

Текущий архив: 2008.11.30;
Скачать: CL | DM;

Наверх




Память: 0.53 MB
Время: 0.015 c
2-1224589591
SkyN
2008-10-21 15:46
2008.11.30
как правильно освобождать память от динамического массива?


4-1201114905
Artem
2008-01-23 22:01
2008.11.30
Как заменить иконку?


15-1222358569
Real
2008-09-25 20:02
2008.11.30
Почему Microsoft игнорирует Delphi?


15-1222744713
Maxer123
2008-09-30 07:18
2008.11.30
Urban Kraftway K23


2-1224766335
Lamer6666
2008-10-23 16:52
2008.11.30
Kak obratit sa k elementu formi po ego imeni?