Select 섹션의 내용이 2챕터의 분량만큼, insert와 update에 대한 내용이 조금 남았다. 이번 3챕터에서 SELECT에 대한 내용을 마무리하고, 4챕터에서 insert와 update의 내용을 다루는 것으로 PyPika 튜토리얼 번역을 마무리하겠다. 문서 자체가 내용이 풍부한 것이 아니어서 얘기할만한 게 조금 더 있을 법 한데, 애초에 난 ORM을 많이 써서 PyPika를 쓸 일이 거의 없기에 짬에서 나오는 바이브가 없는지라 나~중에 얘기해 보자(영원히 안 할수도 있다 ㅎㅎ).

Selecting Data 나머지 내용

Unions

UNIONUNION ALL 쿼리도 지원된다. UNION DISTINCTUNION과 동일한 의미이므로 PyPika는 두 기능을 분리해서 제공하지 않는다. UNION은 쿼리에 SELECT절의 수가 동일해야 하며, 컬럼 갯수가 일치하지 않으면 UnionException을 통해 유니온된 쿼리를 문자열로 캐스팅하려고 한다.

마지막 문장은 도대체 어떻게 번역을 해야 할런지.. 'Unions require that queries have the same number of SELECT clauses so trying to cast a unioned query to string with through a UnionException if the column sizes are mismatched.'

UNION 쿼리를 생성하려면, Query.union() 메소드나 + 연산자를 통해 두 쿼리 객체를 연결해주면 된다. UNION ALLQuery.union_all()이나 * 연산자를 사용한다.

provider_a, provider_b = Tables('provider_a', 'provider_b')
q = Query.from_(provider_a).select(
    provider_a.created_time, provider_a.foo, provider_a.bar
) + Query.from_(provider_b).select(
    provider_b.created_time, provider_b.fiz, provider_b.buz
)
SELECT "created_time","foo","bar" FROM "provider_a" UNION
SELECT "created_time","fiz","buz" FROM "provider_b"

Date, Time, and Intervals

pypika.Interval을 사용하면 날짜 계산과 관련된 쿼리를 수행할 수 있다.

from pypika import Interval
from pypika import functions as fn

fruits = Table('fruits')
q = Query.from_(fruits).select(
  fruits.id, fruits.name
).where(
  fruits.harvest_date + Interval(months=1) < fn.Now()
)
SELECT id,name FROM fruits WHERE harvest_date+INTERVAL 1 MONTH<NOW()

Tuples

pypika.Tuple을 통해 튜플도 지원된다. 이렇게 만들어진 Tuple들은 pypika.Criterion으로서 WHERE 절에 사용할 수 있다.

from pypika import Query, Tuple

q = Query.from_(table_abc).select(
  table_abc.foo, table_abc.bar
).where(
  Tuple(table_abc.foo, table_abc.bar) == Tuple(1, 2)
)
SELECT "foo","bar" FROM "abc" WHERE ("foo","bar")=(1,2)

pypika.Tuple을 양 쪽 모두에 사용하는 것은 불필요하기 때문에, PyPika는 그냥 Python의 tuple을 사용할 수 있게도 해준다.

from pypika import Query, Tuple

q = Query.from_(table_abc).select(
  table_abc.foo, table_abc.bar
).where(
  Tuple(table_abc.foo, table_abc.bar) == (1, 2)
)
SELECT "foo","bar" FROM "abc" WHERE ("foo","bar")=(1,2)

IN 절처럼 실제로 SQL에서 Tuple을 쓸 수 있는 모든 곳에서 활용 가능하다.

Query.from_(table_abc).select(
  table_abc.foo, table_abc.bar
).where(
  Tuple(table_abc.foo, table_abc.bar).isin([(1, 1), (2, 2), (3, 3)])
)
SELECT "foo","bar" FROM "abc" WHERE ("foo","bar") IN ((1,1),(2,2),(3,3))

String Functions

문자열 연산과 관련된 함수들이 pypika.functions 패키지에 여럿 포함되어 있다.

from pypika import functions as fn

customers = Tables('customers')
q = Query.from_(customers).select(
    customers.id,
    customers.fname,
    customers.lname,
).where(
    customers.lname.like('Mc%')
)
SELECT id,fname,lname FROM customers WHERE lname LIKE 'Mc%'
from pypika import functions as fn

customers = Tables('customers')
q = Query.from_(customers).select(
    customers.id,
    customers.fname,
    customers.lname,
).where(
    customers.lname.regex(r'^[abc][a-zA-Z]+&')
)
SELECT id,fname,lname FROM customers WHERE lname REGEX '^[abc][a-zA-Z]+&';
from pypika import functions as fn

customers = Tables('customers')
q = Query.from_(customers).select(
    customers.id,
    fn.Concat(customers.fname, ' ', customers.lname).as_('full_name'),
)
SELECT id,CONCAT(fname, ' ', lname) full_name FROM customers

Case Statements

Case 문을 사용하면 삼항 연산자와 같이 여러 조건을 검사하여 해당 조건들에 대한 값을 반환하거나 기본값을 반환할 수 있다. PyPika에서는 Case 객체를 통해 이를 표현하고, when 메소드를 체이닝하면서 각 조건들을 명시한다.

from pypika import Case, functions as fn

customers = Tables('customers')
q = Query.from_(customers).select(
    customers.id,
    Case()
       .when(customers.fname == "Tom", "It was Tom")
       .when(customers.fname == "John", "It was John")
       .else_("It was someone else.").as_('who_was_it')
)
SELECT 
  "id",
  CASE
    WHEN "fname"='Tom' THEN 'It was Tom'
    WHEN "fname"='John' THEN 'It was John'
    ELSE 'It was someone else.'
    END "who_was_it"
FROM "customers"