Oracle의 옛날 조인 문법(= “오라클 전용 OUTER JOIN 문법”) 에서 (+)를 사용하는 경우,
AND 절의 모든 조건이 "조인 대상 테이블" 기준으로 일관되게 처리되어야 하기 때문입니다.
🔹배경 설명
예를 들어 아래와 같은 쿼리가 있다고 합시다:
SELECT a.user_id, b.system_cd
FROM user_info a, system_info b
WHERE a.user_id = b.user_id(+)
AND b.system_cd(+) = 'COM';
🔹 (+)의 의미
(+)는 해당 테이블이 OUTER JOIN의 “부수적인 테이블” 이라는 뜻입니다.
즉, b 테이블이 없어도 a의 데이터는 모두 나오게 하라는 의미죠.
🔹 그런데 왜 상수 조건에도 (+)가 붙을까?
Oracle의 구식 (+) 문법에서는,
조인 조건뿐 아니라 그 조인에 포함된 모든 조건에서
조인 방향(어느 쪽이 outer냐)을 일관되게 표시해야 합니다.
그렇지 않으면 Oracle이 이렇게 이해합니다:
“b.system_cd = 'COM' 조건은 조인 후 필터 조건으로 봐야겠군.
즉, b가 없으면 NULL인데, NULL = 'COM'은 false니까
결국 outer join이 무의미해지네.”
즉, (+)를 빼면 사실상 inner join처럼 동작하게 되어
조인 결과에서 NULL 행(즉, 매칭 안 된 a의 데이터)이 사라집니다.
🔹 비교 예시
(1) (+) 안 붙은 경우 → inner join처럼 됨
SELECT a.user_id, b.system_cd
FROM user_info a, system_info b
WHERE a.user_id = b.user_id(+)
AND b.system_cd = 'COM';
→ b.system_cd = 'COM'은 매칭된 행만 남기므로,
b가 없는 a 행은 제외됨 → 실질적으로 inner join
(2) (+) 붙은 경우 → 진짜 outer join 유지
SELECT a.user_id, b.system_cd
FROM user_info a, system_info b
WHERE a.user_id = b.user_id(+)
AND b.system_cd(+) = 'COM';
→ b가 없어도 a의 행은 유지됨
(이때 b.system_cd는 NULL로 표시됨)
🔹 결론
AND b.system_cd(+) = 'COM' 처럼 상수 비교에도 (+)를 붙이는 이유는:
해당 조건이 outer join의 한쪽 테이블(b)에 속하는 조건임을 명시하여,
outer join의 의미(= NULL 허용)를 유지하기 위해서입니다.
✅ 참고 (추천 방식)
이 구식 (+) 문법은 이제 표준 SQL JOIN 문법으로 대체하는 것이 좋습니다:
SELECT a.user_id, b.system_cd
FROM user_info a
LEFT JOIN system_info b
ON a.user_id = b.user_id
AND b.system_cd = 'COM';
이 방식에서는 b.system_cd = 'COM' 조건이 자연스럽게
조인 조건(on절) 에 포함되므로, (+)를 붙일 필요가 없습니다.
✅ 오라클 방식의 Join에서 상수값 비교에 (+)를 하는 경우
Employees 테이블 전체 행 (PK: EMPLOYEE_ID, FK: DEPARTMENT_ID)
- 88번 row는 department_id가 null
| NO | EMPLOYEE_ID | FIRST_NAME | LAST_NAME | DEPARTMENT_ID | |
| 1 | 198 | Donald | OConnell | DOCONNEL | 50 |
| 2 | 199 | Douglas | Grant | DGRANT | 50 |
| 3 | 200 | Jennifer | Whalen | JWHALEN | 10 |
| 4 | 201 | Michael | Hartstein | MHARTSTE | 20 |
| 5 | 202 | Pat | Fay | PFAY | 20 |
| 6 | 203 | Susan | Mavris | SMAVRIS | 40 |
| 7 | 204 | Hermann | Baer | HBAER | 70 |
| 8 | 205 | Shelley | Higgins | SHIGGINS | 110 |
| 9 | 206 | William | Gietz | WGIETZ | 110 |
| 10 | 100 | Steven | King | SKING | 90 |
| 11 | 101 | Neena | Kochhar | NKOCHHAR | 90 |
| 12 | 102 | Lex | De Haan | LDEHAAN | 90 |
| 13 | 103 | Alexander | Hunold | AHUNOLD | 60 |
| 14 | 104 | Bruce | Ernst | BERNST | 60 |
| 15 | 105 | David | Austin | DAUSTIN | 60 |
| 16 | 106 | Valli | Pataballa | VPATABAL | 60 |
| 17 | 107 | Diana | Lorentz | DLORENTZ | 60 |
| 18 | 108 | Nancy | Greenberg | NGREENBE | 100 |
| 19 | 109 | Daniel | Faviet | DFAVIET | 100 |
| 20 | 110 | John | Chen | JCHEN | 100 |
| 21 | 111 | Ismael | Sciarra | ISCIARRA | 100 |
| 22 | 112 | Jose Manuel | Urman | JMURMAN | 100 |
| 23 | 113 | Luis | Popp | LPOPP | 100 |
| 24 | 114 | Den | Raphaely | DRAPHEAL | 30 |
| 25 | 115 | Alexander | Khoo | AKHOO | 30 |
| 26 | 116 | Shelli | Baida | SBAIDA | 30 |
| 27 | 117 | Sigal | Tobias | STOBIAS | 30 |
| 28 | 118 | Guy | Himuro | GHIMURO | 30 |
| 29 | 119 | Karen | Colmenares | KCOLMENA | 30 |
| 30 | 120 | Matthew | Weiss | MWEISS | 50 |
| 31 | 121 | Adam | Fripp | AFRIPP | 50 |
| 32 | 122 | Payam | Kaufling | PKAUFLIN | 50 |
| 33 | 123 | Shanta | Vollman | SVOLLMAN | 50 |
| 34 | 124 | Kevin | Mourgos | KMOURGOS | 50 |
| 35 | 125 | Julia | Nayer | JNAYER | 50 |
| 36 | 126 | Irene | Mikkilineni | IMIKKILI | 50 |
| 37 | 127 | James | Landry | JLANDRY | 50 |
| 38 | 128 | Steven | Markle | SMARKLE | 50 |
| 39 | 129 | Laura | Bissot | LBISSOT | 50 |
| 40 | 130 | Mozhe | Atkinson | MATKINSO | 50 |
| 41 | 131 | James | Marlow | JAMRLOW | 50 |
| 42 | 132 | TJ | Olson | TJOLSON | 50 |
| 43 | 133 | Jason | Mallin | JMALLIN | 50 |
| 44 | 134 | Michael | Rogers | MROGERS | 50 |
| 45 | 135 | Ki | Gee | KGEE | 50 |
| 46 | 136 | Hazel | Philtanker | HPHILTAN | 50 |
| 47 | 137 | Renske | Ladwig | RLADWIG | 50 |
| 48 | 138 | Stephen | Stiles | SSTILES | 50 |
| 49 | 139 | John | Seo | JSEO | 50 |
| 50 | 140 | Joshua | Patel | JPATEL | 50 |
| 51 | 141 | Trenna | Rajs | TRAJS | 50 |
| 52 | 142 | Curtis | Davies | CDAVIES | 50 |
| 53 | 143 | Randall | Matos | RMATOS | 50 |
| 54 | 144 | Peter | Vargas | PVARGAS | 50 |
| 55 | 145 | John | Russell | JRUSSEL | 80 |
| 56 | 146 | Karen | Partners | KPARTNER | 80 |
| 57 | 147 | Alberto | Errazuriz | AERRAZUR | 80 |
| 58 | 148 | Gerald | Cambrault | GCAMBRAU | 80 |
| 59 | 149 | Eleni | Zlotkey | EZLOTKEY | 80 |
| 60 | 150 | Peter | Tucker | PTUCKER | 80 |
| 61 | 151 | David | Bernstein | DBERNSTE | 80 |
| 62 | 152 | Peter | Hall | PHALL | 80 |
| 63 | 153 | Christopher | Olsen | COLSEN | 80 |
| 64 | 154 | Nanette | Cambrault | NCAMBRAU | 80 |
| 65 | 155 | Oliver | Tuvault | OTUVAULT | 80 |
| 66 | 156 | Janette | King | JKING | 80 |
| 67 | 157 | Patrick | Sully | PSULLY | 80 |
| 68 | 158 | Allan | McEwen | AMCEWEN | 80 |
| 69 | 159 | Lindsey | Smith | LSMITH | 80 |
| 70 | 160 | Louise | Doran | LDORAN | 80 |
| 71 | 161 | Sarath | Sewall | SSEWALL | 80 |
| 72 | 162 | Clara | Vishney | CVISHNEY | 80 |
| 73 | 163 | Danielle | Greene | DGREENE | 80 |
| 74 | 164 | Mattea | Marvins | MMARVINS | 80 |
| 75 | 165 | David | Lee | DLEE | 80 |
| 76 | 166 | Sundar | Ande | SANDE | 80 |
| 77 | 167 | Amit | Banda | ABANDA | 80 |
| 78 | 168 | Lisa | Ozer | LOZER | 80 |
| 79 | 169 | Harrison | Bloom | HBLOOM | 80 |
| 80 | 170 | Tayler | Fox | TFOX | 80 |
| 81 | 171 | William | Smith | WSMITH | 80 |
| 82 | 172 | Elizabeth | Bates | EBATES | 80 |
| 83 | 173 | Sundita | Kumar | SKUMAR | 80 |
| 84 | 174 | Ellen | Abel | EABEL | 80 |
| 85 | 175 | Alyssa | Hutton | AHUTTON | 80 |
| 86 | 176 | Jonathon | Taylor | JTAYLOR | 80 |
| 87 | 177 | Jack | Livingston | JLIVINGS | 80 |
| 88 | 178 | Kimberely | Grant | KGRANT | |
| 89 | 179 | Charles | Johnson | CJOHNSON | 80 |
| 90 | 180 | Winston | Taylor | WTAYLOR | 50 |
| 91 | 181 | Jean | Fleaur | JFLEAUR | 50 |
| 92 | 182 | Martha | Sullivan | MSULLIVA | 50 |
| 93 | 183 | Girard | Geoni | GGEONI | 50 |
| 94 | 184 | Nandita | Sarchand | NSARCHAN | 50 |
| 95 | 185 | Alexis | Bull | ABULL | 50 |
| 96 | 186 | Julia | Dellinger | JDELLING | 50 |
| 97 | 187 | Anthony | Cabrio | ACABRIO | 50 |
| 98 | 188 | Kelly | Chung | KCHUNG | 50 |
| 99 | 189 | Jennifer | Dilly | JDILLY | 50 |
| 100 | 190 | Timothy | Gates | TGATES | 50 |
| 101 | 191 | Randall | Perkins | RPERKINS | 50 |
| 102 | 192 | Sarah | Bell | SBELL | 50 |
| 103 | 193 | Britney | Everett | BEVERETT | 50 |
| 104 | 194 | Samuel | McCain | SMCCAIN | 50 |
| 105 | 195 | Vance | Jones | VJONES | 50 |
| 106 | 196 | Alana | Walsh | AWALSH | 50 |
| 107 | 197 | Kevin | Feeney | KFEENEY | 50 |
Departments 테이블 전체 행(PK: DEPARTMENT_ID)
| NO | DEPARTMENT_ID | DEPARTMENT_NAME | MANAGER_ID | LOCATION_ID |
| 1 | 10 | Administration | 200 | 1700 |
| 2 | 20 | Marketing | 201 | 1800 |
| 3 | 30 | Purchasing | 114 | 1700 |
| 4 | 40 | Human Resources | 203 | 2400 |
| 5 | 50 | Shipping | 121 | 1500 |
| 6 | 60 | IT | 103 | 1400 |
| 7 | 70 | Public Relations | 204 | 2700 |
| 8 | 80 | Sales | 145 | 2500 |
| 9 | 90 | Executive | 100 | 1700 |
| 10 | 100 | Finance | 108 | 1700 |
| 11 | 110 | Accounting | 205 | 1700 |
| 12 | 120 | Treasury | 1700 | |
| 13 | 130 | Corporate Tax | 1700 | |
| 14 | 140 | Control And Credit | 1700 | |
| 15 | 150 | Shareholder Services | 1700 | |
| 16 | 160 | Benefits | 1700 | |
| 17 | 170 | Manufacturing | 1700 | |
| 18 | 180 | Construction | 1700 | |
| 19 | 190 | Contracting | 1700 | |
| 20 | 200 | Operations | 1700 | |
| 21 | 210 | IT Support | 1700 | |
| 22 | 220 | NOC | 1700 | |
| 23 | 230 | IT Helpdesk | 1700 | |
| 24 | 240 | Government Sales | 1700 | |
| 25 | 250 | Retail Sales | 1700 | |
| 26 | 260 | Recruiting | 1700 | |
| 27 | 270 | Payroll | 1700 |
아래와 같이 220을 비교하는 조건에도 (+) 기호가 들어가는 경우가 있다. 이경우는 Outer 조건을 일관성 있게 맞추기 위해서 수행해주는 조건으로 아래와 같이 Outer 조인시에 추가로 상수값을 비교하면 (+) 기호을 넣어준다. 전체적으로 Outer 가 유지 된다.
select *
from DEPARTMENTS D, EMPLOYEES E
where d.department_id = e.department_id (+)
and e.department_id(+) = 220
;
결과
- e.department_id가 동일하고 department_id가 220인 것은 없으므로 우측 Employee부분은 전부 공란이되지만 Outer Join은 유지되어서 좌측은 Department전체가 다 표출된다.
| No | DEPARTMENT_ID | DEPARTMENT_NAME | MANAGER_ID | LOCATION_ID | EMPLOYEE_ID | FIRST_NAME | LAST_NAME | PHONE_NUMBER | SALARY | |
| 1 | 10 | Administration | 200 | 1700 | ||||||
| 2 | 20 | Marketing | 201 | 1800 | ||||||
| 3 | 30 | Purchasing | 114 | 1700 | ||||||
| 4 | 40 | Human Resources | 203 | 2400 | ||||||
| 5 | 50 | Shipping | 121 | 1500 | ||||||
| 6 | 60 | IT | 103 | 1400 | ||||||
| 7 | 70 | Public Relations | 204 | 2700 | ||||||
| 8 | 80 | Sales | 145 | 2500 | ||||||
| 9 | 90 | Executive | 100 | 1700 | ||||||
| 10 | 100 | Finance | 108 | 1700 | ||||||
| 11 | 110 | Accounting | 205 | 1700 | ||||||
| 12 | 120 | Treasury | 1700 | |||||||
| 13 | 130 | Corporate Tax | 1700 | |||||||
| 14 | 140 | Control And Credit | 1700 | |||||||
| 15 | 150 | Shareholder Services | 1700 | |||||||
| 16 | 160 | Benefits | 1700 | |||||||
| 17 | 170 | Manufacturing | 1700 | |||||||
| 18 | 180 | Construction | 1700 | |||||||
| 19 | 190 | Contracting | 1700 | |||||||
| 20 | 200 | Operations | 1700 | |||||||
| 21 | 210 | IT Support | 1700 | |||||||
| 22 | 220 | NOC | 1700 | |||||||
| 23 | 230 | IT Helpdesk | 1700 | |||||||
| 24 | 240 | Government Sales | 1700 | |||||||
| 25 | 250 | Retail Sales | 1700 | |||||||
| 26 | 260 | Recruiting | 1700 | |||||||
| 27 | 270 | Payroll | 1700 |
만일 Oracle 표기법으로 상수값 비교 조건에 (+)를 빼면 전체 결과에서 e.department_id = 220 인것으로 판단을 해서 결과는 empty가 나오게 된다 (employee중에 220번 department_id를 가진 사람은 없으므로) 즉 Inner join처럼 동작하게 된다.
select *
from DEPARTMENTS D, EMPLOYEES E
where d.department_id = e.department_id (+)
and e.department_id = 220
;
-- The End --
'Database > Oracle' 카테고리의 다른 글
| [Oracle] SQL Performance - View 사용 주의 (0) | 2025.11.06 |
|---|---|
| [Oracle] Oracle Join 종류 정리 #2 (0) | 2025.11.05 |
| [Oracle] Oracle Join 종류 정리 (0) | 2025.11.05 |
| [DB] Oracle Docker 이미지로 CDB, PDB 기반 설치하기 (0) | 2025.11.03 |
| [Oracle] 오라클 기본 명령어 #1 (0) | 2022.03.08 |