2017-02-01 4 views
0

Скажем, у меня есть таблица X с транзакциями, где CUSTOMER_ID является первичным ключом.Оптимизация множественных внутренних самосоединений по уникальному ключу

Также у меня есть сотня «функций» (с точки зрения машинного обучения), то есть текста запросов в этой таблице X. Все эти запросы как:

запроса 1:

SELECT 
X.CUSTOMER_ID, 
WHEN(X.GENDER = "F" AND X.IS_PREGNANT = TRUE) THEN 1 OTHERWISE 0 AS WILL_BUY_FOR_KIDS 
FROM X 

Запрос ххх:

SELECT 
X.CUSTOMER_ID, 
WHEN(X.GENDER = "M" AND X.AVG_AMOUNT > 1000) THEN 1 OTHERWISE 0 AS RICH_DUDE 
FROM X 


Задача состоит в том, чтобы произвести таблицу со всеми "особенностями", рассчитанные по X Таблица. Так что мне нужно создать текст выходного запроса (программно), используя тексты «функциональных» запросов. Что-то вроде:

SELECT 
* 
FROM SOME_QUERY_1 
INNER JOIN SOME_QUERY_X 
ON SOME_QUERY_1.CUSTOMER_ID = SOME_QUERY_X.CUSTOMER_ID 
... 

Такой выход запроса выше может быть очень медленным, когда внутреннее Самосоединения сотни подзапросов. Ясно, что было бы здорово, если SQL двигатель будет "переписать" этот запрос на что-то подобное (избегать соединения):

SELECT 
CUSTOMER_ID, 
WHEN(X.GENDER = "F" AND X.IS_PREGNANT = TRUE) THEN 1 OTHERWISE 0 AS WILL_BUY_FOR_KIDS, 
WHEN(X.GENDER = "M" AND X.AVG_AMOUNT > 1000) THEN 1 OTHERWISE 0 AS RICH_DUDE, 
..... 
FROM X 


Несколько вопросов:

  1. Есть ли SQL двигатель что может сделать оптимизацию выше (MySQL, PostgreSQL и т. д.)?
  2. С общей численной алгеброй мы доказали правила упрощения выражений (например: (a + b) * a = a^2 + b*a). Существуют ли такие правила для реляционной альгребры?
+0

Какова цель этих подзапросов? – wildplasser

+0

BTW: postgres достаточно умен, чтобы свернуть подзапросы, но он не обнаруживает, что x1. * И x2. * Относятся к одному кортежу. Результат - объединение слияния при двух индексах. – wildplasser

+0

@wildplasser Я обновил описание, чтобы добавить больше контекста на вопрос –

ответ

1

Кажется, что оптимизатор Oracle выполняет эту работу.

drop table x; 
create table x (a int primary key,b int); 

select x0.b,x1.b,x2.b,x3.b,x4.b,x5.b,x6.b,x7.b,x8.b,x9.b 

from   (select x.a,x.b from x) x0 
     join (select x.a,x.b from x) x1 on x1.a = x0.a 
     join (select x.a,x.b from x) x2 on x2.a = x0.a 
     join (select x.a,x.b from x) x3 on x3.a = x0.a 
     join (select x.a,x.b from x) x4 on x4.a = x0.a 
     join (select x.a,x.b from x) x5 on x5.a = x0.a 
     join (select x.a,x.b from x) x6 on x6.a = x0.a 
     join (select x.a,x.b from x) x7 on x7.a = x0.a 
     join (select x.a,x.b from x) x8 on x8.a = x0.a 
     join (select x.a,x.b from x) x9 on x9.a = x0.a 
;   

Примечание 9-ELIMINATE_JOIN в плане исполнения.

--------------------------------------------------------------------------  
| Id | Operation   | Name | Rows | Bytes | Cost (%CPU)| Time  |  
--------------------------------------------------------------------------  
| 0 | SELECT STATEMENT |  |  1 | 26 |  2 (0)| 00:00:01 |  
| 1 | TABLE ACCESS FULL| X |  1 | 26 |  2 (0)| 00:00:01 |  
--------------------------------------------------------------------------  

Query Block Name/Object Alias (identified by operation id):     
-------------------------------------------------------------     

PLAN_TABLE_OUTPUT                
-------------------------------------------------------------------------------- 

    1 - SEL$44564B95/[email protected]$2             

