C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

стр.: (2) < [1] 2 >
как лучше выводить огромный ровсет?
Tertium
Отправлено: 11.07.2005, 13:09


Машинист паровоза

Группа: Почетный участник
Сообщений: 192



Сервер MSSQL2k. Цепляюсь TADOConnection + TADOQuery.

у меня есть громадная таблица — записей 100000-1000000. В ней [int][int][datetime][varchar]. Задача выводить её в гриде, но при этом идентифицировать для юзера непонятные ему значения интов (1 — "событие открытия двери", 2 — "событие выключения аппарата" и тд), даты выводить разбивая на две колонки (дата и время), а из varchar выводить первые 100 символов и ставить многоточие.
Короче, использовать DBGrid нельзя, поскоку надо каждую запись немного видоизменять перед выводом.
А просто загрузить select * в грид не могу — слишком ровсет огромный. Вывод такой: надо запросом брать в 2-3 раза больше чем на странице умещается, затем по мере скрлллирования подгружать. Но не охота руками это делать. Может есть способ делать это как DBGrid, но подменив метод отображения в грид данных? Там-то эта логика вывода больших ровсетах уже реализована. Ктонть пробовал наследовать DBGrid в таком аспекте? Или может есть ещё какие пути решения задачи?
AVC
Отправлено: 11.07.2005, 13:26


Ветеран

Группа: Модератор
Сообщений: 1583



QUOTE

Короче, использовать DBGrid нельзя, поскоку надо каждую запись немного видоизменять перед выводом.

Заблуждаетесь. Еще как можно.

QUOTE

А просто загрузить select * в грид не могу — слишком ровсет огромный

Правильно. И не ненадо его грузить. Пожалейте пользователя.

Общее правило — ограничивать размер выборки как можно раньше. Может стоит изменить постановку задачи? Зачем пользователю показывать весь 1 000 000 записей? Все равно больше сотни он просто не воспримет. Может стоит подумать об "условиях отбора"?
greyich
Отправлено: 11.07.2005, 15:42


Не зарегистрирован







самое простое и как я понимаю самое легкое — отдать все вычисления на растерзание sql-server'у. На то он и сервер чтобы это всё обработать. Предлагаю использовать представления (пользователь и не догадается что это не таблица). что-то примерно так


select int1, ext_int1 = case ext_int1 when '1' then 'событие открытия двери', when '2' then 'событие выключения аппарата' end, date1 = date(dateTime1), time1 = (datetime1) from table1


текст писался прям в браузере без проверки, если что юзай books online
Guest
Отправлено: 11.07.2005, 15:57


Не зарегистрирован







2AVC: в том-то и засада, что юзер хочет мочь видеть любую часть ровсета. Он конечно задаёт условия начальные (за период с _ по _), но ведь может задать ваще с 1900 до 2100.
2greyich: интересная мысль, логично
Gedeon
Отправлено: 11.07.2005, 16:57


Ветеран

Группа: Модератор
Сообщений: 1742



QUOTE (greyich @ 11/07/2005, 15:42)
select int1, ext_int1 = case ext_int1 when '1' then 'событие открытия двери', when '2' then 'событие выключения аппарата' end, date1 = date(dateTime1), time1 = (datetime1) from table1

А не проще ли как все нормальные люди сделать связанную таблицу и выбирать из нее, да и побыстрее будет и перечислять не надо, а по поводу первых символов SUBSTR поможет?
Tertium
Отправлено: 11.07.2005, 17:14


Машинист паровоза

Группа: Почетный участник
Сообщений: 192



2Gedeon: да это уже детали. самая фишка была в представлении. мне чото в голову сразу не пришло. вот промежуточный результат:
SQL
select (LTRIM(RTRIM(STR(datepart(yy,last_query))))+'[color=orange]-'[/color]+
LTRIM(RTRIM(STR(datepart(mm,last_query))))+'[color=orange]-'[/color]+
LTRIM(RTRIM(STR(datepart(dd,last_query)))) ) as 'дата',
(LTRIM(RTRIM(STR(datepart(hh,last_query))))+':'+
LTRIM(RTRIM(STR(datepart(n,last_query))))+':'+
LTRIM(RTRIM(STR(datepart(ss,last_query)))) ) as 'время',
place_no as 'номер места',
club_id as 'ID зала',
(case event_id
when 1 then 'открытия двери'
when 2 then 'закрытие двери'
when 3 then 'очистка бухгалтерии'
when 4 then 'включение'
when 5 then 'выключение'
when 6 then 'смена платы'
ELSE 'неизвестное событие'
end) as 'тип события'
from Events order by last_query desc

