30
WITH konstrukcijas izmantošanas varianti The WITH query_name clause lets you assign a name to a subquery block. You can then reference the subquery block multiple places in the query by specifying query_name. Oracle Database optimizes the query by treating the query name as either an inline view or as a temporary table. WITH <alias_one> as (subquery_sql_statement), <alias_two> as (sql_statement_from_alias_one) SELECT <column_name_list> FROM <alias_one>, <alias_two> WHERE <join_condition>; WITH Q1 as (SELECT dummy FROM DUAL) SELECT dummy FROM Q1; WITH Q1 AS (SELECT inst_id FROM gv$session), Q2 AS (SELECT unique inst_id FROM Q1 UNION ALL SELECT unique inst_id FROM Q1) 1

datubaze.files.wordpress.com viewWITH konstrukcijas izmantošanas varianti. The . WITH. query_name. clause lets you assign a name to a subquery block. You can then reference the subquery

Embed Size (px)

Citation preview

WITH konstrukcijas izmantošanas varianti

The WITH query_name clause lets you assign a name to a subquery block.

You can then reference the subquery block multiple places in the query by specifying query_name. Oracle Database optimizes the query by treating the query name as either an inline view or as a temporary table.

WITH <alias_one> as (subquery_sql_statement),     <alias_two> as (sql_statement_from_alias_one)

SELECT <column_name_list>FROM <alias_one>, <alias_two>WHERE <join_condition>;

WITH Q1 as (SELECT dummy FROM DUAL)

SELECT dummy FROM Q1;

WITH Q1 AS (SELECT inst_id FROM gv$session),      Q2 AS (SELECT unique inst_id FROM Q1   UNION ALL   SELECT unique inst_id FROM Q1)

SELECT /*+ MATERIALIZE */ *FROM Q1, Q2WHERE Q1.inst_id = Q2.inst_id;

1

 Vaicājumi ar WITH konstrukciju (subquery factoring)1

withPROJEKTA_DARBINIEKI asselect COUNT(DARBINIEKA_NUMURS) darbinieku_skaits, PROJEKTA_NOSAUKUMS

from DARBINIEKIgroup by PROJEKTA_NOSAUKUMS

PROJEKTA_DARBI asselect COUNT(DARBA_NUMURS) darbu_skaits, PROJEKTA_NOSAUKUMS

from DARBIgroup by PROJEKTA_NOSAUKUMS

select A.PROJEKTA_NOSAUKUMS, A.darbinieku_skaits, B.darbu_skaitsfrom PROJEKTA_DARBINIEKI A, PROJEKTA_DARBI Bwhere A.PROJEKTA_NOSAUKUMS = B.PROJEKTA_NOSAUKUMS

1 http://psoug.org/reference/with.html

with vaicajums_1 as ( SELECT ... )

vaicajums_2 as (SELECT ... )

select ... from vaicajums_1, vaicajums_2

3

Subquery factoring_clause

The column aliases following the query_name and the set operators separating multiple subqueries in the AS clause are valid and required for recursive subquery factoring.

The search_clause and cycle_clause are valid only for recursive subquery factoring but are not required.

You can specify this clause in any top-level SELECT statement and in most types of subqueries. The query name is visible to the main query and to all subsequent subqueries. For recursive subquery factoring, the query name is even visible to the subquery that defines the query name itself.

4

Recursive Subquery Factoring If a subquery_factoring_clause refers to its own query_name in the subquery that defines it, then the subquery_factoring_clause is said to be recursive. A recursive subquery_factoring_clause must contain two query blocks:

1) the first is the anchor member. The anchor member must appear before the recursive member, and it cannot reference query_name. The anchor member can be composed of one or more query blocks combined by the set operators: UNION ALL, UNION, INTERSECT or MINUS.

2) the second is the recursive member. The recursive member must follow the anchor member and must reference query_name exactly once. You must combine the recursive member with the anchor member using the UNION ALL set operator.

The number of column aliases following WITH query_name and the number of columns in the SELECT lists of the anchor and recursive query blocks must be the same.

5

search_clause  Use the SEARCH clause to specify an ordering for the rows.1. Specify BREADTH FIRST BY if you want sibling rows returned before any child rows are returned.2. Specify DEPTH FIRST BY if you want child rows returned before any siblings rows are returned.3. Sibling rows are ordered by the columns listed after the BY keyword.4. The c_alias list following the SEARCH keyword must contain column names from the column alias list for query_name.5. The ordering_column is automatically added to the column list for the query name. The query that selects from query_name can include an ORDER BY on ordering_column to return the rows in the order that was specified by the SEARCH clause.

6

cycle_clause 

