JavaRush /Блоги Java /Random-TG /Оптимизатсияи сард SQL, ки аз модели хароҷот вобаста нест...

Оптимизатсияи сард SQL, ки аз модели хароҷот вобаста нестанд. Қисми 2

Дар гурӯҳ нашр шудааст
Оптимизатсияи сард SQL, ки аз модели хароҷот вобаста нестанд. Қисми 1 Оптимизатсияи сард SQL, ки аз модели хароҷот вобаста нестанд.  Қисми 2 - 1

4. Бартараф кардани предикатхои «бемаъно».

Баробари бемаънӣ предикатҳое ҳастанд, ки (қариб) ҳамеша дурустанд. Тавре ки шумо тасаввур карда метавонед, агар шумо пурсед:
SELECT * FROM actor WHERE 1 = 1;
...он гоҳ базаҳои маълумот онро воқеан иҷро намекунанд, балки онро нодида мегиранд. Боре ман ба як савол дар бораи ин дар Stack Overflow ҷавоб додам ва бинобар ин ман тасмим гирифтам, ки ин мақоларо нависам. Ман санҷиши инро ҳамчун машқ ба ихтиёри хонанда мегузорам, аммо агар предикат каме "бемаъно" бошад, чӣ мешавад? Барои намуна:
SELECT * FROM film WHERE release_year = release_year;
Оё ба шумо дар ҳақиқат лозим аст, ки арзиши ҳар як сатрро бо худ муқоиса кунед? Не, ҳеҷ арзише вуҷуд надорад, ки барои он ин предикат FALSE бошад , дуруст? Аммо мо бояд ҳанӯз онро тафтиш кунем. Ҳарчанд предикат наметавонад ба FALSE баробар бошад , он метавонад ба NULL дар ҳама ҷо баробар бошад , боз аз сабаби мантиқи се арзишманд. Сутуни RELEASE_YEAR беэътибор аст ва агар яке аз сатрҳо RELEASE_YEAR IS NULL дошта бошанд , пас NULL = NULL NULL мешавад ва сатр бояд нест карда шавад. Ҳамин тариқ, дархост чунин мешавад:
SELECT * FROM film WHERE release_year IS NOT NULL;
Кадом пойгоҳи додаҳо ин корро мекунанд?

DB2

Бале!
Explain Plan
-------------------------------------------------
ID | Operation    |                   Rows | Cost
 1 | RETURN       |                        |   49
 2 |  TBSCAN FILM | 1000 of 1000 (100.00%) |   49
Predicate Information
 2 - SARG Q1.RELEASE_YEAR IS NOT NULL

MySQL

Ин шармовар аст, аммо MySQL боз ҳам предикатҳоро дар нақшаҳои иҷроӣ харита намекунад, аз ин рӯ фаҳмидани он ки оё MySQL ин оптимизатсияи мушаххасро амалӣ мекунад, каме душвор аст. Шумо метавонед баҳодиҳии фаъолиятро анҷом диҳед ва бубинед, ки оё ягон муқоисаи миқёси калон анҷом дода мешавад. Ё шумо метавонед индекс илова кунед:
CREATE INDEX i_release_year ON film (release_year);
Ва бар ивази дархостҳои зерин нақшаҳо гиред:
SELECT * FROM film WHERE release_year = release_year;
SELECT * FROM film WHERE release_year IS NOT NULL;
Агар оптимизатсия кор кунад, пас нақшаҳои ҳарду дархост бояд тақрибан якхела бошанд. Аммо дар ин ҳолат ин тавр нест:
ID  TABLE  POSSIBLE_KEYS   ROWS  FILTERED  EXTRA
------------------------------------------------------
1   film             1000  10.00           Using where

ID  TABLE  POSSIBLE_KEYS   ROWS  FILTERED  EXTRA
------------------------------------------------------
1   film   i_release_year  1000  100.00    Using where
Тавре ки шумо мебинед, ду дархости мо дар арзишҳои сутунҳои POSSIBLE_KEYS ва FILTERED ба таври назаррас фарқ мекунанд . Аз ин рӯ, ман тахмин мезанам, ки MySQL инро оптимизатсия намекунад.

Oracle

Бале!
----------------------------------------------------
| Id  | Operation         | Name | Starts | E-Rows |
----------------------------------------------------
|   0 | SELECT STATEMENT  |      |      1 |        |
|*  1 |  TABLE ACCESS FULL| FILM |      1 |   1000 |
----------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("RELEASE_YEAR" IS NOT NULL)

PostgreSQL

Мутаассифона не!
QUERY PLAN
--------------------------------------------------------------
Seq Scan on film  (cost=0.00..67.50 rows=5 width=386)
  Filter: ((release_year)::integer = (release_year)::integer)