работает с десятитысячной таблицей ваще незаметно.
кстати вот это уродство:
(LTRIM(RTRIM(STR(datepart(hh,last_query))))+':'+
LTRIM(RTRIM(STR(datepart(n,last_query))))+':'+
LTRIM(RTRIM(STR(datepart(ss,last_query)))) ) as 'время'
по человечески возможно написать под mssql? А то функций date и time в нём нету...

PS: ну не ругайтесь, сделал связанную таблицу

Отредактировано Tertium — 11/07/2005, 17:48
avc*
Отправлено: 11.07.2005, 17:19


Не зарегистрирован







QUOTE

2AVC: в том-то и засада, что юзер хочет мочь видеть любую часть ровсета. Он конечно задаёт условия начальные (за период с _ по _), но ведь может задать ваще с 1900 до 2100.

Он (user) предупрежден, что чем шире диапазон, тем дольше ждать? Начинаем показ с опроса диапазона. Хочет видеть много, пусть платит временем.
Select и View в вашем случае это одно и тоже и, естественно, подготовку данных должен делать сервер (это я даже не оговаривал, а о возможностях грид — просто замечание, если меня не так поняли smile.gif ).


Пока писал ответ он уже устарел.

Отредактировано AVC — 11/07/2005, 16:21
Guest
Отправлено: 11.07.2005, 20:45


Не зарегистрирован







всё, убрал нахрен связанную таблицу. С ней-то конечно прикольней запрос выглядит , но скорость выполнения select'а по 10000 записей — 7 секунд. С case...when...then — 1 секунда. Так что...
Tertium
Отправлено: 11.07.2005, 21:07


Машинист паровоза

Группа: Почетный участник
Сообщений: 192



тьфу блин. с фаером перемутил — не авторизовал сайт
olegenty
Отправлено: 12.07.2005, 07:02


Ветеран

Группа: Модератор
Сообщений: 2412



QUOTE (Guest @ 11/07/2005, 21:45)
всё, убрал нахрен связанную таблицу. С ней-то конечно прикольней запрос выглядит , но скорость выполнения select'а по 10000 записей — 7 секунд. С case...when...then — 1 секунда. Так что...

а как же индексы?
Tertium
Отправлено: 12.07.2005, 23:22


Машинист паровоза

Группа: Почетный участник
Сообщений: 192



а что индексы? видимо никак. есть но не помогают видимо. может, конечно я делаю неправильно, но:

SQL
CREATE TABLE [Events] (
[id] [int] IDENTITY (1, 1) NOT NULL ,
[event_id] [int] NOT NULL DEFAULT (0),
[place_no] [tinyint] NOT NULL DEFAULT (0),
[club_id] [int] NOT NULL DEFAULT (0),
[last_query] [datetime] NOT NULL DEFAULT GETDATE(),
PRIMARY KEY CLUSTERED
(
[id]
)
)

CREATE TABLE [event_names] (
[event_id] [int] NOT NULL ,
[event_name] [char] (255) COLLATE Cyrillic_General_CI_AS NOT NULL ,
PRIMARY KEY CLUSTERED
(
[event_id]
)
)


SQL
select bla bla bla from events,event_names where events.event_id=event_names.event_id order by bla bla bla

10000 = 7 sec

SQL
select bla bla (case (event_id)
when 1 then 'идём на фиг'
when 2 then 'продолжаем идти'
when 3 then 'идём на фиг до конца'
end) as 'имя события' from events

10000 = 1 sec

как ещё мона написать с таблицей-словарём?
olegenty
Отправлено: 13.07.2005, 07:01


Ветеран

Группа: Модератор
Сообщений: 2412