Use the CYCLE clause to mark cycles in the recursion.1. The c_alias list following the CYCLE keyword must contain column names from the column alias list for query_name. Oracle Database uses these columns to detect a cycle.2. cycle_value and no_cycle_value should be character strings of length 1.3. If a cycle is detected, then the cycle mark column specified by cycle_mark_c_alias for the row causing the cycle is set to the value specified for cycle_value. The recursion will then stop for this row. That is, it will not look for child rows for the offending row, but it will continue for other noncyclic rows.4. If no cycles are found, then the cycle mark column is set to the default value specified for no_cycle_value.5. The cycle mark column is automatically added to the column list for the query_name.6. A row is considered to form a cycle if one of its ancestor rows has the same values for the cycle columns.If you omit the CYCLE clause, then the recursive WITH clause returns an error if cycles are discovered. In this case, a row forms a cycle if one of its ancestor rows has the same values for all the columns in the column alias list for query_name that are referenced in the WHERE clause of the recursive member.

7

Subquery factoring example 

The following statement creates the query names dept_costs and avg_cost for the initial query block containing a join, and then uses the query names in the body of the main query.

WITH dept_costs AS ( SELECT department_name, SUM(salary) dept_total FROM employees e, departments d WHERE e.department_id = d.department_id GROUP BY department_name), avg_cost AS ( SELECT SUM(dept_total)/COUNT(*) avg FROM dept_costs)

SELECT * FROM dept_costs WHERE dept_total > (SELECT avg FROM avg_cost) ORDER BY department_name;

DEPARTMENT_NAME DEPT_TOTAL-----------------------------------------------------Sales 304500Shipping 156400

Department total salaries>average of all Departments total salaries

8

Departamentu darbinieku algu summa un vidējā visu departamentu darbinieku algu summa

select 'AVG ', 0 DE_KOPĀ, AVG(c.DE_KOPĀ)from (select b.DE_NOS, SUM(a.ALGA) DE_KOPĀ from DARBINIEKI a, DEPARTAMENTI b where a.DE_NUM = b.DE_NUM group by b.DE_NOS) cUNION ALLselect e.DE_NOS, SUM(d.ALGA) DE_KOPĀ, 0 from DARBINIEKI d, DEPARTAMENTI e where d.DE_NUM = e.DE_NUM group by e.DE_NOS;

9

Pakārtotais vaicājums ar starpniecību (subquery factoring statement)

WITH DE_IZMAKSAS as ( select DE_NOS, SUM(ALGA) DE_KOPĀ from DARBINIEKI a, DEPARTAMENTI b where a.DE_NUM = b.DE_NUM group by DE_NOS), VID_IZMAKSAS as ( select SUM(DE_KOPĀ)/COUNT(*) VID from DE_IZMAKSAS)

select * from DE_IZMAKSASwhere DE_KOPĀ>= (select VID from VID_IZMAKSAS) order by DE_NOS;

Departamenta nosaukums un darbinieku algu summa.

Vidējā departamentu darbinieku algu summa.

Departamenti, kuru darbinieku algu summa ir lielāka vai vienāda ar vidējo departamentu darbinieku algu summu

10

Recursive subquery factoring

Examples The following statement shows the employees who directly or indirectly report to employee 101 and their reporting level.

WITH reports_to_101 (eid, emp_last, mgr_id, reportLevel) AS ( SELECT employee_id, last_name, manager_id, 0 reportLevel FROM employees WHERE employee_id = 101 UNION ALL SELECT e.employee_id, e.last_name, e.manager_id, reportLevel+1 FROM reports_to_101 r, employees e WHERE r.eid = e.manager_id )

SELECT eid, emp_last, mgr_id, reportLevelFROM reports_to_101ORDER BY reportLevel, eid;

EID EMP_LAST MGR_ID REPORTLEVEL--------------------------------------------------------------------- 101 Kochhar 100 0 108 Greenberg 101 1 200 Whalen 101 1 203 Mavris 101 1 204 Baer 101 1 205 Higgins 101 1 109 Faviet 108 2 110 Chen 108 2 111 Sciarra 108 2 112 Urman 108 2 113 Popp 108 2 206 Gietz 205 2

11

Rekursīvs vaicājums ar starpniecību

Noteikt darbiniekus, kuri tieši vai netieši sniedz pārskatus 1 darbiniekam un viņu atskaišu līmeni.

Darbinieku pakārtotības koks

1 DEP_1

2 DEP_1 3 DEP_1

4 DEP_1 5 DEP_1

6 DEP_2 10 DEP_3

9 DEP_2

8 DEP_27 DEP_2

12 DEP_3

11 DEP_3

12

Tabulu izveidošana

create table DARBINIEKI(DA_NUM number Primary key,DA_UZV varchar2(20),ALGA number(8,2),DE_NUM number,VADIT_NU number);

