Как стать автором
Обновить
4
0

Пользователь

Отправить сообщение

Забавно конечно. Как минимум на Ростелекоме проще найти что резольвится, чем то что не резольвится. Даже смена днс (+ flushdns) на всякие 1.1.1.1 / 8.8.8.8 не помогает.

google.ru

dns.yandex.ru

И тем не менее, накопители SSD объёмом от 1 Тб всё ещё весьма недёшевы

HDD 1TB - 4-5к рублей, SSD 1TB - от 6.5к рублей (если не учитывать ADATA за 4.5к) на я.маркете. Неподъёмная разница в 2.5к рублей - действительно важный аргумент, особенно для нищих айтишников.

Вот и дожили до времён, когда даже notepad не стартует мгновенно.

Ждём теперь блокировку гитхаба?

Firefox, Vivaldi, Opera, Я.Браузер, Brave и сам Edge есть в Microsoft Store.

Скорее важно уточнить - это приватный ShadowSocks на своём VPS или просто какой-то публичный из интернета. Все публичные SS (и не SS) рано или поздно перебанят тупо по IP, вне зависимости от того 2022 или 2049.

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

А если структурировать?

Горячей воды не будет!

Период: с 10 по 15 июня

Причина: ежегодный профилактический ремонт теплосетей

По вопросам обращаться: ооо ук1, тел. 123-123-123

Кроме прикладного ПО есть ещё и бесчисленное количество драйверов, которые никогда не будут писать под старое железо. Поэтому, вероятно, планируется, что первые доли процентов "armdows" возможны в ноутбучном сегменте через предустановленные оем-версии, ибо ждать пока подтянутся производители нового железа и умрёт старое придётся долго. Однако даже с винбуком на арм, с потребностями, которые бы покрыл и хромобук, но когда нет гарантии что воткнутая железка не заведётся, ОС на арм будет не очень популярна.

BLOB — булевая переменная

Булевая - это true/false. Блобы - это массивы двоичных данных обычно.

А ну ещё забыл тоже достаточно частый вариант, с left join'ами (часто такое с представлениями (view) бывает). Тут у оракла есть возможность не только выкидывать сегмент таблицы, но даже выкидывать целые подзапросы из результирующего плана, если не нужны все поля.

DDL
create table CLIENTS as
select level as id, 'client '||level as name
  from dual
 connect by level <= 1000
/

ALTER TABLE CLIENTS
 ADD CONSTRAINT PK_CLIENTS
  PRIMARY KEY (ID)
/

create table PAYMENTS as
select c.ID * 10000 + p.PAY_ID as PAY_ID, c.ID as CLIENT_ID, 0 as PAY_SUM 
from CLIENTS c, (select level as pay_id from dual connect by level <= 100) p
/

ALTER TABLE PAYMENTS
 ADD CONSTRAINT PK_PAYMENTS
  PRIMARY KEY (PAY_ID)
/

CREATE INDEX IDX_PAYMENTS_CLIENT_ID ON PAYMENTS
(CLIENT_ID)
/

ALTER TABLE PAYMENTS
 ADD CONSTRAINT FK_PAYMENTS_CLIENT 
  FOREIGN KEY (CLIENT_ID) 
  REFERENCES CLIENTS (ID)
/

create or replace view V_PAYMENTS as
select P.*, C.NAME
  from PAYMENTS P
   left join CLIENTS C on P.CLIENT_ID = C.ID
/

BEGIN
  SYS.DBMS_STATS.GATHER_TABLE_STATS (
     OwnName           => USER
    ,TabName           => 'PAYMENTS'
    ,Cascade           => TRUE);
  SYS.DBMS_STATS.GATHER_TABLE_STATS (
     OwnName           => USER
    ,TabName           => 'CLIENTS'
    ,Cascade           => TRUE);
END;
/

Вот чисто синтетический пример с таблицами PAYMENTS и CLIENTS, завёрнутые во вьюшку так, чтобы CLIENTS соединялась через left join. Теперь посмотрим планы запросов:

Со *:
select * 
  from V_PAYMENTS
 where client_id = 1