Нақшаҳо ва хароҷот гуногунанд. Маҳз, ба арзёбии кардиналият нигаред, ки ин комилан хуб нест, дар ҳоле ки ин предикат:
SELECT * FROM film WHERE release_year IS NOT NULL;
натиҷаҳои хеле беҳтар медиҳад:
QUERY PLAN
---------------------------------------------------------
Seq Scan on film  (cost=0.00..65.00 rows=1000 width=386)
  Filter: (release_year IS NOT NULL)
Буммер!

SQL Server

Аҷиб он аст, ки SQL Server низ ин корро намекунад:
|--Table Scan(OBJECT:([film]), WHERE:([release_year]=[release_year]))
Аммо аз руи намуди план, баходихии карди-налй ва арзиши аслй дуруст аст. Аммо дар таҷрибаи ман бо SQL Server, ман гуфта метавонам, ки дар ин ҳолат ягон оптимизатсия рух намедиҳад, зеро SQL Server предикати воқеан иҷрошударо дар нақша намоиш медиҳад (барои фаҳмидани он, ки чаро ба мисолҳои маҳдудияти CHECK дар зер нигаред ). Дар бораи предикатҳои "бемаъно" дар сутунҳои NO NULL чӣ гуфтан мумкин аст ? Табдилдиҳии дар боло зикршуда танҳо лозим буд, зеро RELEASE_YEAR-ро муайян кардан мумкин нест. Агар шумо ҳамон як дархости бемаъноро дар сутуни FILM_ID иҷро кунед, чӣ мешавад ?
SELECT * FROM film WHERE film_id = film_id
Оё он ҳоло ба ягон предикат мувофиқат намекунад? Ё ҳадди ақалл ҳамин тавр бояд бошад. Аммо ин аст?

DB2

Бале!
Explain Plan
-------------------------------------------------
ID | Operation    |                   Rows | Cost
 1 | RETURN       |                        |   49
 2 |  TBSCAN FILM | 1000 of 1000 (100.00%) |   49
Ягон предикат умуман истифода намешавад ва мо ҳама филмҳоро интихоб мекунем.

MySQL

Бале! (Боз тахмини маълумотнок)
ID  TABLE  POSSIBLE_KEYS   ROWS  FILTERED  EXTRA
------------------------------------------------------
1   film                   1000  100.00
Аҳамият диҳед, ки сутуни EXTRA ҳоло холӣ аст, гӯё мо умуман банди WHERE надорем !

Oracle

Бале!
----------------------------------------------------
| Id  | Operation         | Name | Starts | E-Rows |
----------------------------------------------------
|   0 | SELECT STATEMENT  |      |      1 |        |
|   1 |  TABLE ACCESS FULL| FILM |      1 |   1000 |
----------------------------------------------------
Боз ҳам, ҳеҷ предикат татбиқ намешавад.

PostgreSQL

Вой, боз не!
QUERY PLAN
------------------------------------------------------
Seq Scan on film  (cost=0.00..67.50 rows=5 width=386)
  Filter: (film_id = film_id)
Филтр татбиқ карда мешавад ва холҳои кардиналӣ ҳанӯз 5 аст. Баммер!

SQL Server

Ва ин ҷо боз не!
|--Table Scan(OBJECT:([film]), WHERE:([film_id]=[film_id]))

Хулоса

Ин як оптимизатсияи оддӣ ба назар мерасад, аммо он дар ҳама DBMS истифода намешавад; алалхусус, аҷиб он аст, ки он дар SQL Server истифода намешавад!
Пойгоҳи додаҳо Предикатҳои бемаъно, вале зарурӣ (семантикаи NULL) Предикатҳои бемаънӣ ва нолозим (семантикаи NULL)
DB2 LUW 10.5 Бале Бале
MySQL 8.0.2 Не Бале
Oracle 12.2.0.1 Бале Бале
PostgreSQL 9.6 Не Не
SQL Server 2014 Не Не

5. Пешгӯиҳо дар зерпурсишҳои EXISTS

