![]() |
#41 |
Участник
|
Да, индексы перестроил, статистику обновил.
Теперь такой код: [codebox] TT := TIME; GLEntry.RESET; GLEntry.SETCURRENTKEY("Transaction No."); GLEntry.SETRANGE("G/L Account No.",'000.'); IF GLEntry.FIND('-') THEN; MESSAGE('ok %1',TIME - TT);[/codebox] Результат: SELECT * FROM "Test$G_L Entry" WITH (READUNCOMMITTED) WHERE (("G_L Account No_"=?)) ORDER BY "Transaction No_","Entry No_" OPTION (FAST 5) SQL Plan = Filter[3,1];Bookmark Lookup[4,3];Index Scan($4)[6,4] Для запроса был выбран ключ, указанный в SETCURRENTKEY. Пока для меня ясно, что единственный способ заставить навижен использовать нужный ключ - это использовать SETCURRENTKEY. |
|
![]() |
#42 |
Участник
|
Не "заставить", а "попросить".
SETCURRENTKEY лишь добавляет ORDER BY в сиквельный запрос. Индекс выбирает SQL. И это лишь совпадение что он выбирает то что передано в ORDER BY. Попробуйте наложить много фильтров, сделав SETCURRENTKEY на ключ с большим количеством полей (именно те, по которым накладывался фильтр). Или например так: GLEntry.RESET; GLEntry.SETCURRENTKEY("Transaction No."); GLEntry.SETRANGE("G/L Account No.",'000.'); GLEntry.SETFILTER("Entry No.",'<10'); Кстати сколько записей у вас в GLEntry? P.S. Признаться честно, не понимаю почему SQL 2000 так нелепо выбирает индексы для запроса. Все ваши примеры на 2005 работают по другому. Завтра поставлю 2000 и потестирую. |
|
![]() |
#43 |
Участник
|
У меня на Nav 3.6 + SQL2000
Код GLEntry.RESET; GLEntry.SETCURRENTKEY("Transaction No."); GLEntry.SETRANGE("G/L Account No.",'60007'); GLEntry.SETRANGE("Posting Date",010107D,310307D); IF GLEntry.FIND('-') THEN ; отрабатывал в разы быстрее, чем GLEntry.RESET; GLEntry.SETRANGE("G/L Account No.",'60007'); GLEntry.SETRANGE("Posting Date",010107D,310307D); IF GLEntry.FIND('-') THEN ; Хотя выбор индекса действительно осуществлялся независимо от GLEntry.SETCURRENTKEY("Transaction No."). Точнее если закомментирована строка GLEntry.SETRANGE("Posting Date",010107D,310307D);, то индекс по "Transaction No.", а если нет, то индекс, который включал оба фильтруемых поля. Во втором случае всегда использовался кластерный индекс. весьма небыстро отрабатывал. P.S. 17 таблица более 3 млн записей |
|
![]() |
#44 |
Участник
|
Я полагаю использование Clustered Index Scan обусловлено хинтом FAST 5, который призывает оптимизатор получить первые 5 записей как можно быстрее. (В ущерб скорости получения остальных записей)
В 2005 мы избавлялись от этих FAST с помощью plan guide. (Кстати в пятерке запросы уже идут без FAST) Попробуйте сравнить в Query Analizer запросы которые идут по Clustered Index Scan с аналогичным запросом без хинта FAST |
|
![]() |
#45 |
Участник
|
Да, результаты говорят сами за себя
1. c кластерным индексом и FAST ----------> <более минуты 2. с кластерным индексом без FAST -------------> Меньше секунды 3. с использованием SETCURRENTKEY -------------> Меньше секунды Думаю если сравнить в миллисекундах, то думаю второй вариант быстрее, но и третий обеспечивает хорошую производительность |
|
![]() |
#46 |
Участник
|
А мне кажется логичным, что сервер выбирает тот ключ, по которому идет сортировка. Понитно же, что в упорядоченном наборе данных поиск идет на порядок быстрее. Поэтому мы через SETCURRENTKEY устанавливая сортировку по полям поиска в то же время выбираем и наиболее оптимальный ключ. И то, что выбор сервера совподает с нашим не странно, а закономерно. В тоже время без сортировки (без вызова SETCURRENTKEY) выбор ключа не так очевиден, имхо. Поэтому сервер и выбирает не пойми какой ключ.
Кстати, эффект от вызова SETCURRENTKEY увеличился на порядок после создания ключа на сервере, т.е. возведения MaintainSQLIndex в истину. До этого вызов SETCURRENTKEY дало приращение в скорости 25% (с 4 сек до 3 первый вызов FIND('-')). После - время поиска упало практически до нуля. p.s. Все же невозможность явно формировать запросы к базе напрягает. То что сервер ставит фиг пойми какие хинты (типа FAST) не радует. p.p.s. 3.70A, SQL Server 2000. |
|
![]() |
#47 |
Участник
|
Считаю, что golyshev все правильно написал, за что ему респект
![]() SETCURRENTKEY дейсвительно, только добавляет Order By в запрос. Sql сервер на основании условия where и order by решает, какой выбрать ключ. При нормальных ключах и статистике ключ выбирается правильно, даже если не указан setcurrentkey, т.е. только с условием where. Если статистика давно не обновлялась, ключ для запроса с условием where выбирается неправильно. В этом случае использование SETCURRENTKEY добавляет order by, что приводит к выбору правильного ключа. |
|
![]() |
#48 |
Участник
|
Цитата:
Можно привести массу примеров, где это утверждение не верно. Более того, порой просто поражаешься сообразительности SQL в аспекте построения плана запросов. (кстати по мимо поисков по ключу есть и другие компоненты планов - Table Spool'ы, Bookmark Lookup'ы и прочие) Цитата:
Цитата:
Создайте ключи из всех возможных комбинаций полей во всех возможных порядках и все FIND по таблице будут моментальными. Зато изменения в этой таблице станут ойойой какими медленными. ps Главное не превращать это в алхимию. Лучше вместо замеров секундомером, использовать хотя бы монитор клиента, а еще лучше профайлер. Тогда будет ясно почему именно произошло ускорение и поможет ли данный метод в других лучаях. |
|
![]() |
#49 |
Участник
|
Цитата:
При нормальных ключах и статистике ключ выбирается правильно, даже если не указан setcurrentkey, т.е. только с условием where.
Цитата:
Сообщение от ;362029
Можно привести массу примеров, где это утверждение не верно.
|
|
![]() |
#50 |
Участник
|
Цитата:
Цитата:
Сообщение от smoyk
![]() Естественно, но так как мы сортируем именно по полям, по которым проводим поиск, то никакие другие примеры здесь не уместны Вызывая SETCURRENTKEY мы не только сортируем (что значительно ускоряет поиск), но и явно указываем выбираемый ключ. Если SQL 2005 выберет другой ключ исходя из своей логики, думаю это будет не правильно. Думаю, он этого и не сделает имхо. Или я не прав и Вы можете такие примеры привести?
|
|
![]() |
#51 |
Участник
|
Цитата:
Во первых набор данных будет упорядочен по первичному ключу. В вторых, поиск будет идти быстрее, поскольку при использовании не кластерного индекса, к нему всегда идет join кластерного, в котором данные как раз УЖЕ упорядочены по первичному ключу. Цитата:
Это дополнительное действие, которое либо является отдельным пунктом плана запроса, либо входит в этап джоина таблиц. Пример будет вечером. Специально сделаю его на CRONUS, чтобы Вам удалось его повторить. |
|
![]() |
#52 |
Участник
|
Цитата:
Сообщение от MSI
![]() И для каждого из таких запросов, как я понял, надо приципить свой план. Геморройно... Или надо что-то не так делать? Может мы не так лечим и надо действительно сделать обновление статистики и сиквель должен начать правильно выстраивать планы выполнения? Или можно как-то назначить план на группу запросов?
К сожалению, обновление статистики сможет починить лишь запросы, которые идут из кода. (Тем не менее обновлять статистику нужно обязательно. По особо большим и популярным таблицам - каждую ночь) При открытии же форм, как раз идут запросы с > >= < и <=. Например при фильтрации финансовых операций по счету будет запрос: Код: exec sp_executesql N'SELECT * FROM "dbo"."КРОК$G_L_Entry" WHERE (("G_L_Account_No_"=@P1)) AND "Entry_No_">=@P2 ORDER BY "Entry_No_" OPTION (FAST 10)', N'@P1 varchar(20),@P2 int','61100100',4 Очевидно, что если в качестве @P2 первый раз было передано большое число, то план будет построен по кластерному индексу. (Если @P2 маленькое, то план будет правильный - состоять из джоина Index Seek по G_L_Account и Clustered Index Seek) И в дальнейшем, этот план по кластерному индексу будет использован и для маленьких @P2, что будет приводить к сканированию всей таблицы. Эти случаи надо фиксить Plan Guide'ами |
|
![]() |
#53 |
Участник
|
CRONUS Россия ЗАО (navision sp3) на ms sql 2005
Типичный пример на 17 таблице. Прежде всего, заполняем фин книгу (300 записей - это не серьезно) Код: Name DataType Subtype Length i Integer dlg Dialog recGenJnlTEMP Record Gen. Journal Line recGenJnlDimTEMP Record Journal Line Dimension Gen. Jnl.-Post Line Codeunit Gen. Jnl.-Post Line --------- dlg.OPEN('@1##################'); FOR i := 1 TO 1000000 DO BEGIN dlg.UPDATE(1,i DIV 100); recGenJnlTEMP.DELETEALL; recGenJnlTEMP.INIT; recGenJnlTEMP.VALIDATE("Posting Date" , 010105D); recGenJnlTEMP.VALIDATE("Account Type" , recGenJnlTEMP."Account Type"::Customer); recGenJnlTEMP.VALIDATE("Account No." , '49633663'); recGenJnlTEMP.VALIDATE("Document No." , 'INITIAL'); recGenJnlTEMP.VALIDATE(Description , 'Автозапонение'); recGenJnlTEMP.VALIDATE(Amount,100); recGenJnlTEMP.VALIDATE("Bal. Account Type",recGenJnlTEMP."Bal. Account Type"::"G/L Account"); recGenJnlTEMP.VALIDATE("Bal. Account No.",'68-800'); recGenJnlTEMP.VALIDATE("Shortcut Dimension 1 Code",'ПРОДАЖИ'); recGenJnlTEMP.VALIDATE("Shortcut Dimension 2 Code",'МЕРСЕДЕС'); recGenJnlTEMP.INSERT; recGenJnlDimTEMP.DELETEALL; recGenJnlDimTEMP.INIT; recGenJnlDimTEMP."Table ID" := 81; recGenJnlDimTEMP."Dimension Code" := 'ПРОДМЕНЕД'; recGenJnlDimTEMP."Dimension Value Code" := 'ВК'; recGenJnlDimTEMP.INSERT; "Gen. Jnl.-Post Line".RunWithCheck(recGenJnlTEMP,recGenJnlDimTEMP); COMMIT; END; Код: recGLEntry.RESET; recGLEntry.SETCURRENTKEY("Source Type","Source No."); recGLEntry.SETRANGE("Source Type",recGLEntry."Source Type"::Customer); recGLEntry.SETRANGE("Source No.",'49633663'); recGLEntry.SETRANGE("Document No.",'104005'); recGLEntry.FIND('-'); Код: exec sp_executesql N'SELECT * FROM "CRONUS"."dbo"."CRONUS Россия ЗАО$G_L Entry" WITH (READUNCOMMITTED) WHERE (("Source Type"=@P1)) AND (("Source No_"=@P2)) AND (("Document No_"=@P3)) ORDER BY "Source Type","Source No_","G_L Account No_","Global Dimension 1 Code","Global Dimension 2 Code","Business Unit Code","Posting Date","Entry No_" OPTION (FAST 5)', N'@P1 int,@P2 varchar(20),@P3 varchar(20)' ,1,'49633663','104005' ![]() |
|
![]() |
#54 |
Участник
|
Наши мелкомягкие друзья, прочухав косяк с планами запроса, выродили UPDATE 6 для 4-го клиента. Смысл UPDATE в следующем - создается ручками на сиквеле табличка ndo$dbconfig в которую можно прописать правила работы Нава с сиквелем такие как:
IndexHint=Yes;Company="DEFAULT COMPANY";Table="Vendor";Key="Search Name";Search Method="-+$";Index=1'; - использовать 1 индекс сиквеля для поиска по ключу Search Name; UseRecompileForTable="G/L Entry";Company="CRONUS International Ltd.";RecompileMode=1; - делать рекомпайл для каждого запроса по таблице. т.е. проблема прокэшированного плана запроса вроде-как снимается. Однако, решив пойти по по пути с REcompile, столкнулся с проблемой производительности в весьма тонкой вещи - фильтрация по flow field. Может кто сталкивался и решал подобные проблемы с REcompile? |
|
![]() |
#55 |
Участник
|
Советую перед установкой Update 6
внимательно прочитать это: http://www.mibuso.com/forum/viewtopic.php?...02a2f750c740e98 |
|
![]() |
#56 |
Участник
|
Всем привет.
Не могу понять. Есть запрос [codebox]select top 100 * from dbo.[FIRM$G_L Entry] where [G_L Account No_]='20_03' and [Posting Date] between '2007-07-01' and '2007-07-31' --order by [G_L Account No_], [Posting Date][/codebox] на 2000 сервере план такой [attachment=723:SQL2000_1.jpg] а на 2005 сервере вот так выходит [attachment=724:SQL2005_1.jpg] на 2005 делал update statistics dbo.[ЭГЭБ-1$G_L Entry] WITH FULLSCAN DBCC UPDATEUSAGE (0, [ЭГЭБ-1$G_L Entry]) не помогло. причем, когда есть ORDER BY индекс выбирается правильный в обоих случаях. Как заставить 2005 SQL правильно выбирать индексы без ORDER BY? |
|
![]() |
#57 |
Участник
|
А подскажите пожалуйста как быть в такой ситуации.
Понять её можно на следующем примере. Имеется таблица с первичным ключём по int полю Ind. _OVTR - Record для этой таблицы Код: FOR _i:= 1 TO 10000 DO BEGIN IF NOT _OVTR.GET(64) THEN BEGIN _OVTR.INIT; _OVTR.Ind := 64; _OVTR.INSERT; END; _OVTR.GET(64); _OVTR.DELETE; END; 64 11 0 0 DB S GRANT 64 11 795253988 1 KEY (4000de78af45) X GRANT 64 11 795253988 1 PAG 3:39153 IX GRANT 64 11 795253988 1 KEY (ffffffffffff) RangeS-U GRANT 64 11 795253988 0 TAB IX GRANT Если создать в Navision новую сессию (66) и попытаться вставить в таблицу строку с Ind=65, то возникает блокировка. sp_lock 64,66 показывает 66 11 0 0 DB S GRANT 64 11 0 0 DB S GRANT 64 11 795253988 1 KEY (4000de78af45) X GRANT 64 11 795253988 1 PAG 3:39153 IX GRANT 66 11 795253988 1 PAG 3:39153 IX GRANT 64 11 795253988 1 KEY (ffffffffffff) RangeS-U GRANT 66 11 795253988 1 KEY (ffffffffffff) RangeIn- WAIT 64 11 795253988 0 TAB IX GRANT 66 11 795253988 0 TAB IX GRANT Т.е. возникла блокировка по диапазону индекса, как понимаю от 64 до бесконечности. Если модифицировать код так Код: FOR _i:= 1 TO 10000 DO BEGIN //IF NOT _OVTR.GET(64) THEN BEGIN _OVTR.INIT; _OVTR.Ind := 64; _OVTR.INSERT; END; _OVTR.GET(64); _OVTR.DELETE; END; 64 11 0 0 DB S GRANT 64 11 795253988 1 KEY (4000de78af45) X GRANT 64 11 795253988 1 PAG 3:39153 IX GRANT 64 11 795253988 0 TAB IX GRANT добавление записи с Ind=65 уже не вызывает блокирование т.к. отсутствует блокировка RangeS-U и судя по всему её вызывает GET. Подскажите пожалуйста, как это можно обойти? |
|
![]() |
#58 |
Участник
|
GET вызывает блокировку записи перед ней и после неё. Таким образом Вы не можете вставить значение 65 без блокировки. Если работать без GET, то блокировки записей в таблице нет и вы без проблем пытаетесь вставить записи (если не делать 2 раза подряд Insert).
|
|
![]() |
#59 |
Участник
|
Я это понимаю, но в рабочем коде запись может уже существовать и потому нужна проверка. Как быть, чтоб проверка существования была без блокировок?
Пока писал возникла мысль сделать без GET и прочих проверок так: Код: IF _OVTR.INSERT THEN; |
|
![]() |
#60 |
Administrator
|
IF _OVTR.INSERT THEN;
прекрасно работает! ![]() |
|