Skip to content

Latest commit

 

History

History
156 lines (122 loc) · 4.86 KB

advanced-query-cn.md

File metadata and controls

156 lines (122 loc) · 4.86 KB

高级查询

我们已经学习了基础查询和在条件查询中使用 SQL 函数。现在我们来学习一些更高级的查询技巧。

Union

UNION 操作符用于合并两个查询结果相同的 SELECT 语句。

在 SQL 中,UNION 操作符位于两个 SELECT 语句中间,但是在 SQLlin 中,我们使用一个高阶函数来实现 UNION

fun sample() {
    lateinit var selectStatement: SelectStatement<Person>
    database {
        PersonTable { table ->
            selectStatement = UNION {
                table SELECT WHERE (age GTE 5)
                table SELECT WHERE (length(name) LTE 8)
            }
        }
    }
}

你只需要将你的 SELECT 语句写在 UNION {...} 块内部。 UNION {...} 块内部至少要有两个 SELECT 语句,否则你将会在运行时得到一个 IllegalStateException 异常。

如果你想要交替使用 UNIONUNION ALL ,请使用 UNION {...}UNION_ALL {...} 块嵌套:

fun sample() {
    lateinit var selectStatement: SelectStatement<Person>
    database {
        PersonTable { table ->
            selectStatement = UNION {
                table SELECT WHERE (age GTE 5)
                UNION_ALL {
                    table SELECT WHERE (length(name) LTE 8)
                    table SELECT WHERE (name EQ "Tom")
                }
            }
        }
    }
}

前面的代码等价于:

SELECT * FROM person WHERE age >= 5
UNION
SELECT * FROM person WHERE length(name) <= 8
UNION ALL
SELECT * FROM person WHERE name = "Tom"

子查询

SQLlin 还不支持子查询,我们将会尽快开发该功能。

Join

SQLlin 目前支持 join 表。

我们需要另外两个数据库实体:

@DBRow("transcript")
@Serializable
data class Transcript(
    val name: String?,
    val math: Int,
    val english: Int,
)

@Serializable
data class Student(
    val name: String?,
    val age: Int?,
    val math: Int,
    val english: Int,
)

@Serializable
data class CrossJoinStudent(
    val age: Int?,
    val math: Int,
    val english: Int,
)

Transcript 代表另一张表,Student 表示 join 的查询结果的类型(所以 Student 不需要被添加 @DBRow 注解),它拥有所有 PersonTranscript 所拥有的列名。

Cross Join

fun joinSample() {
    db {
        PersonTable { table ->
            table SELECT CROSS_JOIN<CrossJoinStudent>(TranscriptTable)
        }
    }
}

CROSS_JOIN 函数接收一个或多个 Table 作为参数。在普通的 SELECT 语句中,该语句的查询结果的类型由 sqllin-processor 生成的 Table 决定,但是 JOIN 操作符将会将其改变为指定的类型。在前面的示例中, CROSS_JOIN 将该类型改变为了 CrossJoinStudent

注意,由于 SQL 中 CROSS JOIN 自身的特性,如果附带 CROSS JOIN 子句的 SELECT 语句查询的列包含两个表中的同名列,这会导致查询失败。因为 class 中不能包含两个同名的属性。因此请确保 CROSS_JOIN 函数转换后的结果类型不包含两个表中的同名列。

Inner Join

fun joinSample() {
    db {
        PersonTable { table ->
            table SELECT INNER_JOIN<Student>(TranscriptTable) USING name
            table SELECT NATURAL_INNER_JOIN<Student>(TranscriptTable)
            table SELECT INNER_JOIN<CrossJoinStudent>(TranscriptTable) ON (name EQ TranscriptTable.name)
        }
    }
}

INNER_JOINCROSS_JOIN 非常相似,不同之处在于 INNER_JOIN 需要连接一个 USINGON 子句。如果一个 INNER JOIN 语句没有 USINGON 子句,那么它是不完整的,但是你的代码仍然可以编译,但它在运行时不会做任何事情。

NATURAL_INNER_JOIN 将会产生一个完整的 SELECT 语句(与 CROSS_JOIN 相似)。所以,你不能再它末尾连接 USINGON 子句,这将由 Kotlin 编译器来保证。

注意,带有 ON 子句的 INNER_JOIN 子句的行为与 CROSS_JOIN 相同,你不能 select 在两个表中拥有相同名字的列。

INNER_JOIN 拥有一个别名——JOINNATURAL_INNER_JOIN 也拥有一个别名——NATURAL_JOIN 。这就像你在 SQL 的 inner join 查询中可以省略 INNER 关键字一样。

Left Outer Join

fun joinSample() {
    db {
        PersonTable { table ->
            table SELECT LEFT_OUTER_JOIN<Student>(TranscriptTable) USING name
            table SELECT NATURAL_LEFT_OUTER_JOIN<Student>(TranscriptTable)
            table SELECT LEFT_OUTER_JOIN<CrossJoinStudent>(TranscriptTable) ON (name EQ TranscriptTable.name)
        }
    }
}

LEFT_OUTER_JOIN 的用法与 INNER_JOIN 非常相似,不同之处仅仅是它们的 API 名字。

最后

你已经学习了所有的 SQLlin 用法,享受你的 SQLlin 的编程旅程并对它的更新保持关注吧 :)