Ҷолиб он аст, ки ман ҳамеша дар бораи онҳо дар мастер-класси худ мепурсам, ки ман дар он нуқтаи назарро ҳимоя мекунам, ки SELECT * одатан ба ягон фоида оварда намерасонад. Савол ин аст: оё SELECT * -ро дар зерпурсиш EXISTS истифода бурдан мумкин аст ? Масалан, агар актёронеро пайдо кунем, ки дар филмҳо бозӣ кардаанд...
SELECT first_name, last_name
FROM actor a
WHERE EXISTS (
  SELECT * -- Is this OK?
  FROM film_actor fa
  WHERE a.actor_id = fa.actor_id
)
Ва ҷавоб ин аст ... ҳа. Метавонед. Ситорача ба дархост таъсир намерасонад. Чӣ тавр шумо ба ин боварӣ дошта метавонед? Дархости зеринро баррасӣ кунед:
-- DB2
SELECT 1 / 0 FROM sysibm.dual
-- Oracle
SELECT 1 / 0 FROM dual
-- PostgreSQL, SQL Server
SELECT 1 / 0
-- MySQL
SELECT pow(-1, 0.5);
Ҳамаи ин пойгоҳи додаҳо дар бораи тақсимот ба хатогии сифр гузориш медиҳанд. Ба як далели ҷолиб таваҷҷӯҳ кунед: дар MySQL, вақте ки мо ба сифр тақсим мекунем, мо ба ҷои хатогӣ NULL мегирем, аз ин рӯ, мо бояд боз як амали ғайриқонуниро анҷом диҳем. Акнун, агар мо ба ҷои дархостҳои дар боло овардашуда дархостҳои зеринро иҷро кунем, чӣ мешавад?
-- DB2
SELECT CASE WHEN EXISTS (
  SELECT 1 / 0 FROM sysibm.dual
) THEN 1 ELSE 0 END
FROM sysibm.dual
-- Oracle
SELECT CASE WHEN EXISTS (
  SELECT 1 / 0 FROM dual
) THEN 1 ELSE 0 END
FROM dual
-- PostgreSQL
SELECT EXISTS (SELECT 1 / 0)
-- SQL Server
SELECT CASE WHEN EXISTS (
  SELECT 1 / 0
) THEN 1 ELSE 0 END
-- MySQL
SELECT EXISTS (SELECT pow(-1, 0.5));
Ҳоло ҳеҷ яке аз пойгоҳи додаҳо хатогиро барнамегардонад. Ҳамаашон TRUE ё 1-ро бармегардонанд . Ин маънои онро дорад, ки ҳеҷ яке аз пойгоҳи додаҳои мо воқеан проексияи (яъне банди SELECT ) зерпурсиш EXISTS -ро арзёбӣ намекунад . Масалан, SQL Server нақшаи зеринро нишон медиҳад:
|--Constant Scan(VALUES:((CASE WHEN (1) THEN (1) ELSE (0) END)))
Тавре ки шумо мебинед, ифодаи CASE ба доимӣ табдил дода шудааст ва зерпурсиш нест карда шудааст. Дигар пойгоҳи додаҳо зерпурсишро дар нақша нигоҳ медоранд ва дар бораи пешгӯӣ чизе зикр намекунанд, аз ин рӯ биёед бори дигар ба нақшаи дархости аслии Oracle назар андозем:
SELECT first_name, last_name
FROM actor a
WHERE EXISTS (
  SELECT *
  FROM film_actor fa
  WHERE a.actor_id = fa.actor_id
)
Нақшаи дархости дар боло зикршуда чунин менамояд:
------------------------------------------------------------------
| Id  | Operation             | Name                    | E-Rows |
------------------------------------------------------------------
|   0 | SELECT STATEMENT      |                         |        |
|*  1 |  HASH JOIN SEMI       |                         |    200 |
|   2 |   TABLE ACCESS FULL   | ACTOR                   |    200 |
|   3 |   INDEX FAST FULL SCAN| IDX_FK_FILM_ACTOR_ACTOR |   5462 |
------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("A"."ACTOR_ID"="FA"."ACTOR_ID")
Column Projection Information (identified by operation id):
-----------------------------------------------------------
   1 - (#keys=1) LAST_NAME, FIRST_NAME
   2 - (rowset=256) A.ACTOR_ID, FIRST_NAME, LAST_NAME
   3 - FA.ACTOR_ID
Мо маълумотро дар бораи проекция дар Id=3 мушоҳида мекунем . Дар асл, мо ҳатто ба ҷадвали FILM_ACTOR дастрасӣ надорем , зеро ба мо лозим нест. Предикати EXISTS- ро метавон бо истифода аз индекси калиди хориҷӣ дар як сутуни ACTOR_ID иҷро кард - ҳама чизе, ки барои ин дархост лозим аст - гарчанде ки мо SELECT * навиштаем .

Хулоса

Хушбахтона, ҳама пойгоҳи додаҳои мо пешгӯиро аз зерпурсишҳои EXISTS хориҷ мекунанд :
Пойгоҳи додаҳо Проексия вуҷуд дорад
DB2 LUW 10.5 Бале
MySQL 8.0.2 Бале
Oracle 12.2.0.1 Бале
PostgreSQL 9.6 Бале
SQL Server 2014 Бале
Қисми 3- ро тамошо кунед , ки дар он мо дигар оптимизатсияи SQL-ро муҳокима хоҳем кард.
Шарҳҳо
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION