Pull to refresh
27
0

User

Send message
впрочем, этот способ мало отличается от приведенного вами max+rand. Если вы в ваш max+rand поставите вместо max, count, по идее, получите похожий результат
Надо просто добиться того, чтобы count считался один лишь раз
сорри не спец в mySql, думаю это будет выглядить как-то так
select * from table
where id in (
   select trunc(rand()*cnt.cnt+1)
   from (select count(*) as cnt from table) as cnt
   cross join (select null from table limit :demanded_ranom_records_count) as pivot
)
Домашка:
1)
select A,B,C,D from tableName where A=1 and B=2 or C=3 and D like 'test%';  


create index indexAB on table tableName (A,B,C,D);
create index indexCD on table tableName (C,D,A,B);
select A,B,C,D from tableName where A=1 and B=2 
union all
select A,B,C,D from tableName where C=3 and D like 'test%'  and (not (A=1 and B=2) or A is null or B is null)
)

Но тут, право же, оба индекса сделаны покрываеющими только лишь от того, что тема про покрывающие индексы. Нужны ли тут покрывающие — всамделе большой вопрос. Мне так думается, в большинстве случаев достаточно было бы индексов по А,B и C,D
2)
select A,C,D from tableName where B=3 or C=3 and D ='test' order by B;

тут собсна то же саоме
create index (B,A,C,D);
create index (C,D,B,A);
select A,C,D  from tableName where (B<3 /*or B is null*/)and C=3 and D ='test' order by B
union all
select A,C,D  from tableName where B=3
union all
select A,C,D  from tableName where (B>3 /*or B is null*/)and C=3 and D ='test' order by B

В какой — верхний или нижний запрос поставить предикат b is null — не зна, зависит от того, где нулы в индексе mysql хранит — в верху или внизу

c покрытием то же самое, мне думается можно было бы обойтись индексами по B и C,D,B
Мне показался несколько странным и удивительным приведенный Вами порядок
>> Во-первых, смотрим на клоз order by
>>Далее клоз where.

Сразу оговорюсь я не знаком с МySQL (но стремлюсь быть в курсе), я эскалирую свои знания из других систем.
Если бы речь шла об оракле, я бы настаивал на следующем порядке:

В первую очередь смотрим на where. Причем только на строгие равентсва (=,in).
Во вторую очередь смотрим на ордер бай и отбор по диапазонам. Здесь все очень не однозначно, зависит от состава данных. Для поиска по диапазону мне удавалось использовать только один предикат, по этой причине тут надо выбрать наиболее селективный и оценить, что нам будет дешевле — отсортировать набор, полученный с отбором по всем предикатам, или же сканирование индекса по предикатам выбранным в первую очередь, но без сортировки.
В третью очередь, соответственно, ставим то, что не во поставили во вторую
В четвертую очередь, в самый хвост ставим селект лист и отбор по неравенству.

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

В MySQL как-то все совсем иначе?
С формальной точки зрения сеьмя — пассив. Она не приносит профита, лишь затраты и обязательства.

Когда у Пети появятся седины и даст о себе знать кризис среднего возраста, он, определенно, поймет, что хоть богатство у него есть, счастливым он от этого не стал.
Не однократно сталкивался с ситуацией, когда за плевую доработку в одну строчку мне начислляли 100% KPI, а тяжелая, рутинная работа по предотвращению пока еще не проявивших себя проблем, вобще не попадала в оценку эффективности. Реально не однократно возникали мысли, действительно все запустить на столько, чтобы проблемы стали очевидны, и лишь тогда их героически решать и получать за то заслуженное вознаграждение.

Надо помнить еще и закон Паретто — 20%усилий делают 80% результата. Система мотивации, построенная на оценке результирующей эффективности демотивирует делать 80% работы, дающих лишь 20% результата.
Да, сосредоточившись на вложенностях, упустил что отсутствие вложений вовсе тоже не нарушает логики
Получилось чуть компактнее. Не знаю, на сколько правильно, может какие -то кейсы не учел и не верно отработю

Вкратце суть решения.
1) разматываю строку
2) для каждой строки считаю глубину вложенности скобки по критерию открыавющая/закрывающая
3) делаю селфджойн по глубине, к закрыающим скобкам подтягиваю их открывающие
4) сверяю кооректность открыающей/закрывающей пары
5) суммирую вложенные ошибки для диапазонов
6) дальше чисто оформительское — отфильтровываю диапазоны содержащие в себе ошибки, ранжирую по длине диапазона, вывожу фаворита в ранге, подтягиваю исходную строку, чтобы выкусить от туда фрагмент.
запрос
with brackets as(select '[[]])[([[]][[(]])]' b 
                 from dual
 )
 ,pivot as (select ord
                   ,br
                   ,decode(br,')',-1,']',-1,'(',1,'[',1) direction
                   ,sum(decode(br,')',-1,']',-1,'(',1,'[',1)) over (order by ord) depth
              from (
                  select level ord,substr(b,level,1) br
                  from brackets connect by level <= length(b)
              )
 )
select from_pos
       ,to_pos
       ,substr(b,from_pos,to_pos-from_pos+1) data
from (
    select s.*
           ,dense_rank() over (order by from_pos-to_pos) d_rank
    from (
      select s.*
           ,sum(error) over (order by to_pos range between to_pos-from_pos preceding and current row) errors
      from (
      select p1.ord to_pos
             ,max(p2.ord) from_pos
             ,decode(translate(max(p1.br),'])','[('),max(p2.br)  keep (dense_rank last order by p2.ord),0,1) error
      from pivot p1
      inner join pivot p2 on p1.depth = p2.depth -1 and p2.ord < p1.ord and p2.direction = 1 and p1.direction = -1
      group by p1.ord
      )s
    )s 
    where errors = 0
)s,brackets
where s.d_rank = 1;


результат
  FROM_POS     TO_POS DATA
---------- ---------- ------------------
         1          4 [[]]
         8         11 [[]]

Интересный пост, прикольная задача..
Решения пока не смотрел. Жду когда(если) появится свободное время, зудит попробовать решить самому, а потом сравнить.
Спасибо
Но дольше — нет. А может намного меньше. Или я не прав?
Я бы согласился с тем, что тут вы абсолютно правы, если бы зафорсив range scan в тесткейсе (третий случай), я не получил бы втрое больше чтений :D
Для меня чесгря это было неожиданностью, которую я пока не могу объяснить.
Сейчас мусор подчищается при первом же SELECTе к данным почищенной таблицы

Наверное в этом и есть секрет FB, о котором я вопрошал ))). О том, чтобы подобным образом поступал бы и оракл, я не в курсе. Спасибо большое за информацию, положу себе в копилочку.

Погодите, в этом примере вы оставили where rownum = 1. Мне интересен был вариант именно без него.

с учетом того, что в таблице оставлена одна лишь запись, отсутствие отсечки по rownum не должно ничего менять (в плане согласованных чтений).
Скрытый текст
SQL> select id from test;

        ID
----------
   1000000


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=586 Card=1 Bytes=1
          3)

   1    0   INDEX (FAST FULL SCAN) OF 'SYS_C0051758' (INDEX (UNIQUE))
          (Cost=586 Card=1 Bytes=13)





Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       2102  consistent gets
          0  physical reads
          0  redo size
        344  bytes sent via SQL*Net to client
        431  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed



Ну, и для полноты картины просто приведу статистику, какой она должна быть при условии, что читается действительно одна только запись
Скрытый текст
SQL> select * from (
  2    select /*+first_rows*/ id from test order by id desc
  3  )
  4  where rownum = 1;

        ID
----------
   1000000


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: FIRST_ROWS (Cost=2146 Card=
          1 Bytes=13)

   1    0   COUNT (STOPKEY)
   2    1     VIEW (Cost=2146 Card=1 Bytes=13)
   3    2       INDEX (FULL SCAN DESCENDING) OF 'SYS_C0051758' (INDEX
          (UNIQUE)) (Cost=2146 Card=1 Bytes=13)





Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          3  consistent gets
          0  physical reads
          0  redo size
        344  bytes sent via SQL*Net to client
        431  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

Не могу удержаться от поправки, простите уж. Это не из вредности, просто в руках зудит поумничать

Можно даже не делать ограничение выборки (LIMIT, TOP, FIRST etc.) Просто становимся на первую полученную запись. Любая нормальная СУБД не будет фетчить всё подряд пока не попросишь.

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

Припоминаю тому хороший пример. До 10й версии оракла, он в PL/SQL не буферезировал сам результат курсора, полученный в цикле for, начиная с 10й версии, цикл for стал фетчить пачками по 100 записей. Тем, кто опирался на отсутствие буферизации в цикле ранее, с выходом новой версии, пришлось переписывать свои приложения.

ну и коли уж начал отвечать, отвечу и на другую реплику:

Чувствуете разницу? Надеюсь понятно объяснил.