insert into DARBINIEKI values(1, 'Sakne', 900,1,NULL);insert into DARBINIEKI values(2, 'Zars', 800,1,1);insert into DARBINIEKI values(3, 'Stumrs', 800,1,1);insert into DARBINIEKI values(4, 'Sakne', 600,1,2);insert into DARBINIEKI values(5, 'Lapa', 600,1,2);insert into DARBINIEKI values(6, 'Ozols', 800,2,1);insert into DARBINIEKI values(7, 'Osis', 700,2,6);insert into DARBINIEKI values(8, 'Alksnis', 700,2,6);insert into DARBINIEKI values(9, 'Egle', 700,2,8);insert into DARBINIEKI values(10, 'Priede', 800,3,1);insert into DARBINIEKI values(11, 'Virsis', 700,3,10);insert into DARBINIEKI values(12, 'Persiks', 600,3,11);

create table DEPARTAMENTI(DE_NUM number Primary key,DE_NOS varchar2(20));

insert into DEPARTAMENTI values(1, 'DEP1');insert into DEPARTAMENTI values(2, 'DEP2');insert into DEPARTAMENTI values(3, 'DEP3');

13

WITH ATSK_1 (NUM, UZV, VADIT, ATS_LIM) AS (select DA_NUM, DA_UZV, VADIT_NU, 0 ATS_LIM from DARBINIEKI where DA_NUM = 1 UNION ALL select e.DA_NUM, e.DA_UZV, e.VADIT_NU, ATS_LIM+1 from ATSK_1 r, DARBINIEKI e where r.NUM = e.VADIT_NU )

select NUM, UZV, VADIT, ATS_LIMfrom ATSK_1order by ATS_LIM, NUM;

14

Noteikt darbiniekus, kuri tieši vai netieši sniedz pārskatus 1 darbiniekam, viņu atskaišu līmeni un vadītāju ķēdi.

with ATSK_1 (NUM, UZV, VADIT, ATS_LIM, VAD_SAR) AS (select DA_NUM, DA_UZV, VADIT_NU, 0 ATS_LIM, CAST(VADIT_NU as varchar2(200)) from DARBINIEKI where DA_NUM= 1 UNION ALL select e.DA_NUM, e.DA_UZV, e.VADIT_NU, ATS_LIM+1, CAST(VAD_SAR || ',' || VADIT_NU as varchar2(200)) FROM ATSK_1 r, DARBINIEKI e WHERE r.NUM = e.VADIT_NU )

select NUM, UZV, VADIT, ATS_LIM, VAD_SARfrom ATSK_1order by ATS_LIM, NUM;

15

Noteikt darbiniekus, kuri tieši vai netieši sniedz pārskatus 1 darbiniekam un atrodas tiešā pakļautībā (1 līmenis)

with ATSK_1 (NUM, UZV, VADIT, ATS_LIM, VAD_SAR) AS (select DA_NUM, DA_UZV, VADIT_NU, 0 ATS_LIM, CAST(VADIT_NU as varchar2(200)) from DARBINIEKI where DA_NUM= 1 UNION ALL select e.DA_NUM, e.DA_UZV, e.VADIT_NU, ATS_LIM+1, CAST(VAD_SAR || ',' || VADIT_NU as varchar2(200)) FROM ATSK_1 r, DARBINIEKI e WHERE r.NUM = e.VADIT_NU )select NUM, UZV, VADIT, ATS_LIM, VAD_SARfrom ATSK_1where ATS_LIM <=1order by ATS_LIM, NUM;

16

Grafiska pakārtotības shemas izvade

with POZ_IZV (NUM, UZV, VADIT, ATS_LIM, ALGA) as (select DA_NUM, DA_UZV, VADIT_NU, 0 ATS_LIM, ALGA from DARBINIEKI where VADIT_NU is null UNION ALL SELECT e.DA_NUM, e.DA_UZV, e.VADIT_NU, r.ATS_LIM+1 ATS_LIM, e.ALGA from POZ_IZV r, DARBINIEKI e WHERE r.NUM = e.VADIT_NU) SEARCH DEPTH FIRST BY UZV SET order1select LPAD(' ',2*ATS_LIM)||UZV uzvārds, NUM, VADIT, ALGAfrom POZ_IZVorder by order1;

17

The following statement shows the entire organization, indenting for each level of management, with each level ordered by hire_date. The value of is_cycle is set to Y for any employee who has the same hire_date as any manager above him in the management chain.

WITH dup_hiredate (eid, emp_last, mgr_id, reportLevel, hire_date, job_id) AS (SELECT employee_id, last_name, manager_id, 0 reportLevel, hire_date, job_id FROM employees WHERE manager_id is null UNION ALL SELECT e.employee_id, e.last_name, e.manager_id, r.reportLevel+1 reportLevel, e.hire_date, e.job_id FROM dup_hiredate r, employees e WHERE r.eid = e.manager_id) SEARCH DEPTH FIRST BY hire_date SET order1 CYCLE hire_date SET is_cycle TO 'Y' DEFAULT 'N'SELECT lpad(' ',2*reportLevel)||emp_last emp_name, eid, mgr_id, hire_date, job_id, is_cycleFROM dup_hiredateORDER BY order1;