индекс должен быть ещё и по bla-bla-bla, раз уж сортируешь...
Tertium
Отправлено: 13.07.2005, 15:47


Машинист паровоза

Группа: Почетный участник
Сообщений: 192



не поленился, пересоздал таблицы, сделал индекс Events сначала одновременно по двум полям, по каким идёт сортировка, потом по каждомук полю в отдельности. тот же результат в обоих случаях. 7 секунд. против 1 с case. так что bla или не bla, а волшебства не произошло smile.gif

Отредактировано Tertium — 13/07/2005, 15:48
Gedeon
Отправлено: 13.07.2005, 17:13


Ветеран

Группа: Модератор
Сообщений: 1742



Не должно такого быть!

Завтра смоделирую ситуацию, проверю, расскажу.
greyICH
Отправлено: 13.07.2005, 18:42


Не зарегистрирован







конечно ждем результатов, но ИМХО запрос на лету формирующий данные из одной таблицы должен работать быстрее, чем сцепление 2-х таблиц.

кроме того места занимает меньше (это если использовать хранимую процедуру) и его гораздо проще изменить если что-то необходимо поменять smile.gif) например изменились правила вывода и цифрам будут соответствовать другие значения — написать еще одну хранимую процедуру дело одной минуты, а если её текст генерить на клиенте, то можно динамически формировать запрос под конкретную ситуацию.

ЗЫ: только не считайте, что я кидаю камни в чей-то огород. очень уважаю мнение Gedeon и olegenty (в bookmarks!!!!)
Guest
Отправлено: 13.07.2005, 22:06


Не зарегистрирован







2greyICH: присоединяюсь насчёт того,что "запрос на лету формирующий данные из одной таблицы должен работать быстрее, чем сцепление 2-х таблиц. "
плюс вариант с case позволяет отловить и те записи в которых событие имеет неизвестный науке вид smile.gif а такое возможно, никогда не знаешь, что электронщикам в голову придёт:)

2Gedeon: код в помощь smile.gif

создание

SQL
create database gambling
go
use gambling
go

CREATE TABLE [Events] (
[id] [int] IDENTITY (1, 1) NOT NULL ,
[event_id] [int] NOT NULL DEFAULT (0),
[place_no] [tinyint] NOT NULL DEFAULT (0),
[club_id] [int] NOT NULL ,
[last_query] [datetime] NOT NULL DEFAULT (getdate()),
PRIMARY KEY CLUSTERED
(
[id]
)
)
GO

CREATE INDEX [my_fucking_index] ON [Events]
(
[last_query] desc,
[club_id] asc )
go

CREATE TABLE [event_names] (
[event_id] [int] NOT NULL ,
[event_name] [char] (255) COLLATE Cyrillic_General_CI_AS NOT NULL ,
PRIMARY KEY CLUSTERED
(
[event_id]
)
)
GO

insert into event_names (event_id,event_name) values(1,'открытие двери')
insert into event_names (event_id,event_name) values(2,'закрытие двери' )
insert into event_names (event_id,event_name) values(3,'очистка бухгалтерии')
insert into event_names (event_id,event_name) values(4,'включение')
insert into event_names (event_id,event_name) values(5,'выключение')
insert into event_names (event_id,event_name) values(6,'смена платы')


заполнение (условно)
CODE

for (int i=0;i<10000;i++)
{
 DataBase.exec(AnsiString().sprintf("insert into events(event_id, place_no, club_id, last_query) values(%d,%d,%d, '%s')",
 rand()%6+1,rand()%127+1,rand()%10,(TDateTime::CurrentDateTime()+rand()%100).FormatString("yyyy-mm-dd hh:nn:ss")));
}


селекты:
1. с case
SQL
select (LTRIM(RTRIM(STR(datepart(yy,last_query))))+'-'+
LTRIM(RTRIM(STR(datepart(mm,last_query))))+'-'+
LTRIM(RTRIM(STR(datepart(dd,last_query)))) ) as 'Дата',
(LTRIM(RTRIM(STR(datepart(hh,last_query))))+':'+
LTRIM(RTRIM(STR(datepart(n,last_query))))+':'+
LTRIM(RTRIM(STR(datepart(ss,last_query)))) ) as 'Время',
place_no as 'Номер места',
club_id as 'ID зала',
(case event_id
when 1 then 'открытия двери'
when 2 then 'закрытие двери'
when 3 then 'очистка бухгалтерии'
when 4 then 'включение'
when 5 then 'выключение'
when 6 then 'смена платы'
ELSE 'неизвестное событие'
end) as 'Событие'
from Events order by last_query desc, club_id


по связанным таблицам:

SQL
select (LTRIM(RTRIM(STR(datepart(yy,last_query))))+'-'+
LTRIM(RTRIM(STR(datepart(mm,last_query))))+'-'+
LTRIM(RTRIM(STR(datepart(dd,last_query)))) ) as 'Дата',
(LTRIM(RTRIM(STR(datepart(hh,last_query))))+':'+
LTRIM(RTRIM(STR(datepart(n,last_query))))+':'+
LTRIM(RTRIM(STR(datepart(ss,last_query)))) ) as 'Время',
place_no as 'Номер места',
club_id as 'ID зала',
event_name as 'Событие'
from Events,event_names where Events.event_id=event_names.event_id order by last_query desc, club_id


Насчёт последнего запроса: связывать таблицы можно только так, или как ещё?..
Tertium
Отправлено: 13.07.2005, 22:11


Машинист паровоза

Группа: Почетный участник
Сообщений: 192



да чтоб этот файрвол!!! smile.gif Предыдущий пост считать моим smile.gif
AVC
Отправлено: 14.07.2005, 08:37


Ветеран

Группа: Модератор
Сообщений: 1583



Немного статистики по Oracle (если интересно)
Исходные данные — таблица из работающей системы 17 полей 2`341`428 записей.
Выполнение запросов на count или получение нескольких первых строк дают примерно одинаковый результат менее 1 сек. Поэтому для замеров открывался курсор и "фетчились" все записи результата.
Итак времена
Просто проход по таблице — 65 сек
Рашифровка ID функцией — 199 сек
Расшифровка связью двух таблиц — 102 сек
Аналог case — 69 сек

PS. Замеры проводились на работающей системе и во время эксперимента в таблицу заносились данные с других станций.
Tertium
Отправлено: 14.07.2005, 09:44


Машинист паровоза

Группа: Почетный участник
Сообщений: 192



вот и я говорю smile.gif
Gedeon
Отправлено: 14.07.2005, 14:18


Ветеран

Группа: Модератор
Сообщений: 1742



Итак, что получилось у меня:
Сервер: CELERON 1000, 256 MB RAM.
Microsoft SQL Server 2000 — 8.00.194

CODE

CREATE TABLE [dbo].[TST_SELECT_SPEED] (
[ID] [int] IDENTITY (1, 1) NOT NULL ,
[EVENT_ID] [int] NOT NULL
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[TST_SELECT_SPEED] WITH NOCHECK ADD
CONSTRAINT [PK_TST_SELECT_SPEED] PRIMARY KEY  CLUSTERED
(
 [ID]
)  ON [PRIMARY]
GO

CREATE  INDEX [SPEED] ON [dbo].[TST_SELECT_SPEED]([EVENT_ID]) ON [PRIMARY]
GO

CREATE TABLE [dbo].[TST_DESCR] (
[ID] [int] IDENTITY (1, 1) NOT NULL ,
[Descr] [varchar] (50) COLLATE Cyrillic_General_CI_AS NOT NULL
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[TST_DESCR] WITH NOCHECK ADD
CONSTRAINT [PK_TST_DESCR] PRIMARY KEY  CLUSTERED
(
 [ID]
)  ON [PRIMARY]
GO

Заполняем
CODE

DECLARE @COUNTER AS INT
SET @COUNTER = 0
WHILE (@COUNTER<1000000)
BEGIN
INSERT INTO dbo.TST_SELECT_SPEED (EVENT_ID) VALUES ((CAST(RAND()*5)+1 AS INT))--Random_Number

SET @COUNTER = @COUNTER + 1
END

Ну и выгребаем записи
CODE

SELECT dbo.TST_SELECT_SPEED.ID, dbo.TST_DESCR.Descr
FROM dbo.TST_DESCR INNER JOIN
    dbo.TST_SELECT_SPEED ON
    dbo.TST_DESCR.ID = dbo.TST_SELECT_SPEED.EVENT_ID
--ORDER BY dbo.TST_SELECT_SPEED.ID

8 секунд, с сортировкой 12
Второй способ
CODE

select dbo.TST_SELECT_SPEED.ID,
(case dbo.TST_SELECT_SPEED.EVENT_ID
when 1 then 'открытия двери'
when 2 then 'закрытие двери'
when 3 then 'очистка бухгалтерии'
when 4 then 'включение'
when 5 then 'выключение'
end) as 'Событие'
FROM dbo.TST_SELECT_SPEED
--ORDER BY dbo.TST_SELECT_SPEED.EVENT_ID

8 секунд, с сортировкой 8 секунд
Я удивлен ohmy.gif .
Ну и сортировка по dbo.TST_SELECT_SPEED.EVENT_ID в обоих случаях дает 8 секунд.
Т.е. никакой семикратной разницы нет! При 1000000 записей результаты одинаковы. По поводу увеличения времени при связанных таблицах с сортировкой по первичному ключу я вообще не понял, надо подумать.
Таблицы простейшие, но это-то и правильно при таком сравнении, надо будет усложнить, проверить результат.
Поэтому считаю связанные таблицы более удобными для практического применения, в этом случае элементарно дополнить изменить справочник, сделайте то же с ХП, если юзер ниче не понимает в сервере или ему туда нельзя. smile.gif
Tertium
Отправлено: 14.07.2005, 15:14


Машинист паровоза

Группа: Почетный участник
Сообщений: 192



я выложил код выше и настаиваю на семикратной разнице. не раз проверял. тестил в родном его Query Analizer. Неужели это из-за пропущенного INNER JOIN??? или это из-за того, что у меня SQLServer стоит на Win2k3 прикрытый 4 сервиспаком? Без SP он вообще не работал.

и как быть в варианте со связанными таблицами с описанным выше случаем, когда возможно неизвестное событие, и его надо выводить?

Отредактировано Tertium — 14/07/2005, 15:19
greyICH
Отправлено: 14.07.2005, 16:00


Не зарегистрирован







уважаемый Gedeon, как мне кажется дает более универсальный способ решения, подходящий к любой проблеме данного рода. спорить в данном случае не решусь.
2Tertium: вообщем вам предложили 2 варианта решения — вам и выбирать. вполне может быть участвуют факторы, которые Вы не замечаете и потому решение будет в любом случае субъективным
Gedeon
Отправлено: 14.07.2005, 16:02


Ветеран

Группа: Модератор
Сообщений: 1742



В точности повторил Ваш код, 10000 записей результат
Связанные таблицы ~700 mS
case ~650 mS
с сортировкой
Связанные таблицы ~550 mS
case ~450 mS
без сортировки


2. Использовать OUTER JOIN
Gedeon
Отправлено: 14.07.2005, 16:17


Ветеран

Группа: Модератор
Сообщений: 1742



И еще очень настоятельно рекомендую Вам присмотреться к 1 посту AVC в этом топике, не выбирайте все записи, сделайте юзеру
TOP 100, TOP 10 PERCENT на худой конец, и отлавливайте перемещение на последнюю запись, а там если нужно уже тащите больше записей, поскольку при таком количестве вопрос времени упирается в вопрос доставки данных на клиента, а не обработки сервером, поэтому выиграть что-то тяжело.
НЕ НУЖНЫ ЮЗЕРУ ВСЕ ЗАПИСИ, он их не в состоянии проанализировать ну никак, тут можно задуматься над функциональностью программы, че-то ему добавить, чтоб унялся smile.gif .
Tertium
Отправлено: 15.07.2005, 02:02


Машинист паровоза

Группа: Почетный участник
Сообщений: 192



2Gedeon
не совсем понимаю такие маленькие цифры smile.gif у меня P-IV 2.6GHz 1GB RAM Win2k3 sp1 MSSQL2k sp4 и при этом я получаю в лучшем случае 1000мс... А у Вас на целероне, значит, 650-700 мс...

Не поленился ещё раз. Проверил. Приводить здесь запросы не буду, скажу только, что переписал с простого t1.id=t2.id на явный иннерджойн, как у вас. Те же 10000 несчастных записей. Ну и те же 8 секунд с сортировкой, 7 — без. Опять же, с case — 1 секунда.
Может быть тут играет роль тестовый кортеж? Да не думаю, тем более что код его генерации я приводил, и вы его вроде повторили.
Повторил с миллионом:
в таблице: индекс на event_id, индекс на {last_query desc, club_id asc} — как в order by.
Да, добрая шутка — WHILE (@COUNTER<1000000) smile.gif) Я успел кофе сварить и выпить (12:07) smile.gif
case с сортировкой — 11сек
case без сортировки — 8 сек
inner join/left outer join без сортировки — приблизительно разброс 43-50 секунд
inner join/left outer join с сортировкой — 3мин 30сек.
Вот так вот.
Я, кстати, понял, у меня большую часть запроса выполнялось раньше преобразование даты-времени через LTRIM(RTRIM(STR(datepart(…)))). Выше показаны результаты с закомментаренным монстром (с млн записей).
Но даже не в преобразовании даты дело — разрыв всё равно велик (8 и 45).
Кстати спор-то пошёл изначально не из-за того, во сколько раз inner join выполняется медленнее, а из-за того, выполняется ли он медленнее вообще. И даже в Ваших результатах case опережает.
Никого не хочу обидеть, но я же не саботирую результаты:)