-------------------------------------------------------------------------------------------
| Id  | Operation                    | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |            |   100 |  2700 |    50   (2)| 00:00:01 |
|*  1 |  HASH JOIN OUTER             |            |   100 |  2700 |    50   (2)| 00:00:01 |
|*  2 |   TABLE ACCESS FULL          | PAYMENTS   |   100 |  1200 |    49   (3)| 00:00:01 |
|   3 |   TABLE ACCESS BY INDEX ROWID| CLIENTS    |     1 |    15 |     1   (0)| 00:00:01 |
|*  4 |    INDEX UNIQUE SCAN         | PK_CLIENTS |     1 |       |     0   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - access("P"."CLIENT_ID"="C"."ID"(+))
   2 - filter("P"."CLIENT_ID"=1)
   4 - access("C"."ID"(+)=1)

С конкретными полями:
select PAY_ID, PAY_SUM
  from V_PAYMENTS
 where client_id = 1
------------------------------------------------------------------------------
| Id  | Operation         | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |          |   100 |  1200 |    49   (3)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| PAYMENTS |   100 |  1200 |    49   (3)| 00:00:01 |
------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter("P"."CLIENT_ID"=1)

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

На самом деле есть случай, когда извлекаемые и используемые в соединении с другими таблицами целиком находятся в одном составном индексе. В этом случае оракл может вообще не обращаться к сегменту таблицы, если не требуется дополнительных полей. И тут разница уже будет - в случае со * это будет "index scan + table access", в случае выбора только проиндексированного поля - только "index scan" без обращения к таблице. Плюс ещё бывают поля с *LOB'ами которые ещё в одном сегменте находятся и могут не требоваться в данном конкретном запросе.

Запрос со *:
-----------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                  | Name     | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                           |          |     1 |    71 |     4   (0)| 00:00:01 |       |       |
|   1 |  PARTITION LIST SINGLE                     |          |     1 |    71 |     4   (0)| 00:00:01 |   KEY |   KEY |
|   2 |   TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| TBL1     |     1 |    71 |     4   (0)| 00:00:01 |   KEY |   KEY |
|*  3 |    INDEX RANGE SCAN                        | IDX_TBL1 |     1 |       |     3   (0)| 00:00:01 |   KEY |   KEY |
-----------------------------------------------------------------------------------------------------------------------

Запрос с полем:
--------------------------------------------------------------------------------------------------
| Id  | Operation             | Name     | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |          |     1 |    12 |     3   (0)| 00:00:01 |       |       |
|   1 |  PARTITION LIST SINGLE|          |     1 |    12 |     3   (0)| 00:00:01 |   KEY |   KEY |
|*  2 |   INDEX RANGE SCAN    | IDX_TBL1 |     1 |    12 |     3   (0)| 00:00:01 |   KEY |   KEY |
--------------------------------------------------------------------------------------------------

Без чтения планов в оракле оптимизировать что-то невозможно. Поэтому любые советы по оптимизации по хорошему нужно сопровождать демонстрацией плана запросов, например как это делал @xtender в своих статьях на orasql.org.

Что касается CTE в оракле, то использовать их нужно с умом - они могут материализовываться в отдельную временную таблицу, а могут и инлайниться, встраиваюсь в запрос таким образом, как будто CTE и нет вообще, форсированно этим можно управлять соответствующими хинтами MATERIALIZE и INLINE соответственно. CTE c материализацией часто используется для следующей цели - допустим у нас есть тяжелый подзапрос с большим количеством соединений, возвращающий очень малое количество строк. И этот подзапрос нужно связать с другим сложным подзапросом или представлением - чтобы оракл не пытался объединить эти две части, ломая хорошо оптимизированные подзапросы, которые вернут условно по три строчки за пол секунды каждый, в один мегазапрос с кучей рейндж или фулл сканов, который будет работать пол часа. Поэтому надо чётко понимать, что заинлайненный СТЕ - это просто декорация, а материализованный - временная таблица, в которую выгружены все данные из подзапроса СТЕ. В вашем случае без CTE вероятно как раз и родился такой мегазапрос, который отъедал temp на группировках/сортировках, а переписав его на CTE вы материализовали какую-то его часть в отдельный подзапрос, который выполнился отдельно и выгрузился во временную таблицу и дальше уже использовались только результирующие строки из него.

