|
| 1 | +# SubQuery Support |
| 2 | +The library currently supports subQueries in the following areas: |
| 3 | + |
| 4 | +1. In certain where conditions |
| 5 | +1. In certain insert statements |
| 6 | +1. In the "from" clause of a select statement |
| 7 | + |
| 8 | +## SubQueries in Where Conditions |
| 9 | +The library support subQueries in the following where conditions: |
| 10 | + |
| 11 | +- isEqualTo |
| 12 | +- isNotEqualTo |
| 13 | +- isIn |
| 14 | +- isNotIn |
| 15 | +- isGreaterThan |
| 16 | +- isGreaterThanOrEqualTo |
| 17 | +- isLessThan |
| 18 | +- isLessThanOrEqualTo |
| 19 | + |
| 20 | +A Java example is as follows: |
| 21 | + |
| 22 | +```java |
| 23 | +SelectStatementProvider selectStatement = select(id, animalName, bodyWeight, brainWeight) |
| 24 | + .from(animalData) |
| 25 | + .where(brainWeight, isEqualTo( |
| 26 | + select(min(brainWeight)) |
| 27 | + .from(animalData) |
| 28 | + ) |
| 29 | + ) |
| 30 | + .orderBy(animalName) |
| 31 | + .build() |
| 32 | + .render(RenderingStrategies.MYBATIS3); |
| 33 | +``` |
| 34 | + |
| 35 | +### Kotlin Support |
| 36 | +The library includes Kotlin versions of the where conditions that allow use of the Kotlin subQuery builder. The Kotlin |
| 37 | +where conditions are in the `org.mybatis.dynamic.sql.util.kotlin` package. An example is as follows: |
| 38 | + |
| 39 | +```kotlin |
| 40 | +val selectStatement = select(id, firstName, lastName, birthDate, employed, occupation, addressId) { |
| 41 | + from(Person) |
| 42 | + where(id, isEqualTo { |
| 43 | + select(max(id)) { |
| 44 | + from(Person) |
| 45 | + } |
| 46 | + }) |
| 47 | +} |
| 48 | +``` |
| 49 | + |
| 50 | +## SubQueries in Insert Statements |
| 51 | +The library supports an INSERT statement that retrieves values from a SELECT statement. For example: |
| 52 | + |
| 53 | +```java |
| 54 | +InsertSelectStatementProvider insertSelectStatement = insertInto(animalDataCopy) |
| 55 | + .withColumnList(id, animalName, bodyWeight, brainWeight) |
| 56 | + .withSelectStatement( |
| 57 | + select(id, animalName, bodyWeight, brainWeight) |
| 58 | + .from(animalData) |
| 59 | + .where(id, isLessThan(22)) |
| 60 | + ) |
| 61 | + .build() |
| 62 | + .render(RenderingStrategies.MYBATIS3); |
| 63 | +``` |
| 64 | + |
| 65 | +### Kotlin Support |
| 66 | + |
| 67 | +The library includes a Kotlin builder for subQueries in insert statements that integrates with the select DSL. You |
| 68 | +can write inserts like this: |
| 69 | + |
| 70 | +```kotlin |
| 71 | +val insertStatement = insertSelect(Person) { |
| 72 | + columns(id, firstName, lastName, birthDate, employed, occupation, addressId) |
| 73 | + select(add(id, constant<Int>("100")), firstName, lastName, birthDate, employed, occupation, addressId) { |
| 74 | + from(Person) |
| 75 | + orderBy(id) |
| 76 | + } |
| 77 | +} |
| 78 | +``` |
| 79 | + |
| 80 | +## SubQueries in a From Clause |
| 81 | + |
| 82 | +The library supports subQueries in from clauses and the syntax is a natural extension of the |
| 83 | +select DSL. An example is as follows: |
| 84 | + |
| 85 | +```java |
| 86 | +DerivedColumn<Integer> rowNum = DerivedColumn.of("rownum()"); |
| 87 | + |
| 88 | +SelectStatementProvider selectStatement = |
| 89 | + select(animalName, rowNum) |
| 90 | + .from( |
| 91 | + select(id, animalName) |
| 92 | + .from(animalData) |
| 93 | + .where(id, isLessThan(22)) |
| 94 | + .orderBy(animalName.descending()) |
| 95 | + ) |
| 96 | + .where(rowNum, isLessThan(5)) |
| 97 | + .and(animalName, isLike("%a%")) |
| 98 | + .build() |
| 99 | + .render(RenderingStrategies.MYBATIS3); |
| 100 | +``` |
| 101 | + |
| 102 | +Notice the use of a `DerivedColumn` to easily specify a function like `rownum()` that can be |
| 103 | +used both in the select list and in a where condition. |
| 104 | + |
| 105 | +### Table Qualifiers with SubQueries |
| 106 | + |
| 107 | +The library attempts to automatically calculate table qualifiers. If a table qualifier is specified, |
| 108 | +the library will automatically render the table qualifier on all columns associated with the |
| 109 | +table. For example with the following query: |
| 110 | + |
| 111 | +```java |
| 112 | +SelectStatementProvider selectStatement = |
| 113 | + select(id, animalName) |
| 114 | + .from(animalData, "ad") |
| 115 | + .build() |
| 116 | + .render(RenderingStrategies.MYBATIS3); |
| 117 | +``` |
| 118 | + |
| 119 | +The library will render SQL as: |
| 120 | + |
| 121 | +```sql |
| 122 | +select ad.id, ad.animal_name |
| 123 | +from AnimalData ad |
| 124 | +``` |
| 125 | + |
| 126 | +Notice that the table qualifier `ad` is automatically applied to columns in the select list. |
| 127 | + |
| 128 | +In the case of join queries the table qualifier specified, or if not specified the table name |
| 129 | +itself, will be used as the table qualifier. |
| 130 | + |
| 131 | +With subQueries, it is important to understand the limits of automatic table qualifiers. The rules are |
| 132 | +as follows: |
| 133 | + |
| 134 | +1. The scope of automatic table qualifiers is limited to a single select statement. For subQueries, the outer |
| 135 | + query has a different scope than the subQuery. |
| 136 | +1. A qualifier can be applied to a subQuery as a whole, but that qualifier is not automatically applied to |
| 137 | + any column |
| 138 | + |
| 139 | +As an example, consider the following query: |
| 140 | + |
| 141 | +```java |
| 142 | +DerivedColumn<Integer> rowNum = DerivedColumn.of("rownum()"); |
| 143 | + |
| 144 | +SelectStatementProvider selectStatement = |
| 145 | + select(animalName, rowNum) |
| 146 | + .from( |
| 147 | + select(id, animalName) |
| 148 | + .from(animalData, "a") |
| 149 | + .where(id, isLessThan(22)) |
| 150 | + .orderBy(animalName.descending()), |
| 151 | + "b" |
| 152 | + ) |
| 153 | + .where(rowNum, isLessThan(5)) |
| 154 | + .and(animalName, isLike("%a%")) |
| 155 | + .build() |
| 156 | + .render(RenderingStrategies.MYBATIS3); |
| 157 | +``` |
| 158 | + |
| 159 | +The rendered SQL will be as follows: |
| 160 | + |
| 161 | +```sql |
| 162 | +select animal_name, rownum() |
| 163 | +from (select a.id, a.animal_name |
| 164 | + from AnimalDate a |
| 165 | + where id < #{parameters.p1} |
| 166 | + order by animal_name desc) b |
| 167 | +where rownum() < #{parameters.p2} |
| 168 | + and animal_name like #{parameters.p3} |
| 169 | +``` |
| 170 | + |
| 171 | +Notice that the qualifier `a` is automatically applied to columns in the subQuery and that the |
| 172 | +qualifier `b` is not applied anywhere. |
| 173 | + |
| 174 | +If your query requires the subQuery qualifier to be applied to columns in the outer select list, |
| 175 | +you can manually apply the qualifier to columns as follows: |
| 176 | + |
| 177 | +```java |
| 178 | +DerivedColumn<Integer> rowNum = DerivedColumn.of("rownum()"); |
| 179 | + |
| 180 | +SelectStatementProvider selectStatement = |
| 181 | + select(animalName.qualifiedWith("b"), rowNum) |
| 182 | + .from( |
| 183 | + select(id, animalName) |
| 184 | + .from(animalData, "a") |
| 185 | + .where(id, isLessThan(22)) |
| 186 | + .orderBy(animalName.descending()), |
| 187 | + "b" |
| 188 | + ) |
| 189 | + .where(rowNum, isLessThan(5)) |
| 190 | + .and(animalName.qualifiedWith("b"), isLike("%a%")) |
| 191 | + .build() |
| 192 | + .render(RenderingStrategies.MYBATIS3); |
| 193 | +``` |
| 194 | + |
| 195 | +In this case, we have manually applied the qualifier `b` to columns in the outer query. The |
| 196 | +rendered SQL looks like this: |
| 197 | + |
| 198 | +```sql |
| 199 | +select b.animal_name, rownum() |
| 200 | +from (select a.id, a.animal_name |
| 201 | + from AnimalDate a |
| 202 | + where id < #{parameters.p1} |
| 203 | + order by animal_name desc) b |
| 204 | +where rownum() < #{parameters.p2} |
| 205 | + and b.animal_name like #{parameters.p3} |
| 206 | +``` |
| 207 | + |
| 208 | +### Kotlin Support |
| 209 | + |
| 210 | +The library includes a Kotlin builder for subQueries that integrates with the select DSL. You |
| 211 | +can write queries like this: |
| 212 | + |
| 213 | +```kotlin |
| 214 | +val selectStatement = |
| 215 | + select(firstName, rowNum) { |
| 216 | + from { |
| 217 | + select(id, firstName) { |
| 218 | + from(Person) |
| 219 | + where(id, isLessThan(22)) |
| 220 | + orderBy(firstName.descending()) |
| 221 | + } |
| 222 | + } |
| 223 | + where(rowNum, isLessThan(5)) |
| 224 | + and(firstName, isLike("%a%")) |
| 225 | + } |
| 226 | +``` |
| 227 | + |
| 228 | +The same rules about table qualifiers apply as stated above. In Kotlin, a subQuery qualifier |
| 229 | +can be added with the overloaded "+" operator as shown below: |
| 230 | + |
| 231 | +```kotlin |
| 232 | +val selectStatement = |
| 233 | + select(firstName, rowNum) { |
| 234 | + from { |
| 235 | + select(id, firstName) { |
| 236 | + from(Person, "a") |
| 237 | + where(id, isLessThan(22)) |
| 238 | + orderBy(firstName.descending()) |
| 239 | + } |
| 240 | + + "b" |
| 241 | + } |
| 242 | + where(rowNum, isLessThan(5)) |
| 243 | + and(firstName, isLike("%a%")) |
| 244 | + } |
| 245 | +``` |
| 246 | + |
| 247 | +In this case the `a` qualifier is used in the context of the inner select statement and |
| 248 | +the `b` qualifier is applied to the subQuery as a whole. |
0 commit comments