EMP_NAME EID MGR_ID HIRE_DATE JOB_ID IS_CYCLE---------------------------------------------------------------------------------------King 100 17-JUN-03 AD_PRES N De Haan 102 100 13-JAN-01 AD_VP N Hunold 103 102 03-JAN-06 IT_PROG N Austin 105 103 25-JUN-05 IT_PROG N Kochhar 101 100 21-SEP-05 AD_VP N Mavris 203 101 07-JUN-02 HR_REP N Baer 204 101 07-JUN-02 PR_REP N Higgins 205 101 07-JUN-02 AC_MGR N Gietz 206 205 07-JUN-02 AC_ACCOUNT Y Greenberg 108 101 17-AUG-02 FI_MGR N Faviet 109 108 16-AUG-02 FI_ACCOUNT N Chen 110 108 8-SEP-05 FI_ACCOUNT N. . .

18

Recursive WITH clauses

create table TAB_1 (ID number,VECAKA_ID number,constraint TAB_1_PA primary key(ID),constraint TAB_1_AA foreign key(VECAKA_ID) references TAB_1(ID));

create index TAB_1_IND on TAB_1(VECAKA_ID);

begininsert into TAB_1 values (1, NULL);insert into TAB_1 values (2, 1);insert into TAB_1 values (3, 2);insert into TAB_1 values (4, 2);insert into TAB_1 values (5, 4);insert into TAB_1 values (6, 4);insert into TAB_1 values (7, 1);insert into TAB_1 values (8, 7);insert into TAB_1 values (9, 1);insert into TAB_1 values (10, 9);insert into TAB_1 values (11, 10);insert into TAB_1 values (12, 9);end;

19

with T1(ID, VECAKA_ID) as ( -- Sākuma (enkura) loceklis. select ID, VECAKA_ID from TAB_1 where VECAKA_ID is null union all -- Rekursīvs loceklis. select t2.ID, t2.VECAKA_ID from TAB_1 t2, T1 where t2.VECAKA_ID = T1.ID)

search breadth first by ID set KARTIBA1select ID, VECAKA_IDfrom T1order by KARTIBA1;

The ordering of the rows is specified using the SEARCH clause, which can use two methods.

1) BREADTH FIRST BY : sibling rows are returned before child rows are processed.

2) DEPTH FIRST BY : child rows are returned before siblings are processed.

T1.ID = 1 nullt2.ID = 2 t2.VECAKA_ID =1t2.ID = 7 t2.VECAKA_ID =1t2.ID = 9 t2.VECAKA_ID =1

t2.ID = 3 t2.VECAKA_ID =2t2.ID = 4 t2.VECAKA_ID =2t2.ID = 8 t2.VECAKA_ID =7t2.ID = 10 t2.VECAKA_ID =9t2.ID = 12 t2.VECAKA_ID =9

t2.ID = 5 t2.VECAKA_ID =4t2.ID = 6 t2.VECAKA_ID =4t2.ID = 11 t2.VECAKA_ID =10

ID PARENT_ID----------------------- 1 2 1 7 1 9 1 3 2 4 2 8 7 10 9 12 9 5 4 6 4 11 10

ID VECAKA_ID---------------------- 1 2 1 3 2 4 2 5 4 6 4 7 1 8 7 9 1 10 9 11 10 12 9

20

The following statement counts the number of employees under each manager

WITH emp_count (eid, emp_last, mgr_id, mgrLevel, salary, cnt_employees) AS (SELECT employee_id, last_name, manager_id, 0 mgrLevel, salary, 0 cnt_employees FROM employees UNION ALL SELECT e.employee_id, e.last_name, e.manager_id, r.mgrLevel+1 mgrLevel, e.salary, 1 cnt_employees FROM emp_count r, employees e WHERE e.employee_id = r.mgr_id ) SEARCH DEPTH FIRST BY emp_last SET order1SELECT emp_last, eid, mgr_id, salary, sum(cnt_employees), max(mgrLevel) mgrLevelFROM emp_countGROUP BY emp_last, eid, mgr_id, salaryHAVING max(mgrLevel) > 0ORDER BY mgr_id NULLS FIRST, emp_last;

EMP_LAST EID MGR_ID SALARY SUM(CNT_EMPLOYEES) MGR LEVEL-------------------------------------------------------------------------------------King 100 24000 106 3Cambrault 148 100 11000 7 2De Haan 102 100 17000 5 2Errazuriz 147 100 12000 6 1Fripp 121 100 8200 8 1Hartstein 201 100 13000 1 1Kaufling 122 100 7900 8 1. . .

21