Думается мне красивых имён аккаунтов хватит ещё на десяток подобных новостей.

Ещё в школе наткнулся на отличную русскоязычную справку в Турбо Паскале с гиперссылками и примерами, что сразу сподвигло изучать язык и программирование в частности даже за пределами преподаваемого на уроках. Думаю если бы не это, то я бы и не стал программистом.

Это уже из статьи видно, не "смешной милый котик дарит цветок", а кот с цветком по факту.

А CTE разве не будет считаться подзапросом? А ещё у вас выпали IP адреса, у которых было меньше 3-х посещений.

На оракле можно было бы решить через model, например -

Запрос
with journal as
(
    select 1 as id,  '127.0.0.1' as ip, to_timestamp('01.01.2010 10:10','dd.mm.yyyy HH24:mi') as time from dual union all
    select 2 as id,  '8.8.8.8'   as ip, to_timestamp('02.01.2010 11:15','dd.mm.yyyy HH24:mi') as time from dual union all
    select 3 as id,  '127.0.0.1' as ip, to_timestamp('03.01.2010 12:20','dd.mm.yyyy HH24:mi') as time from dual union all
    select 4 as id,  '127.0.0.1' as ip, to_timestamp('04.01.2010 13:25','dd.mm.yyyy HH24:mi') as time from dual union all
    select 5 as id,  '8.8.8.8'   as ip, to_timestamp('05.01.2010 14:30','dd.mm.yyyy HH24:mi') as time from dual union all
    select 6 as id,  '127.0.0.1' as ip, to_timestamp('06.01.2010 15:35','dd.mm.yyyy HH24:mi') as time from dual union all
    select 7 as id,  '8.8.8.8'   as ip, to_timestamp('07.01.2010 16:40','dd.mm.yyyy HH24:mi') as time from dual union all
    select 8 as id,  '127.0.0.1' as ip, to_timestamp('08.01.2010 17:45','dd.mm.yyyy HH24:mi') as time from dual union all
    select 9 as id,  '127.0.0.1' as ip, to_timestamp('09.01.2010 18:50','dd.mm.yyyy HH24:mi') as time from dual union all
    select 10 as id, '1.1.1.1'   as ip, to_timestamp('10.01.2010 19:55','dd.mm.yyyy HH24:mi') as time from dual union all
    select 11 as id, '1.1.1.1'   as ip, to_timestamp('11.01.2010 20:55','dd.mm.yyyy HH24:mi') as time from dual 
)
select ip, third_visit
  from journal j
model 
    return updated rows -- будем брать только те строки, что попали под правила
    partition by (j.ip)
    dimension by (
        -- нам понадобится номер посещения по IP
        row_number() over (partition by j.ip order by j.time, j.id) as num_visit,
        -- и всего посещений
        count(*) over (partition by j.ip) as total_visits
    )
    measures (cast(null as timestamp) as third_visit, j.time)
    rules 
    (
        -- возьмём дату третьего посещения, если всего посещений >= 3
        third_visit[num_visit = 3, total_visits >= 3] = time[cv(), cv()],
        -- возьмем пустую дату для первой строки по IP, если всего посешений меньше 3
        third_visit[num_visit = 1, total_visits < 3] = null
    )
/

Вывод:
IP        THIRD_VISIT
--------- ------------------------------
1.1.1.1
127.0.0.1 04-JAN-10 01.25.00.000000 PM
8.8.8.8   07-JAN-10 04.40.00.000000 PM

Я считал, что нужно найти третье посещение, а не третье посещение за день. Но это что-то нифига не базовый уровень :D Базовый уровень - это подзапрос с аналитикой.

И в оракле тоже оптимизатор, а планировщиком Oracle Scheduler называют, который джобиками оркестрирует.

Предлагаю сначала на чем-нибудь простеньком потренироваться, типа Сахары или Гоби. Когда обкатаем превращение пустынь в цветущие оазисы до совершенства, там уже можно планку выше поднимать.

Информация

В рейтинге
Не участвует
Откуда
Пермь, Пермский край, Россия
Дата рождения
Зарегистрирован
Активность

Специализация

Fullstack Developer, Software Architect
Lead
Oracle
Oracle PL/SQL
C#
WPF
XAML
.NET