Да не нужны, не нужны юзеру все записи, не спорю.
Ограничения есть, и по периоду, и по группе машин, даже можно конкретные машины галочками выбирать. НО: юзер теоретически МОЖЕТ затребовать весь ровсет (проставить дикие условия отбора). Даже если и так, он отдастся ему не весь сразу — именно для того мы и используем TDBGrid. Запрос делается как я понимаю "с сих по сих", где "сих" — размер экрана по вертикали:) И так при каждом перетягивании ползунка. Так что не беспокойтесь за юзера, не захлебнётся он smile.gif

2greyICH. Я и так выбрал с первого Вашего поста способ с case. И, кстати, до сих пор не понял, как обрабатывать неизвестное событие в случае не с case’ом. Не, запрос-то написать можно, так ведь он будет тормозный, и, если честно, даже напрягаться не хочется, настолько лаконично всё получается case’ом. Способ со связанными таблицами выглядит поцивилизованней, но так удобен и универсален для данной ситуации. Да и к тому же (по крайней мере, у меня на машине) сильно уступает в скорости.

2all. Есть мнение, что стоит тему закрыть. Всем спасибо за бурную дискуссию. Побольше бы таких интересных тем. Хотя если кто-то что-то хочет сказать, милости просим:)
Gedeon
Отправлено: 15.07.2005, 08:40


Ветеран

Группа: Модератор
Сообщений: 1742



QUOTE (Tertium @ 15/07/2005, 02:02)
Те же 10000 несчастных записей. Ну и те же 8 секунд с сортировкой, 7 — без. Опять же, с case — 1 секунда.
Может быть тут играет роль тестовый кортеж? Да не думаю, тем более что код его генерации я приводил, и вы его вроде повторили.
Повторил с миллионом:
в таблице: индекс на event_id, индекс на {last_query desc, club_id asc} — как в order by.
Да, добрая шутка — WHILE (@COUNTER<1000000) smile.gif) Я успел кофе сварить и выпить (12:07) smile.gif
case с сортировкой — 11сек
case без сортировки — 8 сек
inner join/left outer join без сортировки — приблизительно разброс 43-50 секунд
inner join/left outer join с сортировкой — 3мин 30сек.
Вот так вот.
Я, кстати, понял, у меня большую часть запроса выполнялось раньше преобразование даты-времени через LTRIM(RTRIM(STR(datepart(…)))). Выше показаны результаты с закомментаренным монстром (с млн записей).