Да, я разницу чувствую. И Вы объяснили все более чем понятно. Но Вы говорите о частном случае, когда эта одна единственная запись может быть найдена быстрее нежели выполнятся полный скан таблицы/индекса. Я же говорю о другом частном случае, другой крайности, когда поиск одной лишь записи требует сканирования того же объема информации, что и подсчет количества. Тут все зависит от того, как данные физически лягут. И тут уж выбор программиста, что ему более по душе — стабильное но c низкой производительностью решение или же не стабильно высокопрозводительное. Кто-то предпочтет стабильное, поставит его на мониторинг, и когда таблица разрастется до критических размеров, сделает ей шринк (или ребилд индекса) в штатном режиме, в рабочее время. Кто-то другой, может не побояться быть разубженным в три утра, чтобы сделать этот же самый шринк/ребилд, ибо вчера все что надо отработало за 10 минут, а сегодня данные физически легли чуть иначе, и процесс не смог выполниться за три часа. :D
В этом тесткейсе не было ПК.
Поведение с отбором по деградировавшему ПК я показал под спойлером в коментарии, который оказался выше. Нагенерить столько данных чтобы в этом случае полное сканирование индекса превысило бы погрешность измерения оказалось проблематичным, я не влез в отведенные мне квоты. Потому вместо более очевидных для всех таймингов пришлось показать статистику, которая может показаться менее очевидной для человека из смежной области.
Суть того примера сводится к тому же, что и этого. Листья индекса для удаленных данных так же физически не освобождаются, как и данные таблицы, а потому для чтения одной выжившей записи из былого миллиона требуется почти то же сканирование(в этом случае уже индекса), что и для подсчета общего количества.

Просто для того, чтобы не возникало сомнений, что для оракла select id для того тесткейса равнозначно select 'hello', приведу план и статистику и для него:
Скрытый текст
SQL>  select id from test where rownum = 1;

        ID
----------
   1000000


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=586 Card=1 Bytes=1
          3)

   1    0   COUNT (STOPKEY)
   2    1     INDEX (FAST FULL SCAN) OF 'SYS_C0051757' (INDEX (UNIQUE)
          ) (Cost=586 Card=1 Bytes=13)





Statistics
----------------------------------------------------------
          7  recursive calls
          0  db block gets
       2261  consistent gets
          0  physical reads
          0  redo size
        344  bytes sent via SQL*Net to client
        431  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed



Здесь у нас оказалось ненамного больше согласованных чтений (consistent gets). Наверное потому что действительно вычитывалось значение.
О том, собственно, и идет речь.

По моим разумениям Лимит против count (не для MyISAM) даст:
— Огромный прирост производительности для случая, когда таблица заполнена данными и данные не разрежены
— Не прогнозируемый, но все же скорее прирост для случая, когда таблица заполнена данными и данные разрежены.
— Никакого прироста для случая, когда таблица данными не заполнена.

Я предполагаю что для системы которая принимает решения на основе факта наличия/отсутствия записей в таблице, случай, когда таблица не содержит записей или же данные в таблице разрежены — характерен.
А в чем тут секрет FB?
Да, я понял фичу о которой Вы говорите. FB как и оракл, судя по всему, отдает фетч еще до того, как закончил скан/джойн.
Но тогда замена count(*) limit'ом(или как там в FB?) должна бы давать преимущество лишь в случае, когда результирующий набор не пуст, если он достаточно статичен, удаления производятся весьма редко, а если производится, место удаленных записей вскорости занимают новые. Если же результирующий набор пуст, все равно придется сканировать ВСЕ, чтобы убедиться в этом. Если данные разрежены, сканировать придется все равно достаточно много. А, я так полагаю, если есть проверка на наличие строк в таблице, ситуация, когда таблица не содержит данных — скорее закономерна нежели исключительна, и из таблицы записи определенно удаляются.(если эта проверка, конечно, не является проверкой на «всякий пожарный», с целью поднять ошибку, которая в промышленной эксплуатации не должна подниматься никогда)

за спойлером я провел тесткейс с первичным ключом. на оракле. Добавил миллион строк, удалил все, кроме одной, с наибольшим ID. Попробовал count, для которого использовался index full scan, where rownum для которого использовался он же и where rownum, для которого использовался range scan. По количеству согласованных чтений у count и where rownum при index full scat — сопостовими. Range scan втрое менее эффективен (при условии, что мы заведомо не знаем минимальный id записи в таблице)
Скрытый текст
SQL> set autotrace on
SQL> select count(*) from test;

  COUNT(*)