Outline Data                  
-------------                 

    /*+                   
     BEGIN_OUTLINE_DATA               
     FULL(@"SEL$44564B95" "X"@"SEL$2")           
     OUTLINE(@"SEL$3")               
     OUTLINE(@"SEL$2")               
     OUTLINE(@"SEL$1")               
     MERGE(@"SEL$3")               
     MERGE(@"SEL$2")               
     OUTLINE(@"SEL$5428C7F1")             
     OUTLINE(@"SEL$5")               
     OUTLINE(@"SEL$4")               
     MERGE(@"SEL$5428C7F1")              
     MERGE(@"SEL$5")               
     OUTLINE(@"SEL$730B2DEF")             
     OUTLINE(@"SEL$7")               
     OUTLINE(@"SEL$6")               
     MERGE(@"SEL$730B2DEF")              
     MERGE(@"SEL$7")               
     OUTLINE(@"SEL$DE510E9C")             
     OUTLINE(@"SEL$9")               
     OUTLINE(@"SEL$8")               
     MERGE(@"SEL$DE510E9C")              
     MERGE(@"SEL$9")               
     OUTLINE(@"SEL$6C54F645")             
     OUTLINE(@"SEL$11")               
     OUTLINE(@"SEL$10")               
     MERGE(@"SEL$6C54F645")              
     MERGE(@"SEL$11")               
     OUTLINE(@"SEL$5E3B1022")             
     OUTLINE(@"SEL$13")               
     OUTLINE(@"SEL$12")               
     MERGE(@"SEL$5E3B1022")              
     MERGE(@"SEL$13")               
     OUTLINE(@"SEL$D60B40D8")             
     OUTLINE(@"SEL$15")               
     OUTLINE(@"SEL$14")               
     MERGE(@"SEL$D60B40D8")              
     MERGE(@"SEL$15")               
     OUTLINE(@"SEL$B8655000")             
     OUTLINE(@"SEL$17")               
     OUTLINE(@"SEL$16")               
     MERGE(@"SEL$B8655000")              
     MERGE(@"SEL$17")               
     OUTLINE(@"SEL$EC740ABE")             
     OUTLINE(@"SEL$19")               
     OUTLINE(@"SEL$18")               
     MERGE(@"SEL$EC740ABE")              
     MERGE(@"SEL$19")               
     OUTLINE(@"SEL$7AC5A3AA")             
     OUTLINE(@"SEL$20")               
     MERGE(@"SEL$7AC5A3AA")              
     OUTLINE(@"SEL$F6D45FB3")             
     ELIMINATE_JOIN(@"SEL$F6D45FB3" "X"@"SEL$17")        
     ELIMINATE_JOIN(@"SEL$F6D45FB3" "X"@"SEL$15")        
     ELIMINATE_JOIN(@"SEL$F6D45FB3" "X"@"SEL$13")        
     ELIMINATE_JOIN(@"SEL$F6D45FB3" "X"@"SEL$11")        
     ELIMINATE_JOIN(@"SEL$F6D45FB3" "X"@"SEL$9")        
     ELIMINATE_JOIN(@"SEL$F6D45FB3" "X"@"SEL$7")        
     ELIMINATE_JOIN(@"SEL$F6D45FB3" "X"@"SEL$5")        
     ELIMINATE_JOIN(@"SEL$F6D45FB3" "X"@"SEL$3")   
     OUTLINE(@"SEL$5A225B26")             
     ELIMINATE_JOIN(@"SEL$5A225B26" "X"@"SEL$19")        
     OUTLINE_LEAF(@"SEL$44564B95")            
     ALL_ROWS                 
     DB_VERSION('11.2.0.2')              
     OPTIMIZER_FEATURES_ENABLE('11.2.0.2')          
     IGNORE_OPTIM_EMBEDDED_HINTS            
     END_OUTLINE_DATA               
    */                    

Column Projection Information (identified by operation id):      

    1 - "X"."B"[NUMBER,22]              

Note                    
-----                   
    - dynamic sampling used for this statement (level=2)       
+0

Прохладный. Самое интересное - это этап ELIMINATE_JOIN, распространенный среди других SQL-движков? Есть ли проверенные реляционные правила, которые могут сделать этот трюк, или это «взлом»? –

+0

Привет Ларс :-) Этот вопрос слишком широк. Между теорией существует большое расстояние к реализации каждого конкретного провайдера. –

0

Во-первых, вы должны написать запрос, как:

SELECT X1.A * 2, // Some operation on X1.A 
     X2.B/2 // Some operation on X2.B 
FROM X X1 JOIN 
    X X2 
    ON X1.C = X2.C; 

Подзапросы не дают никакого значения (но я вернусь к этому позже).

Если C объявлен как unique (или primary key), то на поле есть указатель. Я уверен, что каждая база данных будет по-прежнему делать JOIN, но это будет очень быстро:

  • Процесс записи (X1) и найти C
  • Lookup C в индексе
  • Возврат к записи уже в памяти, чтобы получить X2.

Дополнительные накладные расходы для прохождения индекса не являются большой нагрузкой по сравнению с чтением данных в первую очередь.

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

Вы можете спросить, почему в базе данных нет оптимизации для этого. Ну, разработчики баз данных больше ориентированы на оптимизацию хорошо написанных запросов, чем на оптимизацию плохо написанных. Если нет необходимости в JOIN, тогда запись запроса должна быть в курсе. (Фактически, JOIN имеет одно использование, он отфильтровывает значения NULL.)

+0

Основная проблема заключается в том, чтобы избежать множественных внутренних самосоединений по первичному ключу. Я обновил описание, чтобы добавить больше контекста к вопросу. –

+0

Не уверен, что ... –

Смежные вопросы