Не, не, не не спешите закрывать, для 10000 записей 8 секунд? Разбирайтесь с сервером, что-то не то, такое кол-во записей для SQL Server — это ерунда. Я проверял на 10000 записей полностью скопировав запрос, у Вас такие времена огромные, что-то явно не то с сервером, план запроса посмотрите, время с case у нас одинаковое, и похоже упирается в тот же транспорт — 8 секунд, поэтому я явно подозреваю что-то с сервером, кстати на испытываемой мною машине крутится серверная часть клиент-банка, плюс батники каждые 5 минут криптуют файлы, так что Вам есть над чем задуматься.
Если будет затишье на основном сервере попробую там (2хР4 2 ГБ ОЗУ + RAID6).

Отредактировано Gedeon — 15/07/2005, 08:47
olegenty
Отправлено: 15.07.2005, 09:02


Ветеран

Группа: Модератор
Сообщений: 2412



SQL
select c.*
from dbo.Cycle c
inner join dbo.Action a on c.ActionID = a.ActionID
order by a.ActionTypeID

проверил у себя вот на таком запросе.
в таблице dbo.Cycle — 574958 записей. в таблице dbo.Action — 7 записей, где присутствует 5 типов ActionTypeID
запускалось на работающем сервере (статистику не посмотрел, но десятки транзакций/сотни инструкций в секунду реально делает, только вчера ХП отлаживал, профайлер ни на секунду не останавливается в выводе).
время выполнения 8-10 сек

если написать top 10000, то время выполнения 0 сек., top 50000 — 1 сек., top 100000 — 2 сек., top 150000 — 2-3 сек...

так что если не отдавать пользователю больше нескольких сотен записей (а нафига ему больше?), то можно сказать, что всё работает мгновенно.
Tertium
Отправлено: 15.07.2005, 11:58


Машинист паровоза

Группа: Почетный участник
Сообщений: 192



2Gedeon: Вы жеговорили, что повторили мой код полностью. Я в предыдущем посте: 8 секунд это из-за шестикратного LTRIM(RTRIM(STR(datepart(…)))). Наверно, вы такую дурацкую строчку в запрос не вставляли (кстати, как всё-таки ещё можно отделить дату от времени?).
Могу сделать предположение, почему разница была аж в 7-8 раз: может быть, работая с одной таблицей, сервер кэшировал строковые функции (что бы это ни значило:)), а когда с двумя работал — нет. Хотя и на миллионе 8 к 45.

Убрав LTRIM(RTRIM(STR(datepart(…)))), я получил с 10000 промежутки времени меньше секунды.
Query Analizer показывает время в секундах, поэтому я повысил точность, сделав 1 млн записей. Результаты уже не замутнённые строковыми функциями:
case с сортировкой — 11сек
case без сортировки — 8 сек
inner join/left outer join без сортировки — приблизительно разброс 43-50 секунд
inner join/left outer join с сортировкой — 3мин 30сек.
разбег времени с join (с сортировкой и без) ставит меня в тупик.

2olegentry: мой запрос несско тяжелее select* изза перегруженности строковыми функциями. НO: он не выполняется полностью в реальной среде, как я уже неоднократно писал. Поэтому не нужно top. Здесь мы тестируем запросы на скорость уже чисто академически smile.gif
PS: что есть всё-таки ХП в этом контексте?