----------
         1


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=586 Card=1)
   1    0   SORT (AGGREGATE)
   2    1     INDEX (FAST FULL SCAN) OF 'SYS_C0051757' (INDEX (UNIQUE)
          ) (Cost=586 Card=1)





Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       2102  consistent gets
          0  physical reads
          0  redo size
        350  bytes sent via SQL*Net to client
        431  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL> select 'hello' from test where rownum = 1;

'HELL
-----
hello


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=586 Card=1)
   1    0   COUNT (STOPKEY)
   2    1     INDEX (FAST FULL SCAN) OF 'SYS_C0051757' (INDEX (UNIQUE)
          ) (Cost=586 Card=1)





Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       2101  consistent gets
          0  physical reads
          0  redo size
        352  bytes sent via SQL*Net to client
        431  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL> select 'hello' from test where id> 0 and rownum = 1;

'HELL
-----
hello


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=2 Card=1 Bytes=13)
   1    0   COUNT (STOPKEY)
   2    1     INDEX (RANGE SCAN) OF 'SYS_C0051757' (INDEX (UNIQUE)) (C
          ost=2 Card=1 Bytes=13)





Statistics
----------------------------------------------------------
         58  recursive calls
          0  db block gets
       6456  consistent gets
          0  physical reads
          0  redo size
        352  bytes sent via SQL*Net to client
        431  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          5  sorts (memory)
          0  sorts (disk)
          1  rows processed



PS. Не подумайте, я Вас нисколь не троллю. Мне просто эта тема очень интересна, для меня она сейчас актуальна. Лишь этим объясняется моя дотошность в этом вопросе
Маленький тесткейс для оракла.
Запрос к пустой таблице с count(*) vs where rownum = 1
Connected to Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 
Connected as ZTXN@ORCL01
 
SQL> set timing on
SQL> select count(*) from test;
 
  COUNT(*)
----------
         0
 
Executed in 14.437 seconds
SQL> select 'hello' from test where rownum = 1;
 
'HELLO'
-------
 
Executed in 15.812 seconds


Секрет, в общем, прост —
Скрытый текст
create table test as select  
                       lpad('1',100,'1') val1
                       ,lpad('2',100,'2') val2
                       ,lpad('3',100,'3') val3
                       ,lpad('4',100,'4') val4
                       ,lpad('5',100,'5') val5
                       ,lpad('6',100,'6') val6
                       ,lpad('7',100,'7') val7
                       ,lpad('8',100,'8') val8
                     from dual connect by level < 1e6;
delete from test;
commit;                     

Вероятно проект был заточен под MyISAM, который хранит информацию о количестве строк в таблице.

Не знаю как в этом самом MyISAM происходит удаление строк. Если так же как везде, размечается на удаление без высвобождения ранее занятых строками ресурсов, то поиск первой попавшейся записи, вполне может привести к весьма длительному сканированию датафайла, если перед этим было вставлено в таблицу много данных, а потом оставлено мало. Здесь надо будет как-то принуждать использование индекса, что на разных платформах решается разными способами.

Да и сам поиск произвольной строки, в разных СУБД производится по разному. Для MySQL, PG, DB2 используется limit, для M$ — top для оракла — rownum… Для кажогй СУБД всяко придется лепить что-то свое.

В самделе если задуматься не так уж и абсурдно описанное вами решение, может быть даже вполне обосновано. Да, пеереехать даже на InnoDB уже не получится, но, быть может там специфика такая, что InnoDB стопудов не нужен будет никогда.
Пока не открыл спойлер «Только самое нужное», меня не покидало ощущение, что вы учите не столько программированию сколько способам выдать себя за программиста. Чем-то навевало методологию школ пикапа. Целеполагание, работа над самооценкой, обильные сакцесстори, призванные снять барьер «врать плохо», ненавязчиво переведя вранье в ранг нормы, а само, собсна программирование, за спойлером. :D
>>Кстати, касательно возможностей PostgreSQL, кому приходилось использовать на практике:
>>— наследование таблиц

Открыл для себя наследование в постгре благодаря вопросу в QA.

Использование наслдеования, имеет вполне документированные подводные камни, и, фактически, заставляет отказаться от таких чтук, как PK/FK, что очень сужает область применимости до ничтожной.
>>Иногда лучше что-то не сделать и знать об этом, чем сделать два раза ничего не подозревая (например операция со счетом).

Если вы выполняете саму задачу и установку статуса задачи в разных транзакциях, вы всегда имеете риск, что задача отработала(посчитала), но статус не обновился.

Information

Rating
Does not participate
Location
Москва и Московская обл., Россия
Registered
Activity