Так, ещё раз выкладываю весь код, пробуйте у себя AS IS.
создание:
SQL
Create database gambling
go
use gambling
go
CREATE TABLE [dbo].[Events2] (
[id] [int] IDENTITY (1, 1) NOT NULL ,
[event_id] [int] NOT NULL DEFAULT (0),
[place_no] [tinyint] NOT NULL DEFAULT (0),
[club_id] [int] NOT NULL DEFAULT (0),
[last_query] [datetime] NOT NULL DEFAULT (getdate())
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[event_names] (
[event_id] [int] NOT NULL ,
[event_name] [char] (255) COLLATE Cyrillic_General_CI_AS NOT NULL
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[Events2] WITH NOCHECK ADD
PRIMARY KEY CLUSTERED
(
[id]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[event_names] WITH NOCHECK ADD
CONSTRAINT [PK_event_names] PRIMARY KEY CLUSTERED
(
[event_id]
) ON [PRIMARY]
GO

CREATE INDEX [IX_Events2] ON [dbo].[Events2]([last_query] DESC , [club_id]) ON [PRIMARY]
GO

CREATE INDEX [IX_Events2_2] ON [dbo].[Events2]([event_id]) ON [PRIMARY]
GO

заполнение
SQL
use gambling
delete from Events2 go
DECLARE @COUNTER AS INT
DECLARE @club_id AS INT
DECLARE @place_no AS INT
DECLARE @event_id AS INT
SET @COUNTER = 0
WHILE (@COUNTER<1000000)
BEGIN set @club_id=rand()*1000
set @place_no=rand()*127
set @event_id=rand()*5
INSERT INTO Events2 (EVENT_ID, club_id, place_no, last_query)
VALUES (@event_id, @club_id, @place_no, GETDATE() )
SET @COUNTER = @COUNTER + 1
END

insert into event_names (event_id,event_name) values(1,'открытие двери')
insert into event_names (event_id,event_name) values(2,'закрытие двери' )
insert into event_names (event_id,event_name) values(3,'очистка бухгалтерии')
insert into event_names (event_id,event_name) values(4,'включение')
insert into event_names (event_id,event_name) values(5,'выключение')
insert into event_names (event_id,event_name) values(6,'смена платы')
insert into event_names (event_id,event_name) values(0,'неизвестное')

запрос с case:
SQL
use gambling
select /*(LTRIM(RTRIM(STR(datepart(yy,last_query))))+'-'+
LTRIM(RTRIM(STR(datepart(mm,last_query))))+'-'+
LTRIM(RTRIM(STR(datepart(dd,last_query)))) ) as 'Дата',
(LTRIM(RTRIM(STR(datepart(hh,last_query))))+':'+
LTRIM(RTRIM(STR(datepart(n,last_query))))+':'+
LTRIM(RTRIM(STR(datepart(ss,last_query)))) ) as 'Время',*/
place_no as 'Номер места',
club_id as 'ID зала',
(case event_id
when 1 then 'открытия двери'
when 2 then 'закрытие двери'
when 3 then 'очистка бухгалтерии'
when 4 then 'включение'
when 5 then 'выключение'
when 6 then 'смена платы'
ELSE 'неизвестное событие'
end) as 'Событие'
from Events2 --order by last_query desc, club_id

запрос с inner join:
SQL
use gambling
select /*(LTRIM(RTRIM(STR(datepart(yy,last_query))))+'-'+
LTRIM(RTRIM(STR(datepart(mm,last_query))))+'-'+
LTRIM(RTRIM(STR(datepart(dd,last_query)))) ) as 'Дата',
(LTRIM(RTRIM(STR(datepart(hh,last_query))))+':'+
LTRIM(RTRIM(STR(datepart(n,last_query))))+':'+
LTRIM(RTRIM(STR(datepart(ss,last_query)))) ) as 'Время',*/
place_no as 'Номер места',
club_id as 'ID зала',
event_name as 'Событие'
from Events2 inner JOIN event_names ON Events2.event_id = event_names.event_id
--order by last_query desc, club_id


можете откомметарить блоки преобразования даты и глянуть, что на целероне будет smile.gif

Отредактировано Tertium — 15/07/2005, 12:52
olegenty
Отправлено: 15.07.2005, 13:27


Ветеран

Группа: Модератор
Сообщений: 2412



теперь о применении функций...
так вот, функции ДОЛЖНЫ привенять В ПОСЛЕДНЮЮ очередь, когда выборка УЖЕ сделана и минимальна. я тоже наступал на такие грабли: применял функцию на "ядренном" view, который возвращает туеву хучу записей. увеличение производительности настало при выносе применения функций в ХП, когда выборка уже конкретизирована условиями. оч. помогло.

ХП — хранимая процедура.
Gedeon
Отправлено: 15.07.2005, 13:27


Ветеран

Группа: Модератор
Сообщений: 1742



Хочу Вас расстроить, я проверял именно с такими преобразованиями т.е. использовал ctrl+c, ctrl+v ничего не меняя, указанное время для этого случая, преобразования ничего не стоят.
стр.: (2) < [1] 2 >
Вернуться в Работа с базами данных в C++Builder